cartethyia 2.8.0 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -3
- package/dist/dashboard/assets/{BannedPage-Cc0vKpMG.js → BannedPage-Dq530awD.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-Bqj2aa1T.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-CXsaOAO2.js → CliToolsPage-DaZZ3cqP.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BeLpJ4ma.js → CombosPage-BlC_xdkX.js} +1 -1
- package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-DnV0uZ3m.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-CBxAjo63.js → CustomizationPage-7xT5yg2E.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-B5yEE0k6.js → HealthEventsModal-Clq8E9cW.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-BWFLECmz.js → InjectSystemPromptPanel-6Teoq9Se.js} +1 -1
- package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-CgoPc5B9.js → LoginPage-CZuI0MAR.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-D9bFQern.js → ModelPicker-C1hNTlkr.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
- package/dist/dashboard/assets/{OverviewPage-B_p4tL8k.js → OverviewPage-0yfhSI1C.js} +1 -1
- package/dist/dashboard/assets/OverviewPage-0yfhSI1C.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js.gz +0 -0
- package/dist/dashboard/assets/{ProviderIcon-D_0PiZtg.js → ProviderIcon-Ddhm1InL.js} +1 -1
- package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-CLI2bJTK.js → ProvidersPage-C3fYuu6i.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js +9 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-DA-a1iSN.js → QuotaPage-D1RV_2BX.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-CctTga2C.js → SettingsPage-BzZAvYSX.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-febLU4r1.js → SetupPage--s-EfEIL.js} +1 -1
- package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-M6fvR5Wz.js → StudioPage-BjXFGSK4.js} +1 -1
- package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-nyeuRqz9.js → UsagePage-CP8F8BN7.js} +1 -1
- package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BFOlvrJC.js → api-keys-2hc12Uz2.js} +1 -1
- package/dist/dashboard/assets/api-keys-2hc12Uz2.js.gz +0 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css +2 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css.gz +0 -0
- package/dist/dashboard/assets/{assert-CqTLw5e7.js → assert-BMcj1xZE.js} +1 -1
- package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
- package/dist/dashboard/assets/{badge-C83EqLB0.js → badge-DkRUuVfz.js} +1 -1
- package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
- package/dist/dashboard/assets/{banner-D1xn7pIe.js → banner-CDjW841Q.js} +1 -1
- package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
- package/dist/dashboard/assets/{card-DU3Lq5Lm.js → card-Bto5UlL9.js} +1 -1
- package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-Dw1U_drK.js → cli-tools-DaH6g9hL.js} +1 -1
- package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-CEL5Sg_J.js → clipboard-button-BYKBCzjk.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BjFR5UHy.js → dialog-DL0Zftwv.js} +1 -1
- package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-HvBW0AjI.js → drawer-Dpx_UXaS.js} +1 -1
- package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CECVELyy.js → entry-BV7-1NoM.js} +1 -1
- package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-DYYofQ7W.js → entry-CGvdNU6r.js} +1 -1
- package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BSu3gFlt.js → entry-DScKYvZj.js} +3 -3
- package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DLW4i8zK.js → formatters-BS3DZlZj.js} +1 -1
- package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
- package/dist/dashboard/assets/{index-1-fCqe64.js → index-SQnvvTWX.js} +2 -2
- package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DGvU2oLK.js → inline-Dl-cuLK0.js} +1 -1
- package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
- package/dist/dashboard/assets/{input-iNnO1X2K.js → input-Bfx0EzRc.js} +1 -1
- package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
- package/dist/dashboard/assets/{layout-0q8UGypb.js → layout-BxhbQ_JS.js} +1 -1
- package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-C0w2bQjJ.js → list-row-D49Y4ptn.js} +1 -1
- package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
- package/dist/dashboard/assets/{live-M5aJ_xCa.js → live-CbVTdX9V.js} +1 -1
- package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
- package/dist/dashboard/assets/{network-BKeQGT3K.js → network-JGvDveof.js} +1 -1
- package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-CivAk3da.js → numbers-Dw6kiew-.js} +1 -1
- package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-CpzvI5ep.js → pagination-iUoI8GeE.js} +1 -1
- package/dist/dashboard/assets/pagination-iUoI8GeE.js.gz +0 -0
- package/dist/dashboard/assets/providers/sensenova-intl.png +0 -0
- package/dist/dashboard/assets/providers/sensenova.png +0 -0
- package/dist/dashboard/assets/{routing-BFQX2VBc.js → routing-8jZr-3WE.js} +1 -1
- package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
- package/dist/dashboard/assets/{select-CXbck0jG.js → select-oO1FxMVb.js} +1 -1
- package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
- package/dist/dashboard/assets/{settings-C_XvMHh9.js → settings-CLr3kdIx.js} +1 -1
- package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DggRiCxE.js → stack-Y3pDteZr.js} +1 -1
- package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
- package/dist/dashboard/assets/{state-ClFzfbYp.js → state-CArX_Iak.js} +1 -1
- package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
- package/dist/dashboard/assets/{switch-CTIFxKqS.js → switch-DwlwmDO-.js} +1 -1
- package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BClG3yWj.js → useMutation-DJ9GYEgf.js} +1 -1
- package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -51
- package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
- package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
- package/package.json +1 -1
- package/src/boot/app.ts +1 -1
- package/src/boot/dependencies.ts +4 -6
- package/src/config/providers.ts +6 -5
- package/src/config/spec.ts +8 -8
- package/src/console/backup/nine-router.ts +71 -10
- package/src/console/backup/service.ts +8 -1
- package/src/console/console-router.ts +1 -1
- package/src/console/domain-registration.ts +7 -2
- package/src/console/inferhub/client.ts +1 -1
- package/src/console/observability/live.ts +2 -2
- package/src/console/providers/catalog/contracts.ts +9 -3
- package/src/console/providers/catalog/model-operations.ts +2 -3
- package/src/console/providers/catalog/provider-operations.ts +1 -1
- package/src/console/providers/catalog/routes.ts +2 -2
- package/src/console/providers/catalog/store.ts +1914 -1907
- package/src/console/providers/detail/routes.ts +1 -0
- package/src/console/providers/detail/store.ts +4 -0
- package/src/console/providers/oauth/callback-listener.ts +30 -11
- package/src/console/providers/oauth/device-egress.ts +123 -0
- package/src/console/providers/oauth/routes.ts +123 -16
- package/src/console/quota/account-quota-tenant.ts +5 -5
- package/src/console/quota/account-test-egress.ts +2 -2
- package/src/console/quota/daily-growth-job.ts +1 -1
- package/src/console/routing/pools/contracts.ts +1 -1
- package/src/console/routing/pools/operations.ts +1 -1
- package/src/console/routing/pools/probe-verdict.ts +2 -2
- package/src/console/routing/pools/relay-deploy.ts +1 -1
- package/src/console/routing/pools/store.ts +4 -4
- package/src/network/client.ts +67 -0
- package/src/network/egress/bidi-session.ts +322 -0
- package/src/network/egress/dial.ts +201 -0
- package/src/network/egress/factory.ts +191 -0
- package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
- package/src/network/egress/tls-profile.ts +123 -0
- package/src/network/egress/ws.ts +175 -0
- package/src/network/{types.ts → pool/agent-config.ts} +1 -1
- package/src/network/pool/agent.ts +2 -2
- package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +7 -7
- package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
- package/src/network/pool/resolver.ts +10 -849
- package/src/network/pool/selector.ts +2 -2
- package/src/network/ssrf.ts +1 -1
- package/src/network/{retry.ts → util/retry.ts} +1 -1
- package/src/observability/telemetry-buffer.ts +1 -1
- package/src/persistence/schema.ts +9 -0
- package/src/persistence/ttl-cache.ts +1 -1
- package/src/protocol/codecs.ts +58 -1
- package/src/protocol/request/messages.ts +31 -18
- package/src/protocol/response/messages.ts +34 -7
- package/src/protocol/translation/quirks.ts +15 -0
- package/src/protocol/upstream/send-claude-messages.ts +2 -2
- package/src/providers/adapters/compatible-adapter.ts +5 -0
- package/src/providers/authentication/oauth-client.ts +40 -6
- package/src/providers/authentication/oauth-device-flow.ts +7 -3
- package/src/providers/authentication/oauth-flow-store.ts +82 -1
- package/src/providers/authentication/oauth-refresh-service.ts +766 -739
- package/src/providers/default-registry.ts +76 -0
- package/src/providers/discovery/discovery-types.ts +7 -0
- package/src/providers/discovery/probe-phases.ts +6 -2
- package/src/providers/discovery/probing-service.ts +7 -0
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +11 -0
- package/src/providers/integrations/buddy/account.ts +969 -0
- package/src/providers/integrations/buddy/capabilities.ts +10 -10
- package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
- package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +5 -5
- package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
- package/src/providers/integrations/buddy/codebuddy/shared.ts +4 -4
- package/src/providers/integrations/buddy/{buddy-identity.ts → headers.ts} +138 -140
- package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
- package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
- package/src/providers/integrations/cline/cline-oauth.ts +10 -5
- package/src/providers/integrations/cline/cline.ts +3 -4
- package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
- package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
- package/src/providers/integrations/clixad/provider.ts +145 -0
- package/src/providers/integrations/codex/codex-oauth.ts +8 -5
- package/src/providers/integrations/cursor/cursor-auth.ts +1 -1
- package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
- package/src/providers/integrations/cursor/cursor-shared.ts +1 -1
- package/src/providers/integrations/cursor/cursor.ts +485 -70
- package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
- package/src/providers/integrations/deepai/deepai-common.ts +145 -0
- package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
- package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
- package/src/providers/integrations/deepai/provider.ts +42 -0
- package/src/providers/integrations/devin/devin-auth.ts +7 -0
- package/src/providers/integrations/factory/factory-oauth.ts +16 -7
- package/src/providers/integrations/freebuff/freebuff.ts +2 -2
- package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
- package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
- package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
- package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
- package/src/providers/integrations/freepi/freepi-session.ts +158 -0
- package/src/providers/integrations/freepi/freepi.ts +80 -54
- package/src/providers/integrations/github/github-discovery.ts +30 -13
- package/src/providers/integrations/github/github-oauth.ts +217 -44
- package/src/providers/integrations/github/github-quota.ts +22 -5
- package/src/providers/integrations/github/github.ts +40 -32
- package/src/providers/integrations/grok/grok-oauth.ts +4 -4
- package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
- package/src/providers/integrations/hermes/hermes.ts +34 -5
- package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
- package/src/providers/integrations/kilo/kilo.ts +1 -1
- package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
- package/src/providers/integrations/muse/muse-oauth.ts +5 -5
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/openrouter/provider.ts +1 -1
- package/src/providers/integrations/perch/perch-protocol.ts +0 -3
- package/src/providers/integrations/perch/perch.ts +3 -3
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
- package/src/providers/integrations/qoder/qoder-adapter.ts +19 -113
- package/src/providers/integrations/qoder/qoder-auth.ts +8 -1
- package/src/providers/integrations/qoder/qoder-catalog.ts +1 -1
- package/src/providers/integrations/qoder/qoder-chat.ts +25 -8
- package/src/providers/integrations/qoder/qoder-checkin.ts +1 -1
- package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
- package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
- package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
- package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
- package/src/providers/integrations/qoder/qoder-types.ts +9 -0
- package/src/providers/integrations/qoder/qoder.ts +3 -25
- package/src/providers/integrations/sensenova/provider.ts +144 -0
- package/src/providers/integrations/trae/trae-quota.ts +15 -1
- package/src/providers/integrations/vercel/provider.ts +117 -0
- package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
- package/src/providers/integrations/zro/zro.ts +2 -1
- package/src/providers/operations/account-refresh-status.ts +14 -0
- package/src/providers/operations/client-versions.ts +95 -30
- package/src/providers/operations/oauth-refresh-lead.ts +46 -22
- package/src/providers/operations/provider-caches.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +22 -25
- package/src/providers/operations/session-resolution.ts +17 -1
- package/src/providers/provider-contracts.ts +2 -2
- package/src/providers/provider-metadata.ts +30 -0
- package/src/providers/quota/quota-contracts.ts +2 -0
- package/src/providers/quota/refresh.ts +25 -3
- package/src/security/ip-boundary.ts +1 -1
- package/src/transport/dispatch/attempt-finalize.ts +1 -1
- package/src/transport/dispatch/attempt-loop.ts +3 -3
- package/src/transport/dispatch/handler-deps.ts +1 -1
- package/src/transport/dispatch/leases.ts +1 -1
- package/src/transport/dispatch/retry-policy.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +1 -1
- package/src/transport/dispatch/upstream.ts +1 -1
- package/src/transport/shutdown-notice.ts +1 -1
- package/src/workers/buddy-growth-tasks.ts +8 -8
- package/src/workers/daily-checkin.ts +4 -4
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/zcode-gift-claim.ts +1 -1
- package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +0 -8
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js +0 -9
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css +0 -2
- package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
- package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
- package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
- package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
- package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
- package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
- package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
- package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
- package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
- package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
- package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
- package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
- package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
- package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
- package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
- package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
- package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
- package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
- package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
- package/src/console/providers/catalog/discover-constants.ts +0 -6
- package/src/network/pool-health.ts +0 -198
- package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
- package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
- package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
- package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -188
- package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
- package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -179
- package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
- package/src/providers/integrations/qoder/chat-proto.ts +0 -328
- package/src/providers/integrations/qoder/qoder-grpc.ts +0 -870
- package/src/providers/integrations/qoder/qoder-ide.ts +0 -481
- /package/dist/dashboard/assets/{app-DIXLjojg.js → app-C0gTXEoN.js} +0 -0
- /package/dist/dashboard/assets/{app-DIXLjojg.js.gz → app-C0gTXEoN.js.gz} +0 -0
- /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
- /package/src/network/pool/{byte-accounting.ts → accounting.ts} +0 -0
- /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
- /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
- /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
- /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
- /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
- /package/src/network/{rate-limit-verdict.ts → verdict.ts} +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// DeepAI model catalog + discovery.
|
|
2
|
+
//
|
|
3
|
+
// All 22 rows below were fetched live from `GET /deepai_code/models` on
|
|
4
|
+
// 2026-11-11 (see `documentation/research/deepai/2026-11-11-deepai-code-live-wire-verification.md`).
|
|
5
|
+
// The 11 zero-Theos rows carry the daily free-tier allowance (`freeTier: true`
|
|
6
|
+
// + `free: true` — they bill zero and do not draw from the balance). The
|
|
7
|
+
// metered rows keep their `theosPerRequest` price as a per-request cost
|
|
8
|
+
// estimate for the operator.
|
|
9
|
+
//
|
|
10
|
+
// Wire routing: every row is a chat-wire model except `gpt-6-astra`, which the
|
|
11
|
+
// upstream serves only on `/responses` (verified live: it answers 400
|
|
12
|
+
// `model_not_found` on `/chat/completions`, and the rest answer 400 on
|
|
13
|
+
// `/responses`).
|
|
14
|
+
import type { ModelDefinition } from "../../provider-model-definition";
|
|
15
|
+
import { defineModel } from "../../provider-model-definition";
|
|
16
|
+
import {
|
|
17
|
+
DEEPAI_CHAT_PATH,
|
|
18
|
+
DEEPAI_HEADER_INSTANCE,
|
|
19
|
+
DEEPAI_HEADER_THEOS,
|
|
20
|
+
DEEPAI_LANE_VERSION,
|
|
21
|
+
DEEPAI_MODELS_PATH,
|
|
22
|
+
DEEPAI_RESPONSES_PATH,
|
|
23
|
+
deepAIInstanceId,
|
|
24
|
+
} from "./deepai-common";
|
|
25
|
+
|
|
26
|
+
interface DeepAIRosterRow {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly reasoning: boolean;
|
|
30
|
+
readonly vision: boolean;
|
|
31
|
+
readonly ctx: number;
|
|
32
|
+
readonly out: number;
|
|
33
|
+
readonly theosPerRequest: number;
|
|
34
|
+
readonly responsesWire?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const DEEPAI_ROSTER: readonly DeepAIRosterRow[] = [
|
|
38
|
+
{ id: "glm-5.3-flash", name: "GLM 5.3 Flash", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
|
|
39
|
+
{ id: "deepseek-v4.1-flash", name: "DeepSeek V4.1 Flash 1017", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
|
|
40
|
+
{ id: "mimo-2.6-flash", name: "MiMo 2.6 Flash", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
|
|
41
|
+
{ id: "qwen-3.8-flash", name: "Qwen 3.8 Flash", reasoning: true, vision: true, ctx: 991_000, out: 128_000, theosPerRequest: 0 },
|
|
42
|
+
{ id: "minimax-m3.1-flash", name: "MiniMax M3.1 Flash", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
|
|
43
|
+
{ id: "solar-mini-4", name: "Solar Mini 4", reasoning: true, vision: false, ctx: 524_288, out: 131_072, theosPerRequest: 0 },
|
|
44
|
+
{ id: "gpt-6-luna", name: "GPT-6 Luna", reasoning: true, vision: true, ctx: 1_048_576, out: 128_000, theosPerRequest: 0 },
|
|
45
|
+
{ id: "mimo-2.6-pro", name: "MiMo 2.6 Pro", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
|
|
46
|
+
{ id: "crouton-alpha", name: "Crouton Alpha", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
|
|
47
|
+
{ id: "glyph-cluster", name: "Glyph Cluster", reasoning: true, vision: false, ctx: 256_000, out: 131_072, theosPerRequest: 0 },
|
|
48
|
+
{ id: "deepai-cyber", name: "DeepAI Cyber", reasoning: true, vision: false, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
|
|
49
|
+
{ id: "minimax-m3", name: "MiniMax M3", reasoning: true, vision: false, ctx: 512_000, out: 131_000, theosPerRequest: 2 },
|
|
50
|
+
{ id: "gpt-6.1-sol", name: "GPT-6.1 Sol", reasoning: true, vision: true, ctx: 1_048_576, out: 128_000, theosPerRequest: 10 },
|
|
51
|
+
{ id: "gemini-4-argon", name: "Gemini 4 Argon", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 10 },
|
|
52
|
+
{ id: "glm-5.3", name: "GLM 5.3", reasoning: true, vision: false, ctx: 1_048_576, out: 131_072, theosPerRequest: 3 },
|
|
53
|
+
{ id: "kimi-k3", name: "Kimi K3", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 3 },
|
|
54
|
+
{ id: "muse-spark-1.3", name: "Muse Spark 1.3", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 6 },
|
|
55
|
+
{ id: "claude-opus-5-5", name: "Claude Opus 5.5", reasoning: true, vision: true, ctx: 1_000_000, out: 64_000, theosPerRequest: 15 },
|
|
56
|
+
{ id: "muse-wonder", name: "Muse Wonder", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 15 },
|
|
57
|
+
{ id: "qwen-3.8-max-prime", name: "Qwen 3.8 Max Prime", reasoning: true, vision: true, ctx: 1_000_000, out: 131_072, theosPerRequest: 15 },
|
|
58
|
+
{ id: "claude-fable-5-2", name: "Claude Fable 5.2", reasoning: true, vision: true, ctx: 1_000_000, out: 64_000, theosPerRequest: 20 },
|
|
59
|
+
{ id: "gpt-6-astra", name: "GPT-6 Astra", reasoning: true, vision: true, ctx: 1_048_576, out: 128_000, theosPerRequest: 50, responsesWire: true },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
function rowToModel(row: DeepAIRosterRow): ModelDefinition {
|
|
63
|
+
const free = row.theosPerRequest === 0;
|
|
64
|
+
return defineModel({
|
|
65
|
+
id: row.id,
|
|
66
|
+
providerId: "deepai",
|
|
67
|
+
wireFamily: row.responsesWire === true ? "responses" : "chat",
|
|
68
|
+
endpoint: row.responsesWire === true ? DEEPAI_RESPONSES_PATH : DEEPAI_CHAT_PATH,
|
|
69
|
+
ctx: row.ctx,
|
|
70
|
+
out: row.out,
|
|
71
|
+
vision: row.vision,
|
|
72
|
+
reasoning: row.reasoning,
|
|
73
|
+
toolCall: true,
|
|
74
|
+
...(free ? { free: true, freeTier: true } : {}),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Static roster — the live-verified free tier plus metered rows. */
|
|
79
|
+
export const DEEPAI_DEFAULT_MODELS: readonly ModelDefinition[] =
|
|
80
|
+
DEEPAI_ROSTER.map(rowToModel);
|
|
81
|
+
|
|
82
|
+
/** Live `/deepai_code/models` listing. */
|
|
83
|
+
export async function discoverDeepAIModels(options: {
|
|
84
|
+
readonly baseUrl: string;
|
|
85
|
+
readonly fetcher?: typeof fetch;
|
|
86
|
+
}): Promise<readonly ModelDefinition[] | null> {
|
|
87
|
+
const fetcher = options.fetcher ?? fetch;
|
|
88
|
+
const url = `${options.baseUrl.replace(/\/+$/, "")}${DEEPAI_MODELS_PATH}`;
|
|
89
|
+
try {
|
|
90
|
+
const response = await fetcher(url, {
|
|
91
|
+
headers: {
|
|
92
|
+
accept: "application/json",
|
|
93
|
+
// The catalog is lane-aware: the 22-row roster only comes back with the
|
|
94
|
+
// lane headers (verified live 2026-11-11 — bare GET returns a smaller
|
|
95
|
+
// 9-row default roster).
|
|
96
|
+
[DEEPAI_HEADER_THEOS]: DEEPAI_LANE_VERSION,
|
|
97
|
+
[DEEPAI_HEADER_INSTANCE]: deepAIInstanceId(),
|
|
98
|
+
},
|
|
99
|
+
signal: AbortSignal.timeout(10_000),
|
|
100
|
+
});
|
|
101
|
+
if (!response.ok) return null;
|
|
102
|
+
const payload = (await response.json()) as unknown;
|
|
103
|
+
const list = payload !== null && typeof payload === "object" && Array.isArray((payload as { models?: unknown }).models)
|
|
104
|
+
? ((payload as { models: unknown }).models as readonly Record<string, unknown>[])
|
|
105
|
+
: null;
|
|
106
|
+
if (list === null || list.length === 0) return null;
|
|
107
|
+
const rows: DeepAIRosterRow[] = [];
|
|
108
|
+
for (const entry of list) {
|
|
109
|
+
const id = typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : null;
|
|
110
|
+
if (id === null) continue;
|
|
111
|
+
// `theosPerRequest` arrives as a string ("0", "2", "50") or a number.
|
|
112
|
+
const theosNumber = parseTheosAmount(entry.theosPerRequest);
|
|
113
|
+
const input = entry.input;
|
|
114
|
+
const hasImage = Array.isArray(input) && input.some((m) => m === "image");
|
|
115
|
+
rows.push({
|
|
116
|
+
id,
|
|
117
|
+
name: typeof entry.name === "string" ? entry.name : id,
|
|
118
|
+
reasoning: entry.reasoning === true,
|
|
119
|
+
vision: hasImage,
|
|
120
|
+
ctx: positiveInt(entry.contextWindow) ?? positiveInt(entry.context_window) ?? 1_048_576,
|
|
121
|
+
out: positiveInt(entry.maxTokens) ?? positiveInt(entry.max_completion_tokens) ?? 131_072,
|
|
122
|
+
theosPerRequest: theosNumber,
|
|
123
|
+
responsesWire: entry.api === "openai-responses",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (rows.length === 0) return null;
|
|
127
|
+
return rows.map(rowToModel);
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function positiveInt(value: unknown): number | null {
|
|
134
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Parses `theosPerRequest` — a numeric string ("0"/"50") or a number. */
|
|
138
|
+
function parseTheosAmount(value: unknown): number {
|
|
139
|
+
if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, value);
|
|
140
|
+
if (typeof value === "string") {
|
|
141
|
+
const trimmed = value.trim();
|
|
142
|
+
if (/^\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
143
|
+
const n = Number(trimmed);
|
|
144
|
+
if (Number.isFinite(n)) return Math.max(0, n);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Absent / unparseable means the server did not meter it → free.
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// DeepAI shared wire helpers.
|
|
2
|
+
//
|
|
3
|
+
// Wire facts reconstructed from the DeepAI Code 0.3.35 bundle and verified
|
|
4
|
+
// live 2026-11-11 (see `documentation/research/deepai/`):
|
|
5
|
+
//
|
|
6
|
+
// - Model base is `https://api.deepai.org/deepai_code`; the sign-in flow
|
|
7
|
+
// (`/deepai_code/desktop_auth/`) returns an opaque `api_key`.
|
|
8
|
+
// - Every model call carries:
|
|
9
|
+
// `X-DeepAI-Code-Theos: 4` (lane version)
|
|
10
|
+
// `X-DeepAI-Code-Instance` (16 random bytes hex, per process)
|
|
11
|
+
// `X-DeepAI-Code-Machine` (sha256hex of the machine id)
|
|
12
|
+
// `Api-Key: <api_key>` (the credential; NOT a Bearer)
|
|
13
|
+
// - The machine header is a digest of a platform identifier (SMBIOS UUID /
|
|
14
|
+
// MachineGuid / `/etc/machine-id`); the raw identifier never leaves the
|
|
15
|
+
// machine. The grant is bound to the account, not the machine, so a
|
|
16
|
+
// missing/mangled machine header is tolerated (verified live).
|
|
17
|
+
import { randomBytes } from "node:crypto";
|
|
18
|
+
import { createHash } from "node:crypto";
|
|
19
|
+
import { execFile } from "node:child_process";
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
|
|
22
|
+
export const DEEPAI_PROVIDER_ID = "deepai" as const;
|
|
23
|
+
export const DEEPAI_API_ORIGIN = "https://api.deepai.org";
|
|
24
|
+
export const DEEPAI_MODEL_BASE = `${DEEPAI_API_ORIGIN}/deepai_code`;
|
|
25
|
+
|
|
26
|
+
export const DEEPAI_LANE_VERSION = "4";
|
|
27
|
+
export const DEEPAI_HEADER_THEOS = "X-DeepAI-Code-Theos";
|
|
28
|
+
export const DEEPAI_HEADER_INSTANCE = "X-DeepAI-Code-Instance";
|
|
29
|
+
export const DEEPAI_HEADER_MACHINE = "X-DeepAI-Code-Machine";
|
|
30
|
+
export const DEEPAI_HEADER_API_KEY = "Api-Key";
|
|
31
|
+
|
|
32
|
+
export const DEEPAI_CHAT_PATH = "/deepai_code/chat/completions";
|
|
33
|
+
export const DEEPAI_RESPONSES_PATH = "/deepai_code/responses";
|
|
34
|
+
export const DEEPAI_MODELS_PATH = "/deepai_code/models";
|
|
35
|
+
export const DEEPAI_THEOS_PATH = "/deepai_code/theos";
|
|
36
|
+
export const DEEPAI_USAGE_SUMMARY_PATH = "/get_usage_summary";
|
|
37
|
+
export const DEEPAI_SIGNIN_PATH = "/deepai_code/desktop_auth/";
|
|
38
|
+
export const DEEPAI_EXCHANGE_PATH = "/deepai_code/desktop_auth/exchange/";
|
|
39
|
+
|
|
40
|
+
const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
41
|
+
const SMBIOS_PLACEHOLDERS = new Set([
|
|
42
|
+
"00000000-0000-0000-0000-000000000000",
|
|
43
|
+
"ffffffff-ffff-ffff-ffff-ffffffffffff",
|
|
44
|
+
"03000200-0400-0500-0006-000700080009",
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
function run(file: string, args: readonly string[]): Promise<string> {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
execFile(
|
|
50
|
+
file,
|
|
51
|
+
[...args],
|
|
52
|
+
{ encoding: "utf8", timeout: 10_000, windowsHide: true },
|
|
53
|
+
(error, stdout) => resolve(error ? "" : stdout),
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function parseSmbiosUUID(output: string): string | null {
|
|
59
|
+
const uuid = UUID_RE.exec(output)?.[0]?.toLowerCase() ?? null;
|
|
60
|
+
return uuid && !SMBIOS_PLACEHOLDERS.has(uuid) ? uuid : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function parseMachineGuid(regQuery: string): string | null {
|
|
64
|
+
const match = /MachineGuid\s+REG_SZ\s+(\S+)/.exec(regQuery);
|
|
65
|
+
return match?.[1] && UUID_RE.test(match[1]) ? match[1].toLowerCase() : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function parseLinuxMachineId(file: string): string | null {
|
|
69
|
+
const id = file.trim().toLowerCase();
|
|
70
|
+
return /^[0-9a-f]{32}$/.test(id) && !/^0+$/.test(id) ? id : null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function detectMachineId(): Promise<string | null> {
|
|
74
|
+
try {
|
|
75
|
+
if (process.platform === "win32") {
|
|
76
|
+
return (async () => {
|
|
77
|
+
const smbios = await run("powershell", [
|
|
78
|
+
"-NoProfile",
|
|
79
|
+
"-NonInteractive",
|
|
80
|
+
"-Command",
|
|
81
|
+
"(Get-CimInstance Win32_ComputerSystemProduct).UUID",
|
|
82
|
+
]);
|
|
83
|
+
const uuid = parseSmbiosUUID(smbios);
|
|
84
|
+
if (uuid) return uuid;
|
|
85
|
+
const guid = await run("reg", [
|
|
86
|
+
"query",
|
|
87
|
+
"HKLM\\SOFTWARE\\Microsoft\\Cryptography",
|
|
88
|
+
"/v",
|
|
89
|
+
"MachineGuid",
|
|
90
|
+
]);
|
|
91
|
+
return parseMachineGuid(guid);
|
|
92
|
+
})();
|
|
93
|
+
}
|
|
94
|
+
if (process.platform === "darwin") {
|
|
95
|
+
return (async () => {
|
|
96
|
+
const ioreg = await run("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"]);
|
|
97
|
+
const match = /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(ioreg);
|
|
98
|
+
return match?.[1] && UUID_RE.test(match[1]) ? match[1].toLowerCase() : null;
|
|
99
|
+
})();
|
|
100
|
+
}
|
|
101
|
+
for (const path of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
102
|
+
try {
|
|
103
|
+
const id = parseLinuxMachineId(readFileSync(path, "utf8"));
|
|
104
|
+
if (id) return Promise.resolve(id);
|
|
105
|
+
} catch {
|
|
106
|
+
// try the next path
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return Promise.resolve(null);
|
|
110
|
+
} catch {
|
|
111
|
+
return Promise.resolve(null);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let machineDigestPromise: Promise<string | null> | undefined;
|
|
116
|
+
|
|
117
|
+
/** sha256hex(machineId), computed once per process; null when unreadable. */
|
|
118
|
+
export function deepAIMachineDigest(): Promise<string | null> {
|
|
119
|
+
machineDigestPromise ??= detectMachineId().then(
|
|
120
|
+
(raw) => (raw === null ? null : createHash("sha256").update(raw).digest("hex")),
|
|
121
|
+
);
|
|
122
|
+
return machineDigestPromise;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Fresh per-process instance id (16 random bytes hex). */
|
|
126
|
+
export function deepAIInstanceId(): string {
|
|
127
|
+
return randomBytes(16).toString("hex");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Baseline DeepAI headers shared by every model API call. `apiKey` is the
|
|
132
|
+
* account credential (the sign-in `api_key`); omit it for unauthenticated
|
|
133
|
+
* reads (model catalog).
|
|
134
|
+
*/
|
|
135
|
+
export async function deepAIBaseHeaders(apiKey?: string): Promise<Record<string, string>> {
|
|
136
|
+
const headers: Record<string, string> = {
|
|
137
|
+
"content-type": "application/json",
|
|
138
|
+
[DEEPAI_HEADER_THEOS]: DEEPAI_LANE_VERSION,
|
|
139
|
+
[DEEPAI_HEADER_INSTANCE]: deepAIInstanceId(),
|
|
140
|
+
};
|
|
141
|
+
const machine = await deepAIMachineDigest();
|
|
142
|
+
if (machine !== null) headers[DEEPAI_HEADER_MACHINE] = machine;
|
|
143
|
+
if (apiKey !== undefined && apiKey.length > 0) headers[DEEPAI_HEADER_API_KEY] = apiKey;
|
|
144
|
+
return headers;
|
|
145
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// DeepAI browser login client.
|
|
2
|
+
//
|
|
3
|
+
// DeepAI's desktop sign-in is a loopback flow: the app builds
|
|
4
|
+
// `https://api.deepai.org/deepai_code/desktop_auth/?state=<state>&port=<port>`,
|
|
5
|
+
// the operator signs in in the browser, and the server redirects to
|
|
6
|
+
// `http://127.0.0.1:<port>/callback?code=<code>&state=<state>`. The code is
|
|
7
|
+
// then exchanged at `/deepai_code/desktop_auth/exchange/` for an opaque
|
|
8
|
+
// `api_key`. There is no PKCE verifier and no refresh token — the key is
|
|
9
|
+
// long-lived and the flow is re-run when needed.
|
|
10
|
+
//
|
|
11
|
+
// Verified live 2026-11-11: the exchange answers 200 with
|
|
12
|
+
// `{ api_key, username, is_pro }`, and the api_key authenticates the model
|
|
13
|
+
// API via the `Api-Key` header (a Bearer or `x-api-key` spelling is rejected
|
|
14
|
+
// with 401).
|
|
15
|
+
import { randomBytes } from "node:crypto";
|
|
16
|
+
import { OAuthClient } from "../../authentication/oauth-client";
|
|
17
|
+
import type {
|
|
18
|
+
OAuthAuthorizeRequest,
|
|
19
|
+
OAuthCodeExchangeContext,
|
|
20
|
+
OAuthExchangeResult,
|
|
21
|
+
OAuthImportInput,
|
|
22
|
+
} from "../../authentication/oauth-flow-store";
|
|
23
|
+
import {
|
|
24
|
+
DEEPAI_API_ORIGIN,
|
|
25
|
+
DEEPAI_EXCHANGE_PATH,
|
|
26
|
+
DEEPAI_THEOS_PATH,
|
|
27
|
+
deepAIBaseHeaders,
|
|
28
|
+
} from "./deepai-common";
|
|
29
|
+
|
|
30
|
+
const DEEPAI_LOGIN_TTL_MS = 365 * 24 * 60 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
function loopbackPortFromRedirectUri(redirectUri: string): number {
|
|
33
|
+
const url = new URL(redirectUri);
|
|
34
|
+
const port = Number(url.port);
|
|
35
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65_535) {
|
|
36
|
+
throw new Error("deepai: redirect URI names an invalid loopback port");
|
|
37
|
+
}
|
|
38
|
+
return port;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class DeepAIOAuthClient extends OAuthClient {
|
|
42
|
+
override readonly supportsDeviceCode = false;
|
|
43
|
+
override readonly supportsBrowserCode = true;
|
|
44
|
+
override readonly providerLabel = "deepai";
|
|
45
|
+
override readonly clientId = "";
|
|
46
|
+
override readonly tokenUrl = "";
|
|
47
|
+
override readonly scopes = "";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* DeepAI's hosted sign-in validates the state as 32 hex characters; the
|
|
51
|
+
* shared base64url state (which can contain `-`/`_`) is rejected with
|
|
52
|
+
* "Invalid desktop sign-in request".
|
|
53
|
+
*/
|
|
54
|
+
override generateState(): string {
|
|
55
|
+
return randomBytes(16).toString("hex");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** DeepAI's hosted sign-in URL, carrying the loopback port + state. */
|
|
59
|
+
override buildAuthorizeUrl(request: OAuthAuthorizeRequest): string {
|
|
60
|
+
const port = loopbackPortFromRedirectUri(request.redirectUri);
|
|
61
|
+
const signIn = new URL(DEEPAI_EXCHANGE_PATH.replace("/desktop_auth/exchange/", "/desktop_auth/"), DEEPAI_API_ORIGIN);
|
|
62
|
+
signIn.search = "";
|
|
63
|
+
signIn.searchParams.set("state", request.state);
|
|
64
|
+
signIn.searchParams.set("port", String(port));
|
|
65
|
+
return signIn.toString();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Exchanges the callback code for the DeepAI api_key. */
|
|
69
|
+
override async exchangeCode(
|
|
70
|
+
code: string,
|
|
71
|
+
_codeVerifier: string,
|
|
72
|
+
_redirectUri: string,
|
|
73
|
+
_state?: string,
|
|
74
|
+
_context?: OAuthCodeExchangeContext,
|
|
75
|
+
): Promise<OAuthExchangeResult> {
|
|
76
|
+
const response = await this.fetchFn(new URL(DEEPAI_EXCHANGE_PATH, DEEPAI_API_ORIGIN), {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
79
|
+
body: JSON.stringify({ code }),
|
|
80
|
+
});
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
const text = await response.text().catch(() => "");
|
|
83
|
+
throw new Error(`deepai sign-in exchange failed: HTTP ${response.status} ${text.slice(0, 300)}`);
|
|
84
|
+
}
|
|
85
|
+
return this.parseExchangeBody(await response.json());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Accepts a pasted DeepAI api_key, validated against `/theos`. */
|
|
89
|
+
async importCredential(input: OAuthImportInput): Promise<OAuthExchangeResult> {
|
|
90
|
+
const apiKey = input.credential.trim();
|
|
91
|
+
if (!apiKey) throw new Error("deepai: pasted API key is empty");
|
|
92
|
+
const headers = await deepAIBaseHeaders(apiKey);
|
|
93
|
+
const response = await this.fetchFn(new URL(DEEPAI_THEOS_PATH, DEEPAI_API_ORIGIN), {
|
|
94
|
+
method: "GET",
|
|
95
|
+
headers,
|
|
96
|
+
});
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`deepai: API key validation failed (HTTP ${response.status}) — paste a key minted by the DeepAI sign-in flow`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
access: apiKey,
|
|
104
|
+
refresh: apiKey,
|
|
105
|
+
expiresAt: new Date(Date.now() + DEEPAI_LOGIN_TTL_MS),
|
|
106
|
+
accountLabel: input.accountLabel || "deepai",
|
|
107
|
+
auth_state: { via: "import" },
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private parseExchangeBody(body: unknown): OAuthExchangeResult {
|
|
112
|
+
const record = body !== null && typeof body === "object" ? (body as Record<string, unknown>) : null;
|
|
113
|
+
const apiKey = typeof record?.api_key === "string" ? record.api_key.trim() : "";
|
|
114
|
+
if (!apiKey || /[\r\n]/.test(apiKey)) {
|
|
115
|
+
throw new Error("deepai: sign-in response did not carry a valid api_key");
|
|
116
|
+
}
|
|
117
|
+
const username = typeof record?.username === "string" ? record.username : "";
|
|
118
|
+
return {
|
|
119
|
+
access: apiKey,
|
|
120
|
+
refresh: apiKey,
|
|
121
|
+
expiresAt: new Date(Date.now() + DEEPAI_LOGIN_TTL_MS),
|
|
122
|
+
...(username ? { accountLabel: username, accountName: username } : {}),
|
|
123
|
+
auth_state: { is_pro: record?.is_pro === true },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Shared DeepAI browser-login client instance (registry-facing). */
|
|
129
|
+
export const deepAIOAuthClient = new DeepAIOAuthClient();
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// DeepAI quota collector.
|
|
2
|
+
//
|
|
3
|
+
// Reads the account's DeepAI allowance from `GET /deepai_code/theos`
|
|
4
|
+
// (`balance`/`granted`/`refresh_at`), which is the daily free-tier grant. The
|
|
5
|
+
// entitlement surface (`/get_usage_summary`, `is_pro`) is probed second to
|
|
6
|
+
// distinguish a free vs pro account. Both calls require the `Api-Key` header
|
|
7
|
+
// (not Bearer).
|
|
8
|
+
import type {
|
|
9
|
+
FetchLike,
|
|
10
|
+
ProviderQuotaResult,
|
|
11
|
+
ProviderQuotaWindow,
|
|
12
|
+
} from "../../quota/quota-contracts";
|
|
13
|
+
import { cleanError, number, percentWindow, text } from "../../quota/quota-contracts";
|
|
14
|
+
import type { QuotaCollectionContext } from "../../quota/quota-support";
|
|
15
|
+
import {
|
|
16
|
+
DEEPAI_API_ORIGIN,
|
|
17
|
+
DEEPAI_THEOS_PATH,
|
|
18
|
+
DEEPAI_USAGE_SUMMARY_PATH,
|
|
19
|
+
deepAIBaseHeaders,
|
|
20
|
+
} from "./deepai-common";
|
|
21
|
+
|
|
22
|
+
const LABEL = "DeepAI";
|
|
23
|
+
|
|
24
|
+
async function readJson(
|
|
25
|
+
url: string,
|
|
26
|
+
headers: Record<string, string>,
|
|
27
|
+
fetcher: FetchLike,
|
|
28
|
+
): Promise<Record<string, unknown> | null> {
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetcher(url, {
|
|
31
|
+
method: "GET",
|
|
32
|
+
headers,
|
|
33
|
+
signal: AbortSignal.timeout(15_000),
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) return null;
|
|
36
|
+
const parsed = (await res.json()) as unknown;
|
|
37
|
+
return parsed !== null && typeof parsed === "object" ? (parsed as Record<string, unknown>) : null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Reads the DeepAI allowance and returns a normalized result.
|
|
45
|
+
*
|
|
46
|
+
* The Daily Theos window is pooled (the balance funds model calls) with the
|
|
47
|
+
* daily grant as the ceiling and the account's `refresh_at` as the reset. The
|
|
48
|
+
* plan is `free` when the allowance is enabled, `pro` when `is_pro` reads
|
|
49
|
+
* true from the usage summary.
|
|
50
|
+
*/
|
|
51
|
+
export async function fetchDeepAIQuota(
|
|
52
|
+
credential: string,
|
|
53
|
+
fetcher: FetchLike,
|
|
54
|
+
_context?: QuotaCollectionContext,
|
|
55
|
+
): Promise<ProviderQuotaResult> {
|
|
56
|
+
try {
|
|
57
|
+
const apiKey = text(credential);
|
|
58
|
+
if (!apiKey) {
|
|
59
|
+
return { source: LABEL, plan: null, windows: [], error: "DeepAI credential missing" };
|
|
60
|
+
}
|
|
61
|
+
const headers = await deepAIBaseHeaders(apiKey);
|
|
62
|
+
const theos = await readJson(`${DEEPAI_API_ORIGIN}${DEEPAI_THEOS_PATH}`, headers, fetcher);
|
|
63
|
+
if (!theos) {
|
|
64
|
+
return { source: LABEL, plan: null, windows: [], error: "DeepAI /theos endpoint unavailable" };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const windows: ProviderQuotaWindow[] = [];
|
|
68
|
+
const enabled = theos.enabled === true;
|
|
69
|
+
const granted = number(theos.granted);
|
|
70
|
+
const balance = number(theos.balance);
|
|
71
|
+
if (enabled && granted !== null && granted > 0) {
|
|
72
|
+
const used = balance === null ? null : Math.max(0, granted - balance);
|
|
73
|
+
const resetsAt =
|
|
74
|
+
typeof theos.refresh_at === "string" && Number.isFinite(Date.parse(theos.refresh_at))
|
|
75
|
+
? new Date(theos.refresh_at).toISOString()
|
|
76
|
+
: null;
|
|
77
|
+
windows.push(
|
|
78
|
+
percentWindow(
|
|
79
|
+
"theos_daily",
|
|
80
|
+
"Daily Theos",
|
|
81
|
+
used === null ? null : (used / granted) * 100,
|
|
82
|
+
resetsAt,
|
|
83
|
+
used,
|
|
84
|
+
granted,
|
|
85
|
+
"pooled",
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const summary = await readJson(`${DEEPAI_API_ORIGIN}${DEEPAI_USAGE_SUMMARY_PATH}`, headers, fetcher);
|
|
91
|
+
const isPro = summary?.is_pro === true;
|
|
92
|
+
const plan = enabled ? (isPro ? "pro" : "free") : null;
|
|
93
|
+
return { source: LABEL, plan, windows, error: null };
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return { source: LABEL, plan: null, windows: [], error: cleanError(error) };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// DeepAI provider — TypeScript, DeepAI Code model API, Free Limited.
|
|
2
|
+
//
|
|
3
|
+
// DeepAI Code's model API (`https://api.deepai.org/deepai_code`) is served
|
|
4
|
+
// with a non-Bearer credential: the sign-in flow returns an opaque `api_key`
|
|
5
|
+
// that travels in the `Api-Key` header (a Bearer or `x-api-key` spelling is
|
|
6
|
+
// rejected with 401 — verified live 2026-11-11). Every model call also needs
|
|
7
|
+
// the lane headers (`X-DeepAI-Code-Theos` / `-Instance` / `-Machine`), which
|
|
8
|
+
// the adapter stamps per dispatch. The daily free allowance is 50 Theos and
|
|
9
|
+
// the 11 zero-Theos models draw nothing from it.
|
|
10
|
+
//
|
|
11
|
+
// Verified live 2026-11-11 (evidence: `documentation/research/deepai/`):
|
|
12
|
+
// - `GET /deepai_code/models` → 200, 22 rows (unauthenticated).
|
|
13
|
+
// - `POST /deepai_code/chat/completions` → 200 stream + non-stream with
|
|
14
|
+
// lane headers + `Api-Key`.
|
|
15
|
+
// - `POST /deepai_code/responses` → 200 SSE for `gpt-6-astra` only.
|
|
16
|
+
// - `GET /deepai_code/theos` → 200 allowance (balance/granted/refresh).
|
|
17
|
+
import type { ApiKeyProviderSpec } from "../../configured-provider";
|
|
18
|
+
import {
|
|
19
|
+
DEEPAI_API_ORIGIN,
|
|
20
|
+
DEEPAI_CHAT_PATH,
|
|
21
|
+
DEEPAI_RESPONSES_PATH,
|
|
22
|
+
deepAIBaseHeaders,
|
|
23
|
+
} from "./deepai-common";
|
|
24
|
+
import type { ProviderDispatchContext } from "../../provider-contracts";
|
|
25
|
+
|
|
26
|
+
export const DEEPAI_SPEC: ApiKeyProviderSpec = {
|
|
27
|
+
provider_id: "deepai",
|
|
28
|
+
base_url: DEEPAI_API_ORIGIN,
|
|
29
|
+
endpoint_paths_by_wire_family: {
|
|
30
|
+
chat: DEEPAI_CHAT_PATH,
|
|
31
|
+
responses: DEEPAI_RESPONSES_PATH,
|
|
32
|
+
},
|
|
33
|
+
// The credential is not a bearer: sending `Authorization` would be wrong
|
|
34
|
+
// (and the upstream rejects a stray bearer). `buildExtraHeaders` stamps the
|
|
35
|
+
// `Api-Key` + lane headers instead.
|
|
36
|
+
credential_forwarding: "never",
|
|
37
|
+
buildExtraHeaders: async (context: ProviderDispatchContext) => {
|
|
38
|
+
const secret = context.credential.secret;
|
|
39
|
+
const apiKey = secret === undefined || secret.length === 0 ? "" : new TextDecoder().decode(secret);
|
|
40
|
+
return deepAIBaseHeaders(apiKey);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -137,6 +137,13 @@ export async function resolveDevinAccountLabel(
|
|
|
137
137
|
|
|
138
138
|
const DEVIN_AUTHORIZE_URL = "https://app.devin.ai/auth/cli/continue";
|
|
139
139
|
const DEVIN_TOKEN_URL = "https://api.devin.ai/auth/cli/token";
|
|
140
|
+
// Devin's authorization server only accepts this exact loopback redirect; a
|
|
141
|
+
// public-origin redirect is refused with "Invalid redirect URI". The browser
|
|
142
|
+
// opens it on the operator's own machine (the loopback listener belongs to the
|
|
143
|
+
// desktop flow), so the gateway cannot intercept it — the operator pastes the
|
|
144
|
+
// resulting URL back into the modal. On a server, the browser will show
|
|
145
|
+
// "can't be reached" for 127.0.0.1: that is expected; the address bar still
|
|
146
|
+
// carries the `code` to paste.
|
|
140
147
|
const DEVIN_BROWSER_REDIRECT_URI = "http://127.0.0.1:59653/callback";
|
|
141
148
|
|
|
142
149
|
export const devinOAuthClient: OAuthLoginClient = {
|
|
@@ -30,6 +30,7 @@ import type {
|
|
|
30
30
|
import {
|
|
31
31
|
decodeJwtPayload,
|
|
32
32
|
nonEmptyTrimmedString,
|
|
33
|
+
oauthAccountIdentity,
|
|
33
34
|
readJsonResponse,
|
|
34
35
|
record,
|
|
35
36
|
} from "../../authentication/oauth-flow-store";
|
|
@@ -104,9 +105,11 @@ function buildEnvelope(
|
|
|
104
105
|
// cause; failing upstream would surface as an opaque 401.
|
|
105
106
|
throw new Error("Factory Droid token carries no organization claim; sign in again");
|
|
106
107
|
}
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
108
|
+
const responseIdentity = oauthAccountIdentity(payload);
|
|
109
|
+
const email = nonEmptyTrimmedString(claims?.["email"]) ?? responseIdentity.accountEmail;
|
|
110
|
+
const accountName = responseIdentity.accountName ?? nonEmptyTrimmedString(claims?.["name"]);
|
|
111
|
+
const subject = nonEmptyTrimmedString(claims?.["sub"]) ?? nonEmptyTrimmedString(record(payload["user"])?.["id"]);
|
|
112
|
+
const label = accountLabel ?? email ?? accountName ?? subject;
|
|
110
113
|
const envelope: FactoryDroidEnvelope = { accessToken, orgId };
|
|
111
114
|
const result: OAuthExchangeResult = {
|
|
112
115
|
access: encodeFactoryDroidCredential(envelope),
|
|
@@ -117,12 +120,15 @@ function buildEnvelope(
|
|
|
117
120
|
refresh: nonEmptyTrimmedString(payload["refresh_token"]) ?? accessToken,
|
|
118
121
|
expiresAt: expiryFromClaims(claims),
|
|
119
122
|
...(label === undefined ? {} : { accountLabel: label }),
|
|
123
|
+
...(email === undefined ? {} : { accountEmail: email }),
|
|
124
|
+
...(accountName === undefined ? {} : { accountName }),
|
|
120
125
|
};
|
|
121
126
|
return {
|
|
122
127
|
result,
|
|
123
128
|
authState: {
|
|
124
129
|
orgId,
|
|
125
130
|
...(email === undefined ? {} : { email }),
|
|
131
|
+
...(accountName === undefined ? {} : { accountName }),
|
|
126
132
|
...(subject === undefined ? {} : { userId: subject }),
|
|
127
133
|
},
|
|
128
134
|
};
|
|
@@ -139,7 +145,7 @@ export class FactoryOAuthClient extends OAuthDeviceFlow {
|
|
|
139
145
|
protected override readonly scopes = "";
|
|
140
146
|
|
|
141
147
|
override async startDeviceAuth(
|
|
142
|
-
|
|
148
|
+
context?: OAuthDeviceFlowContext,
|
|
143
149
|
): Promise<OAuthDeviceStartResult> {
|
|
144
150
|
return this.startGenericDeviceAuth({
|
|
145
151
|
tokenUrl: FACTORY_DROID_TOKEN_URL,
|
|
@@ -147,7 +153,7 @@ export class FactoryOAuthClient extends OAuthDeviceFlow {
|
|
|
147
153
|
clientId: FACTORY_DROID_CLIENT_ID,
|
|
148
154
|
formHeaders: WORKOS_HEADERS,
|
|
149
155
|
startParams: { user_agent: "cartethyia" },
|
|
150
|
-
});
|
|
156
|
+
}, context);
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
/**
|
|
@@ -159,14 +165,14 @@ export class FactoryOAuthClient extends OAuthDeviceFlow {
|
|
|
159
165
|
*/
|
|
160
166
|
override async pollDeviceAuth(
|
|
161
167
|
deviceAuthId: string,
|
|
162
|
-
|
|
168
|
+
context?: OAuthDeviceFlowContext,
|
|
163
169
|
): Promise<OAuthDevicePollResult> {
|
|
164
170
|
return this.pollGenericDeviceAuth(deviceAuthId, {
|
|
165
171
|
tokenUrl: FACTORY_DROID_TOKEN_URL,
|
|
166
172
|
deviceAuthUrl: FACTORY_DROID_DEVICE_AUTH_URL,
|
|
167
173
|
clientId: FACTORY_DROID_CLIENT_ID,
|
|
168
174
|
formHeaders: { ...WORKOS_HEADERS, "content-type": "application/x-www-form-urlencoded" },
|
|
169
|
-
});
|
|
175
|
+
}, context);
|
|
170
176
|
}
|
|
171
177
|
|
|
172
178
|
protected override afterDeviceTokenExchange(
|
|
@@ -200,6 +206,9 @@ export class FactoryOAuthClient extends OAuthDeviceFlow {
|
|
|
200
206
|
refresh: refreshToken,
|
|
201
207
|
expiresAt: result.expiresAt,
|
|
202
208
|
auth_state: authState,
|
|
209
|
+
...(result.accountEmail === undefined ? {} : { accountEmail: result.accountEmail }),
|
|
210
|
+
...(result.accountName === undefined ? {} : { accountName: result.accountName }),
|
|
211
|
+
...(result.accountLabel === undefined ? {} : { accountLabel: result.accountLabel }),
|
|
203
212
|
};
|
|
204
213
|
}
|
|
205
214
|
}
|