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
|
@@ -25,28 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import protobuf from "protobufjs"
|
|
28
|
-
|
|
29
|
-
// ── Helper: create a root and add a Type with fields ──
|
|
30
|
-
|
|
31
|
-
type FieldDef = { id: number; name: string; type: string; repeated?: boolean }
|
|
32
|
-
type OneofDef = { name: string; fields: string[] }
|
|
33
|
-
|
|
34
|
-
function addType(
|
|
35
|
-
root: protobuf.Root,
|
|
36
|
-
name: string,
|
|
37
|
-
fields: FieldDef[],
|
|
38
|
-
oneofs?: OneofDef[],
|
|
39
|
-
): protobuf.Type {
|
|
40
|
-
const t = new protobuf.Type(name)
|
|
41
|
-
for (const f of fields) {
|
|
42
|
-
t.add(new protobuf.Field(f.name, f.id, f.type, f.repeated ? "repeated" : undefined))
|
|
43
|
-
}
|
|
44
|
-
for (const o of oneofs ?? []) {
|
|
45
|
-
t.add(new protobuf.OneOf(o.name, o.fields))
|
|
46
|
-
}
|
|
47
|
-
root.add(t)
|
|
48
|
-
return t
|
|
49
|
-
}
|
|
28
|
+
import { addType } from "./protobuf-schema"
|
|
50
29
|
|
|
51
30
|
// ── Utility messages ──
|
|
52
31
|
|
|
@@ -1809,8 +1788,8 @@ export function createMessageTypes(): protobuf.Root {
|
|
|
1809
1788
|
])
|
|
1810
1789
|
|
|
1811
1790
|
addType(root, "CancelAction", [
|
|
1812
|
-
//
|
|
1813
|
-
|
|
1791
|
+
// `agent.proto` declares CancelAction empty (oneof presence is the signal);
|
|
1792
|
+
// the reason travels on the stream, not in the message.
|
|
1814
1793
|
])
|
|
1815
1794
|
|
|
1816
1795
|
// Deferred answers for an AskQuestion the client already replied to with
|
|
@@ -1922,8 +1901,24 @@ export function createMessageTypes(): protobuf.Root {
|
|
|
1922
1901
|
{ id: 8, name: "turns", type: "ConversationTurn", repeated: true },
|
|
1923
1902
|
])
|
|
1924
1903
|
|
|
1904
|
+
// `agent.proto` ModelDetails.thinking_details — empty in the current build;
|
|
1905
|
+
// declared for wire-shape completeness.
|
|
1906
|
+
addType(root, "ThinkingDetails", [])
|
|
1907
|
+
|
|
1908
|
+
// `agent.proto` AgentRunRequest.skill_options (deprecated upstream; kept for
|
|
1909
|
+
// wire-shape completeness).
|
|
1910
|
+
addType(root, "SkillOptions", [
|
|
1911
|
+
{ id: 1, name: "skill_descriptors", type: "SkillDescriptor", repeated: true },
|
|
1912
|
+
])
|
|
1913
|
+
|
|
1925
1914
|
addType(root, "ModelDetails", [
|
|
1926
1915
|
{ id: 1, name: "model_id", type: "string" },
|
|
1916
|
+
{ id: 2, name: "thinking_details", type: "ThinkingDetails" },
|
|
1917
|
+
{ id: 3, name: "display_model_id", type: "string" },
|
|
1918
|
+
{ id: 4, name: "display_name", type: "string" },
|
|
1919
|
+
{ id: 5, name: "display_name_short", type: "string" },
|
|
1920
|
+
{ id: 6, name: "aliases", type: "string", repeated: true },
|
|
1921
|
+
{ id: 7, name: "max_mode", type: "bool" },
|
|
1927
1922
|
])
|
|
1928
1923
|
|
|
1929
1924
|
addType(root, "McpTools", [
|
|
@@ -1933,15 +1928,13 @@ export function createMessageTypes(): protobuf.Root {
|
|
|
1933
1928
|
addType(root, "AgentRunRequest", [
|
|
1934
1929
|
{ id: 1, name: "conversation_state", type: "bytes" },
|
|
1935
1930
|
{ id: 2, name: "action", type: "ConversationAction" },
|
|
1931
|
+
{ id: 3, name: "model_details", type: "ModelDetails" },
|
|
1936
1932
|
{ id: 4, name: "mcp_tools", type: "McpTools" },
|
|
1937
1933
|
{ id: 5, name: "conversation_id", type: "string" },
|
|
1934
|
+
{ id: 6, name: "mcp_file_system_options", type: "McpFileSystemOptions" },
|
|
1935
|
+
{ id: 7, name: "skill_options", type: "SkillOptions" },
|
|
1938
1936
|
{ id: 8, name: "custom_system_prompt", type: "string" },
|
|
1939
1937
|
{ id: 9, name: "requested_model", type: "RequestedModel" },
|
|
1940
|
-
{ id: 10, name: "unknown_flag", type: "uint32" },
|
|
1941
|
-
{ id: 12, name: "field_12", type: "uint32" },
|
|
1942
|
-
{ id: 14, name: "selected_subagent_models", type: "RequestedModel", repeated: true },
|
|
1943
|
-
{ id: 16, name: "conversation_group_id", type: "string" },
|
|
1944
|
-
{ id: 25, name: "run_id", type: "string" },
|
|
1945
1938
|
])
|
|
1946
1939
|
|
|
1947
1940
|
// ── Client heartbeat ──
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared protobufjs schema-builder helpers.
|
|
3
|
+
*
|
|
4
|
+
* Extracted verbatim from the vendored protocol modules that each carried the
|
|
5
|
+
* same implementation: `cursor/protocol/messages.ts` (vendored from
|
|
6
|
+
* `cursor-opencode-provider`, MIT) and `devin/protocol/devin-proto.ts`
|
|
7
|
+
* (vendored from `oh-my-pi`, MIT). The original license texts remain in those
|
|
8
|
+
* files; this module exists so future schema additions have one place to fix.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import protobuf from "protobufjs";
|
|
12
|
+
|
|
13
|
+
export type FieldDef = { id: number; name: string; type: string; repeated?: boolean };
|
|
14
|
+
export type OneofDef = { name: string; fields: string[] };
|
|
15
|
+
|
|
16
|
+
/** Create a root Type with fields and optional oneofs, and register it. */
|
|
17
|
+
export function addType(
|
|
18
|
+
root: protobuf.Root,
|
|
19
|
+
name: string,
|
|
20
|
+
fields: FieldDef[],
|
|
21
|
+
oneofs?: OneofDef[],
|
|
22
|
+
): protobuf.Type {
|
|
23
|
+
const t = new protobuf.Type(name);
|
|
24
|
+
for (const f of fields) {
|
|
25
|
+
t.add(new protobuf.Field(f.name, f.id, f.type, f.repeated ? "repeated" : undefined));
|
|
26
|
+
}
|
|
27
|
+
for (const o of oneofs ?? []) {
|
|
28
|
+
t.add(new protobuf.OneOf(o.name, o.fields));
|
|
29
|
+
}
|
|
30
|
+
root.add(t);
|
|
31
|
+
return t;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Register an enum on the root. */
|
|
35
|
+
export function addEnum(root: protobuf.Root, name: string, values: Record<string, number>): void {
|
|
36
|
+
root.add(new protobuf.Enum(name, values));
|
|
37
|
+
}
|
|
@@ -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
|
+
}
|