cartethyia 2.4.6 → 2.5.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/README.md +45 -13
- package/dist/dashboard/assets/BannedPage-ChqnTRpZ.js +1 -0
- package/dist/dashboard/assets/BannedPage-ChqnTRpZ.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-Bcy150Jo.js → CliToolDetailPage-Bb9CNuoh.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-Bb9CNuoh.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-DyNXefph.js → CliToolsPage-BXUaRCQQ.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BXUaRCQQ.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CisNZOUt.js +1 -0
- package/dist/dashboard/assets/CombosPage-CisNZOUt.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CATpAEOz.js → ConsoleLogPage-BOpI_LOH.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-BOpI_LOH.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-D6jEU7nL.js → CustomizationPage-DAkdvJ4k.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-DAkdvJ4k.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-CZkpn0a7.js → HealthEventsModal-DeT-VXHd.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-DeT-VXHd.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-BJlMVN4U.js → LoginPage-CytxSeGP.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CytxSeGP.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-Br3CXEau.js → ModelPicker-oQ-SDXp8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js +1 -0
- package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js +7 -0
- package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js +1 -0
- package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-BHW4Zt06.js → SettingsPage-8-eVadhc.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-8-eVadhc.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-VOYC_lIa.js → SetupPage-uEIXPyGO.js} +1 -1
- package/dist/dashboard/assets/SetupPage-uEIXPyGO.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DCY0TPTZ.js +1 -0
- package/dist/dashboard/assets/SortableList-DCY0TPTZ.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-BNIDLiU6.js → StudioPage-DqK5E26W.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DqK5E26W.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-38JfiZ0H.js +56 -0
- package/dist/dashboard/assets/UsagePage-38JfiZ0H.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BETfaMYZ.js → api-keys-BmDbsior.js} +1 -1
- package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
- package/dist/dashboard/assets/app-BbpSarVV.css +2 -0
- package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
- package/dist/dashboard/assets/assert-BHfaei-X.js +1 -0
- package/dist/dashboard/assets/assert-BHfaei-X.js.gz +0 -0
- package/dist/dashboard/assets/{badge-BTyGk_DL.js → badge-DpXT4Ixt.js} +1 -1
- package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
- package/dist/dashboard/assets/{banner-bujTWYM4.js → banner-CSAAWLJv.js} +1 -1
- package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
- package/dist/dashboard/assets/{card-B0Bnx5CA.js → card-DEhRXXw4.js} +1 -1
- package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-DE2ug812.js → cli-tools-B0wBq-RL.js} +1 -1
- package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-R_rArH8S.js → clipboard-button-DOo6EU8O.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BWIP_EvW.js → dialog-BvQeBMfP.js} +1 -1
- package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BKcgVugF.js → drawer-CMwPXwQB.js} +1 -1
- package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
- package/dist/dashboard/assets/entry--yj84zk3.js +4 -0
- package/dist/dashboard/assets/entry--yj84zk3.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CcWkGR4a.js → entry-D4O2229X.js} +1 -1
- package/dist/dashboard/assets/{entry-CcWkGR4a.js.gz → entry-D4O2229X.js.gz} +0 -0
- package/dist/dashboard/assets/{entry-kTiTFrvh.js → entry-DKvr5Q9K.js} +1 -1
- package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Booc5mNe.js → github-badge-zliLvBwr.js} +1 -1
- package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
- package/dist/dashboard/assets/{index-D70pChfn.js → index-CcD1eSJ6.js} +2 -2
- package/dist/dashboard/assets/index-CcD1eSJ6.js.gz +0 -0
- package/dist/dashboard/assets/{inline-D1TCqM2D.js → inline-D1IlZZ8H.js} +1 -1
- package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
- package/dist/dashboard/assets/{input-B9oyU93y.js → input-BpVZdrbc.js} +1 -1
- package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BArzXirQ.js → layout-BioLW7gN.js} +1 -1
- package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BVZzaoHF.js → list-row-CwVcGAIc.js} +1 -1
- package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
- package/dist/dashboard/assets/{live-EmuIayqp.js → live-yfEJFF9T.js} +1 -1
- package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-CSPQ9xB0.js → model-bans-CT-TxSNX.js} +1 -1
- package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
- package/dist/dashboard/assets/providers/cursor.webp +0 -0
- package/dist/dashboard/assets/providers/trae.webp +0 -0
- package/dist/dashboard/assets/{routing-BExgxxBU.js → routing-Cs6jl49L.js} +1 -1
- package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
- package/dist/dashboard/assets/select-XPJ-dIw3.js +41 -0
- package/dist/dashboard/assets/select-XPJ-dIw3.js.gz +0 -0
- package/dist/dashboard/assets/settings-CfcTcOyU.js +1 -0
- package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
- package/dist/dashboard/assets/{stack-Ca5HxI5i.js → stack-C9_uUjaA.js} +1 -1
- package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
- package/dist/dashboard/assets/{state-CzUPfLXo.js → state-DA0B95UQ.js} +1 -1
- package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DOUIky_i.js → switch-Cittxy-x.js} +1 -1
- package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
- package/dist/dashboard/assets/{toast-C76WrMhc.js → toast-CNt_jzO4.js} +1 -1
- package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-D87FZwH0.js → useMutation-BQKMcJ1H.js} +1 -1
- package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -52
- package/migrations/full-postgres/0015_add_payload_stages.sql +18 -0
- package/migrations/full-postgres/0016_add_share_link_slug.sql +20 -0
- package/migrations/full-postgres/0017_add_models_pro_only.sql +18 -0
- package/migrations/full-postgres/0018_add_accounts_quota_plan.sql +18 -0
- package/migrations/full-postgres/0019_add_models_free_tier.sql +15 -0
- package/migrations/lite-sqlite3/0015_add_payload_stages.sql +18 -0
- package/migrations/lite-sqlite3/0016_add_share_link_slug.sql +20 -0
- package/migrations/lite-sqlite3/0017_add_models_pro_only.sql +18 -0
- package/migrations/lite-sqlite3/0018_add_accounts_quota_plan.sql +18 -0
- package/migrations/lite-sqlite3/0019_add_models_free_tier.sql +15 -0
- package/package.json +3 -2
- package/scripts/cli/command/console-menu.ts +30 -11
- package/scripts/cli/command/start.ts +77 -13
- package/scripts/cli/command/tray.ts +47 -8
- package/scripts/cli/command/update.ts +1 -1
- package/scripts/cli/gateway/instance-lock.ts +105 -0
- package/scripts/cli/gateway/spawn.ts +173 -101
- package/scripts/cli/ui/background.ts +110 -49
- package/src/boot/app.ts +20 -6
- package/src/boot/dependencies.ts +19 -0
- package/src/boot/elysia-typebox-bridge.ts +37 -0
- package/src/boot/lifecycle.ts +2 -0
- package/src/config/cooldowns.ts +138 -138
- package/src/config/providers.ts +5 -5
- package/src/config/runtime.ts +19 -3
- package/src/config/spec.ts +9 -0
- package/src/console/api-keys/contracts.ts +414 -391
- package/src/console/api-keys/routes.ts +880 -870
- package/src/console/auth/account-reset-store.ts +14 -0
- package/src/console/auth/account-reset.ts +3 -2
- package/src/console/auth/setup-service.ts +6 -3
- package/src/console/domain-registration.ts +9 -2
- package/src/console/observability/contracts.ts +475 -455
- package/src/console/observability/store.ts +1015 -952
- package/src/console/providers/catalog/contracts.ts +31 -0
- package/src/console/providers/catalog/provider-operations.ts +8 -0
- package/src/console/providers/catalog/store.ts +1532 -1525
- package/src/console/providers/oauth/callback-listener.ts +18 -3
- package/src/console/providers/oauth/routes.ts +38 -15
- package/src/console/quota/refresh.ts +19 -0
- package/src/console/updates/routes.ts +162 -4
- package/src/main.ts +3 -0
- package/src/network/http2-bidi.ts +85 -0
- package/src/network/outbound-fetch.ts +1 -26
- package/src/network/pool/agent.ts +478 -460
- package/src/network/pool/resolver.ts +1104 -544
- package/src/network/pool-health-machine.ts +12 -5
- package/src/observability/payload-capture.ts +344 -324
- package/src/persistence/db-pg.ts +9 -3
- package/src/persistence/db-sqlite.ts +497 -495
- package/src/persistence/health-event-flush.ts +137 -0
- package/src/persistence/migrate.ts +181 -129
- package/src/persistence/schema.ts +1360 -1295
- package/src/persistence/settings-revision.ts +25 -0
- package/src/persistence/shared-cache.ts +217 -0
- package/src/persistence/stores/share-store.ts +556 -528
- package/src/persistence/usage-flush.ts +61 -1
- package/src/protocol/canonical-model.ts +23 -1
- package/src/protocol/response/chat.ts +143 -10
- package/src/protocol/response/omp-tool-call-markup.ts +221 -0
- package/src/providers/default-registry.ts +173 -1
- package/src/providers/discovery/openai-model-discovery.ts +39 -3
- package/src/providers/discovery/openrouter-free-roster.ts +98 -0
- package/src/providers/discovery/probing-service.ts +1022 -1001
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +39 -0
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +18 -5
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +4 -0
- package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -175
- package/src/providers/integrations/buddy/codebuddy/shared.ts +15 -6
- package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
- package/src/providers/integrations/buddy/workbuddy/shared.ts +3 -3
- package/src/providers/integrations/claude/claude.ts +606 -582
- package/src/providers/integrations/cline/cline-quota.ts +18 -16
- package/src/providers/integrations/cline/cline.ts +539 -468
- package/src/providers/integrations/commandcode/commandcode.ts +675 -541
- package/src/providers/integrations/commandcode/discovery.ts +116 -0
- package/src/providers/integrations/commandcode/quota.ts +160 -0
- package/src/providers/integrations/cursor/cursor-auth.ts +418 -0
- package/src/providers/integrations/cursor/cursor-catalog.ts +368 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +300 -0
- package/src/providers/integrations/cursor/cursor-identity.ts +92 -0
- package/src/providers/integrations/cursor/cursor-request.ts +471 -0
- package/src/providers/integrations/cursor/cursor-session-manager.ts +281 -0
- package/src/providers/integrations/cursor/cursor-shared.ts +108 -0
- package/src/providers/integrations/cursor/cursor.ts +960 -0
- package/src/providers/integrations/cursor/protocol/framing.ts +129 -0
- package/src/providers/integrations/cursor/protocol/messages.ts +2355 -0
- package/src/providers/integrations/cursor/protocol/struct.ts +305 -0
- package/src/providers/integrations/github/github.ts +4 -4
- package/src/providers/integrations/hermes.ts +79 -8
- package/src/providers/integrations/kilo/kilo-discovery.ts +10 -1
- package/src/providers/integrations/kilo/kilo.ts +153 -146
- package/src/providers/integrations/opencode/provider.ts +532 -538
- package/src/providers/integrations/openrouter/provider.ts +139 -281
- package/src/providers/integrations/qoder/qoder-catalog.ts +450 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +310 -257
- package/src/providers/integrations/qoder/qoder-realm.ts +106 -0
- package/src/providers/integrations/qoder/qoder-virtual.ts +77 -0
- package/src/providers/integrations/qoder/qoder.ts +865 -730
- package/src/providers/integrations/trae/trae-catalog.ts +190 -0
- package/src/providers/integrations/trae/trae-headers.ts +149 -0
- package/src/providers/integrations/trae/trae-oauth.ts +773 -0
- package/src/providers/integrations/trae/trae-protocol.ts +400 -0
- package/src/providers/integrations/trae/trae-realm.ts +220 -0
- package/src/providers/integrations/trae/trae.ts +458 -0
- package/src/providers/operations/account-health-service.ts +107 -10
- package/src/providers/operations/client-versions.ts +133 -6
- package/src/providers/operations/provider-catalog-seeder.ts +95 -86
- package/src/providers/operations/provider-credential-service.ts +46 -7
- package/src/providers/provider-contracts.ts +76 -0
- package/src/providers/provider-metadata.ts +78 -6
- package/src/providers/provider-model-definition.ts +373 -349
- package/src/security/api-key-auth.ts +126 -8
- package/src/transport/contracts.ts +3 -0
- package/src/transport/dispatch/attempt-finalize.ts +32 -1
- package/src/transport/dispatch/attempt-loop.ts +530 -515
- package/src/transport/dispatch/generation.ts +88 -21
- package/src/transport/dispatch/leases.ts +99 -2
- package/src/transport/dispatch/proxy-request.ts +60 -3
- package/src/transport/dispatch/streaming/finalize.ts +16 -1
- package/src/transport/dispatch/streaming/stream.ts +22 -2
- package/src/transport/dispatch/upstream.ts +19 -1
- package/src/transport/guards/auth-guard.ts +26 -1
- package/src/transport/guards/body-policy.ts +5 -4
- package/src/transport/request/preparer.ts +103 -0
- package/src/transport/request/state.ts +14 -0
- package/src/transport/routing/plan-gate.ts +45 -0
- package/src/transport/routing/route-catalog.ts +799 -777
- package/src/transport/routing/route-model.ts +52 -1
- package/src/transport/routing/router.ts +51 -0
- package/dist/dashboard/assets/BannedPage-QhvbJB9o.js +0 -1
- package/dist/dashboard/assets/BannedPage-QhvbJB9o.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-Bcy150Jo.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DyNXefph.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-Cak0ljnO.js +0 -1
- package/dist/dashboard/assets/CombosPage-Cak0ljnO.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CATpAEOz.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-D6jEU7nL.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-CZkpn0a7.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BJlMVN4U.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-Br3CXEau.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CXiumA53.js +0 -7
- package/dist/dashboard/assets/ProxyPage-CXiumA53.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js +0 -1
- package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BHW4Zt06.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-VOYC_lIa.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DpOTnGuH.js +0 -1
- package/dist/dashboard/assets/SortableList-DpOTnGuH.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-BNIDLiU6.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BAFaSDQV.js +0 -56
- package/dist/dashboard/assets/UsagePage-BAFaSDQV.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BETfaMYZ.js.gz +0 -0
- package/dist/dashboard/assets/app-BLCvGdzb.css +0 -2
- package/dist/dashboard/assets/app-BLCvGdzb.css.gz +0 -0
- package/dist/dashboard/assets/assert-7J6_LVD3.js +0 -1
- package/dist/dashboard/assets/assert-7J6_LVD3.js.gz +0 -0
- package/dist/dashboard/assets/badge-BTyGk_DL.js.gz +0 -0
- package/dist/dashboard/assets/banner-bujTWYM4.js.gz +0 -0
- package/dist/dashboard/assets/card-B0Bnx5CA.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-DE2ug812.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-R_rArH8S.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BWIP_EvW.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BKcgVugF.js.gz +0 -0
- package/dist/dashboard/assets/entry-7dpgvpJ3.js +0 -4
- package/dist/dashboard/assets/entry-7dpgvpJ3.js.gz +0 -0
- package/dist/dashboard/assets/entry-kTiTFrvh.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Booc5mNe.js.gz +0 -0
- package/dist/dashboard/assets/index-D70pChfn.js.gz +0 -0
- package/dist/dashboard/assets/inline-D1TCqM2D.js.gz +0 -0
- package/dist/dashboard/assets/input-B9oyU93y.js.gz +0 -0
- package/dist/dashboard/assets/layout-BArzXirQ.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BVZzaoHF.js.gz +0 -0
- package/dist/dashboard/assets/live-EmuIayqp.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CSPQ9xB0.js.gz +0 -0
- package/dist/dashboard/assets/routing-BExgxxBU.js.gz +0 -0
- package/dist/dashboard/assets/select-C9mMoEpJ.js +0 -41
- package/dist/dashboard/assets/select-C9mMoEpJ.js.gz +0 -0
- package/dist/dashboard/assets/settings-C4_Gn_wq.js +0 -1
- package/dist/dashboard/assets/settings-C4_Gn_wq.js.gz +0 -0
- package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
- package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js.gz +0 -0
- package/dist/dashboard/assets/stack-Ca5HxI5i.js.gz +0 -0
- package/dist/dashboard/assets/state-CzUPfLXo.js.gz +0 -0
- package/dist/dashboard/assets/switch-DOUIky_i.js.gz +0 -0
- package/dist/dashboard/assets/toast-C76WrMhc.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-D87FZwH0.js.gz +0 -0
- package/src/network/tls/bun-client-hello.ts +0 -112
- /package/dist/dashboard/assets/{app-Cjq7nrwu.js → app-C-Y4oZT0.js} +0 -0
- /package/dist/dashboard/assets/{app-Cjq7nrwu.js.gz → app-C-Y4oZT0.js.gz} +0 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
// Trae wire protocol: request body and SSE translation.
|
|
2
|
+
//
|
|
3
|
+
// Two upstream rules drive everything here:
|
|
4
|
+
//
|
|
5
|
+
// 1. The model key is `model_name`, not `model`. Sending `model` is silently
|
|
6
|
+
// ignored and upstream answers with its default model, so the caller gets a
|
|
7
|
+
// different model than it asked for with no error.
|
|
8
|
+
// 2. Message `content` must be an array of parts. A plain string is refused
|
|
9
|
+
// (400/4001), so every message is normalized to parts even when there is
|
|
10
|
+
// only text.
|
|
11
|
+
//
|
|
12
|
+
// Sourced from the donor hub `Public/trae2api-hub` (`build_chat_body` and
|
|
13
|
+
// `iter_events` in `trae_client.py`).
|
|
14
|
+
|
|
15
|
+
import type { CanonicalRequest, ContentPart } from "../../../protocol/canonical-model";
|
|
16
|
+
import {
|
|
17
|
+
TRAE_AGENT_CHAT_PATH,
|
|
18
|
+
TRAE_AGENT_FUNCTION,
|
|
19
|
+
TRAE_CHAT_PATH,
|
|
20
|
+
TRAE_MODEL_LIST_PATH,
|
|
21
|
+
TRAE_REALMS,
|
|
22
|
+
type TraeRealm,
|
|
23
|
+
} from "./trae-realm";
|
|
24
|
+
|
|
25
|
+
/** Upstream chat body. */
|
|
26
|
+
export interface TraeChatBody {
|
|
27
|
+
readonly model_name: string;
|
|
28
|
+
readonly messages: readonly TraeMessage[];
|
|
29
|
+
readonly stream: boolean;
|
|
30
|
+
readonly region: string;
|
|
31
|
+
readonly ai_region: string;
|
|
32
|
+
readonly temperature?: number;
|
|
33
|
+
readonly top_p?: number;
|
|
34
|
+
readonly max_tokens?: number;
|
|
35
|
+
readonly stop?: readonly string[];
|
|
36
|
+
readonly presence_penalty?: number;
|
|
37
|
+
readonly frequency_penalty?: number;
|
|
38
|
+
readonly session_id?: string;
|
|
39
|
+
/** Tool declarations; `parameters` is a JSON string, not a schema object. */
|
|
40
|
+
readonly tools?: readonly TraeToolDeclaration[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** One declared tool, in the shape upstream binds. */
|
|
44
|
+
export interface TraeToolDeclaration {
|
|
45
|
+
readonly type: "function";
|
|
46
|
+
readonly function: {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly description?: string;
|
|
49
|
+
readonly parameters: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface TraeMessage {
|
|
54
|
+
readonly role: "system" | "user" | "assistant" | "tool";
|
|
55
|
+
readonly content: readonly TraeContentPart[];
|
|
56
|
+
readonly tool_call_id?: string;
|
|
57
|
+
readonly tool_calls?: readonly TraeToolCall[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type TraeContentPart =
|
|
61
|
+
| { readonly type: "text"; readonly text: string }
|
|
62
|
+
| { readonly type: "image_url"; readonly image_url: { readonly url: string } };
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* One tool call, in the shape upstream's protobuf expects.
|
|
66
|
+
*
|
|
67
|
+
* The nested key is `function_call`, not OpenAI's `function`, and `Name` is a
|
|
68
|
+
* **required** field on replay: an assistant turn sent back with
|
|
69
|
+
* `function: {name: ""}` is rejected before it reaches the model with
|
|
70
|
+
* `required field Name is not set`. Both spellings therefore have to carry the
|
|
71
|
+
* name, and the arguments stay a JSON string rather than an object.
|
|
72
|
+
*/
|
|
73
|
+
export interface TraeToolCall {
|
|
74
|
+
readonly id: string;
|
|
75
|
+
readonly type: "function";
|
|
76
|
+
readonly function_call: { readonly name: string; readonly arguments: string };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Generation fields upstream accepts.
|
|
81
|
+
*
|
|
82
|
+
* Undeclared fields are deliberately omitted: the donor notes that sending
|
|
83
|
+
* anything outside this set risks a 4001, so `provider_options` is never
|
|
84
|
+
* spread into the body wholesale.
|
|
85
|
+
*/
|
|
86
|
+
const PASSTHROUGH_OPTIONS = ["temperature", "top_p", "max_tokens", "stop"] as const;
|
|
87
|
+
|
|
88
|
+
function textOf(part: ContentPart): string {
|
|
89
|
+
return part.kind === "text" ? part.text : "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Renders an image part as a URL upstream accepts.
|
|
94
|
+
*
|
|
95
|
+
* The canonical image payload is opaque (`payload`), because every surface
|
|
96
|
+
* spells an image differently: base64 with a media type, a bare URL, or a
|
|
97
|
+
* provider file id. Only the first two have an upstream representation, so a
|
|
98
|
+
* payload this adapter cannot recognize is dropped rather than sent as a
|
|
99
|
+
* malformed data URI.
|
|
100
|
+
*/
|
|
101
|
+
function dataUriOf(part: Extract<ContentPart, { kind: "image" }>): string | undefined {
|
|
102
|
+
const payload = part.payload;
|
|
103
|
+
if (typeof payload === "string") {
|
|
104
|
+
return /^https?:\/\//i.test(payload) ? payload : undefined;
|
|
105
|
+
}
|
|
106
|
+
if (typeof payload !== "object" || payload === null) return undefined;
|
|
107
|
+
const record = payload as Record<string, unknown>;
|
|
108
|
+
const url = record["url"];
|
|
109
|
+
if (typeof url === "string" && url.length > 0) return url;
|
|
110
|
+
const data = record["data"];
|
|
111
|
+
const mediaType = record["media_type"] ?? record["mediaType"];
|
|
112
|
+
if (typeof data === "string" && typeof mediaType === "string") {
|
|
113
|
+
// `data` may already carry its own `data:` prefix from a previous hop.
|
|
114
|
+
return data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
|
|
115
|
+
}
|
|
116
|
+
const b64 = record["b64_json"] ?? record["base64"];
|
|
117
|
+
if (typeof b64 === "string" && typeof mediaType === "string") {
|
|
118
|
+
return `data:${mediaType};base64,${b64}`;
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Maps one canonical part onto the upstream part shape. */
|
|
124
|
+
function toTraePart(part: ContentPart): TraeContentPart | undefined {
|
|
125
|
+
if (part.kind === "text") return { type: "text", text: part.text };
|
|
126
|
+
if (part.kind === "image") {
|
|
127
|
+
const url = dataUriOf(part);
|
|
128
|
+
return url === undefined ? undefined : { type: "image_url", image_url: { url } };
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Serializes tool-call arguments, which arrive parsed rather than as text. */
|
|
134
|
+
function toolArguments(args: unknown): string {
|
|
135
|
+
if (typeof args === "string") return args;
|
|
136
|
+
try {
|
|
137
|
+
return JSON.stringify(args ?? {});
|
|
138
|
+
} catch {
|
|
139
|
+
return "{}";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Flattens a tool result's content to the single string upstream takes. */
|
|
144
|
+
function toolResultText(content: readonly ContentPart[] | string): string {
|
|
145
|
+
if (typeof content === "string") return content;
|
|
146
|
+
return content
|
|
147
|
+
.map((part) => (part.kind === "text" ? part.text : ""))
|
|
148
|
+
.filter((text) => text.length > 0)
|
|
149
|
+
.join("\n");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Builds the upstream chat body from a canonical request.
|
|
154
|
+
*
|
|
155
|
+
* System content is promoted to a leading `system` message: upstream has no
|
|
156
|
+
* separate system field, and dropping it (rather than promoting it) is what
|
|
157
|
+
* silently loses the caller's instructions.
|
|
158
|
+
*/
|
|
159
|
+
export function buildTraeChatBody(
|
|
160
|
+
modelId: string,
|
|
161
|
+
request: CanonicalRequest,
|
|
162
|
+
realm: TraeRealm,
|
|
163
|
+
): TraeChatBody {
|
|
164
|
+
if (modelId.length === 0) throw new Error("trae model name is required");
|
|
165
|
+
|
|
166
|
+
const messages: TraeMessage[] = [];
|
|
167
|
+
const systemParts: string[] = [];
|
|
168
|
+
for (const part of request.system ?? []) {
|
|
169
|
+
const text = textOf(part);
|
|
170
|
+
if (text.length > 0) systemParts.push(text);
|
|
171
|
+
}
|
|
172
|
+
for (const part of request.instructions ?? []) {
|
|
173
|
+
const text = textOf(part);
|
|
174
|
+
if (text.length > 0) systemParts.push(text);
|
|
175
|
+
}
|
|
176
|
+
if (systemParts.length > 0) {
|
|
177
|
+
messages.push({ role: "system", content: [{ type: "text", text: systemParts.join("\n\n") }] });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const message of request.messages) {
|
|
181
|
+
// `system` was promoted into the leading turn above; appending it again
|
|
182
|
+
// would leave a second, late instruction turn. `developer` has no upstream
|
|
183
|
+
// equivalent and is folded into the same leading turn rather than dropped.
|
|
184
|
+
if (message.role === "system" || message.role === "developer") {
|
|
185
|
+
const text = message.content.map(textOf).filter((t) => t.length > 0).join("\n\n");
|
|
186
|
+
if (text.length > 0) systemParts.push(text);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const parts: TraeContentPart[] = [];
|
|
190
|
+
const toolCalls: TraeToolCall[] = [];
|
|
191
|
+
let toolResult: { callId: string; text: string } | undefined;
|
|
192
|
+
for (const part of message.content) {
|
|
193
|
+
if (part.kind === "toolCall") {
|
|
194
|
+
toolCalls.push({
|
|
195
|
+
id: part.call_id,
|
|
196
|
+
type: "function",
|
|
197
|
+
function_call: { name: part.name, arguments: toolArguments(part.arguments) },
|
|
198
|
+
});
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (part.kind === "toolResult") {
|
|
202
|
+
toolResult = { callId: part.call_id, text: toolResultText(part.content) };
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const mapped = toTraePart(part);
|
|
206
|
+
if (mapped !== undefined) parts.push(mapped);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// A tool answer becomes its own `tool` turn, which is the only shape
|
|
210
|
+
// upstream accepts for it — leaving it inside the assistant turn is
|
|
211
|
+
// rejected as a broken tool sequence.
|
|
212
|
+
if (toolResult !== undefined) {
|
|
213
|
+
messages.push({
|
|
214
|
+
role: "tool",
|
|
215
|
+
content: [{ type: "text", text: toolResult.text }],
|
|
216
|
+
tool_call_id: toolResult.callId,
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
// Empty turns are skipped: upstream rejects them, and emitting
|
|
221
|
+
// `content: ""` turns leaves a ghost turn in the conversation.
|
|
222
|
+
if (parts.length === 0 && toolCalls.length === 0) continue;
|
|
223
|
+
messages.push({
|
|
224
|
+
role: message.role === "assistant" ? "assistant" : "user",
|
|
225
|
+
content: parts.length > 0 ? parts : [{ type: "text", text: "" }],
|
|
226
|
+
...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const config = TRAE_REALMS[realm];
|
|
231
|
+
const controls = request.generation_controls;
|
|
232
|
+
const body: Record<string, unknown> = {
|
|
233
|
+
model_name: modelId,
|
|
234
|
+
messages,
|
|
235
|
+
stream: request.stream,
|
|
236
|
+
region: config.region,
|
|
237
|
+
ai_region: config.region,
|
|
238
|
+
...(request.session_id ? { session_id: request.session_id } : {}),
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Tool declarations, with `parameters` serialized to a string.
|
|
242
|
+
*
|
|
243
|
+
* Upstream's `FunctionDefinition` types that field as a Go `string`, so an
|
|
244
|
+
* object is rejected outright: `cannot unmarshal object into Go struct field
|
|
245
|
+
* FunctionDefinition.tools.function.parameters of type string`. Sending the
|
|
246
|
+
* schema as a JSON string is the only shape it binds.
|
|
247
|
+
*/
|
|
248
|
+
const tools = request.tools;
|
|
249
|
+
if (tools !== undefined && tools.length > 0) {
|
|
250
|
+
body["tools"] = tools.map((tool) => ({
|
|
251
|
+
type: "function",
|
|
252
|
+
function: {
|
|
253
|
+
name: tool.name,
|
|
254
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
255
|
+
parameters: toolArguments(tool.jsonSchema),
|
|
256
|
+
},
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
const options: Record<string, unknown> = {
|
|
260
|
+
temperature: controls.temperature,
|
|
261
|
+
top_p: controls.top_p,
|
|
262
|
+
max_tokens: controls.max_tokens,
|
|
263
|
+
stop: controls.stop,
|
|
264
|
+
|
|
265
|
+
};
|
|
266
|
+
for (const key of PASSTHROUGH_OPTIONS) {
|
|
267
|
+
const value = options[key];
|
|
268
|
+
if (value !== undefined && value !== null) body[key] = value;
|
|
269
|
+
}
|
|
270
|
+
return body as unknown as TraeChatBody;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Body for the live model list. Upstream requires a body even to list. */
|
|
274
|
+
export function buildTraeModelListBody(): Record<string, unknown> {
|
|
275
|
+
return { type: "chat" };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export const TRAE_CHAT_ENDPOINT = TRAE_CHAT_PATH;
|
|
279
|
+
export const TRAE_AGENT_ENDPOINT = TRAE_AGENT_CHAT_PATH;
|
|
280
|
+
export const TRAE_MODEL_LIST_ENDPOINT = TRAE_MODEL_LIST_PATH;
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Rewrites a chat-surface body into the agent surface's shape.
|
|
284
|
+
*
|
|
285
|
+
* Two changes, both required and neither optional:
|
|
286
|
+
*
|
|
287
|
+
* 1. The model key moves from `model_name` to `config_name`, and
|
|
288
|
+
* `config_source: 1` is added. Without `config_source` the server resolves
|
|
289
|
+
* the right upstream model and then streams nothing at all, which presents
|
|
290
|
+
* as a broken model rather than a missing field.
|
|
291
|
+
* 2. `function` selects the roster. Without it the call fails before model
|
|
292
|
+
* resolution with `[LLMUtilsChat.resolveByUsage] function is empty`.
|
|
293
|
+
*
|
|
294
|
+
* Everything else — messages, generation controls, session id — is carried
|
|
295
|
+
* over unchanged; the agent surface reads the same message shape.
|
|
296
|
+
*/
|
|
297
|
+
export function toTraeAgentBody(body: TraeChatBody): Record<string, unknown> {
|
|
298
|
+
const draft = body as unknown as Record<string, unknown>;
|
|
299
|
+
const rewritten: Record<string, unknown> = { function: TRAE_AGENT_FUNCTION };
|
|
300
|
+
for (const [key, value] of Object.entries(draft)) {
|
|
301
|
+
if (key === "model_name") continue;
|
|
302
|
+
rewritten[key] = value;
|
|
303
|
+
}
|
|
304
|
+
rewritten["config_name"] = draft["model_name"];
|
|
305
|
+
rewritten["config_source"] = 1;
|
|
306
|
+
return rewritten;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* True when a dispatch target belongs to the agent surface.
|
|
311
|
+
*
|
|
312
|
+
* Compared by suffix so an absolute URL target still resolves: a catalog row
|
|
313
|
+
* may carry a full `https://…/api/agent/v3/llm_utils_chat` rather than a bare
|
|
314
|
+
* path, and prefixing host logic elsewhere must not flip the surface.
|
|
315
|
+
*/
|
|
316
|
+
export function isTraeAgentEndpoint(endpointPath: string | null | undefined): boolean {
|
|
317
|
+
if (typeof endpointPath !== "string" || endpointPath.length === 0) return true;
|
|
318
|
+
return endpointPath.endsWith(TRAE_AGENT_CHAT_PATH);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** One parsed SSE frame. */
|
|
322
|
+
export interface TraeSseFrame {
|
|
323
|
+
readonly event: string;
|
|
324
|
+
readonly data: unknown;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Parses an SSE byte stream into frames.
|
|
329
|
+
*
|
|
330
|
+
* The done sentinel is a `data:` payload equal to `[DONE]`, which upstream
|
|
331
|
+
* sends instead of a named `done` event. Comment lines (leading `:`) are
|
|
332
|
+
* heartbeats and are skipped rather than treated as data.
|
|
333
|
+
*/
|
|
334
|
+
export async function* parseTraeSse(
|
|
335
|
+
body: ReadableStream<Uint8Array>,
|
|
336
|
+
): AsyncIterable<TraeSseFrame> {
|
|
337
|
+
const decoder = new TextDecoder();
|
|
338
|
+
const reader = body.getReader();
|
|
339
|
+
let buffer = "";
|
|
340
|
+
let event = "message";
|
|
341
|
+
let data: string[] = [];
|
|
342
|
+
try {
|
|
343
|
+
while (true) {
|
|
344
|
+
const { done, value } = await reader.read();
|
|
345
|
+
if (done) break;
|
|
346
|
+
buffer += decoder.decode(value, { stream: true });
|
|
347
|
+
let newline = buffer.indexOf("\n");
|
|
348
|
+
while (newline >= 0) {
|
|
349
|
+
const line = buffer.slice(0, newline).replace(/\r$/, "");
|
|
350
|
+
buffer = buffer.slice(newline + 1);
|
|
351
|
+
if (line === "") {
|
|
352
|
+
if (data.length > 0) {
|
|
353
|
+
const payload = data.join("\n");
|
|
354
|
+
data = [];
|
|
355
|
+
if (payload.trim() === "[DONE]") {
|
|
356
|
+
yield { event: "done", data: { finish_reason: "stop" } };
|
|
357
|
+
} else {
|
|
358
|
+
let parsed: unknown = payload;
|
|
359
|
+
try {
|
|
360
|
+
parsed = JSON.parse(payload);
|
|
361
|
+
} catch {
|
|
362
|
+
// Upstream occasionally emits non-JSON keepalives; a frame we
|
|
363
|
+
// cannot parse is dropped rather than failing the stream.
|
|
364
|
+
parsed = payload;
|
|
365
|
+
}
|
|
366
|
+
yield { event, data: parsed };
|
|
367
|
+
}
|
|
368
|
+
event = "message";
|
|
369
|
+
}
|
|
370
|
+
} else if (line.startsWith(":")) {
|
|
371
|
+
continue;
|
|
372
|
+
} else {
|
|
373
|
+
const separator = line.indexOf(":");
|
|
374
|
+
const field = separator < 0 ? line : line.slice(0, separator);
|
|
375
|
+
let value = separator < 0 ? "" : line.slice(separator + 1);
|
|
376
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
377
|
+
if (field === "event") event = value;
|
|
378
|
+
else if (field === "data") data.push(value);
|
|
379
|
+
}
|
|
380
|
+
newline = buffer.indexOf("\n");
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (data.length > 0) {
|
|
384
|
+
const payload = data.join("\n");
|
|
385
|
+
if (payload.trim() === "[DONE]") {
|
|
386
|
+
yield { event: "done", data: { finish_reason: "stop" } };
|
|
387
|
+
} else {
|
|
388
|
+
let parsed: unknown = payload;
|
|
389
|
+
try {
|
|
390
|
+
parsed = JSON.parse(payload);
|
|
391
|
+
} catch {
|
|
392
|
+
parsed = payload;
|
|
393
|
+
}
|
|
394
|
+
yield { event, data: parsed };
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
} finally {
|
|
398
|
+
reader.releaseLock();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Trae realms: one vendor, two unrelated deployments.
|
|
2
|
+
//
|
|
3
|
+
// `trae` (global / intl) and `traecn` (China) share the wire protocol — an
|
|
4
|
+
// `x-ide-token` credential, a `model_name` body, an SSE stream — but nothing
|
|
5
|
+
// else about the address: different core host, different account/oauth host,
|
|
6
|
+
// different UA, a *different authorization scheme*, and a different model
|
|
7
|
+
// roster. Treating them as one provider with a region flag is what would send
|
|
8
|
+
// a CN account to `*.traeapi.us` and get it refused.
|
|
9
|
+
//
|
|
10
|
+
// Everything per-realm lives here so the adapter, the auth builder, and the
|
|
11
|
+
// discovery fetcher cannot drift apart on host, UA, or version code.
|
|
12
|
+
//
|
|
13
|
+
// Sourced from the donor hub `Public/trae2api-hub` (`REALMS` in
|
|
14
|
+
// `trae_client.py`, `TRAEREALMS`-shaped config in `trae_accounts.py`).
|
|
15
|
+
|
|
16
|
+
/** One Trae deployment. */
|
|
17
|
+
export type TraeRealm = "intl" | "cn";
|
|
18
|
+
|
|
19
|
+
/** Per-realm upstream addresses, client identity, and auth scheme. */
|
|
20
|
+
export interface TraeRealmConfig {
|
|
21
|
+
/** Data-plane host: chat + model list (`/api/ide/v1/*`). */
|
|
22
|
+
readonly core: string;
|
|
23
|
+
/** Fallback core hosts, tried in order after `core`. */
|
|
24
|
+
readonly coreFallbacks: readonly string[];
|
|
25
|
+
/** Business host: CloudIDE OAuth token exchange, entitlement. */
|
|
26
|
+
readonly account: string;
|
|
27
|
+
/** Fallback account hosts. */
|
|
28
|
+
readonly accountFallbacks: readonly string[];
|
|
29
|
+
/** Entitlement / payment host. */
|
|
30
|
+
readonly pay: string;
|
|
31
|
+
/** Growth host (daily check-in credits). CN-only in practice. */
|
|
32
|
+
readonly ug: string;
|
|
33
|
+
/** Upstream-facing User-Agent for the non-chat endpoints. */
|
|
34
|
+
readonly userAgent: string;
|
|
35
|
+
/** Version code sent as `x-ide-version-code`. */
|
|
36
|
+
readonly versionCode: string;
|
|
37
|
+
/** Region tag sent in the request body. */
|
|
38
|
+
readonly region: string;
|
|
39
|
+
/** True when this realm's `/api/ide/v1/model_list` answers. */
|
|
40
|
+
readonly modelListSupported: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Chat path, identical across realms.
|
|
45
|
+
*
|
|
46
|
+
* The list endpoint takes a body (`{"type":"chat"}`); the donor posts it, so
|
|
47
|
+
* this is not a bare GET.
|
|
48
|
+
*/
|
|
49
|
+
export const TRAE_CHAT_PATH = "/api/ide/v1/llm_raw_chat";
|
|
50
|
+
export const TRAE_MODEL_LIST_PATH = "/api/ide/v1/model_list";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Agent path, serving the IDE picker's roster.
|
|
54
|
+
*
|
|
55
|
+
* A different surface from {@link TRAE_CHAT_PATH}, not a version of it: this
|
|
56
|
+
* one selects models by `config_name` + `config_source` and ignores
|
|
57
|
+
* `model_name` entirely, so the two rosters cannot be merged into one call.
|
|
58
|
+
*/
|
|
59
|
+
export const TRAE_AGENT_CHAT_PATH = "/api/agent/v3/llm_utils_chat";
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Resolves which roster the agent surface serves for an account.
|
|
63
|
+
*
|
|
64
|
+
* `solo_agent` holds the picker's chat-capable configs. Other values are
|
|
65
|
+
* either rejected (`solo_coder` is `4001 param is invalid`) or resolve to a
|
|
66
|
+
* different, non-chat set.
|
|
67
|
+
*/
|
|
68
|
+
export const TRAE_AGENT_FUNCTION = "solo_agent";
|
|
69
|
+
|
|
70
|
+
/** OAuth / business paths, relative to the realm's `account` host. */
|
|
71
|
+
export const TRAE_EXCHANGE_TOKEN_PATH = "/cloudide/api/v3/trae/oauth/ExchangeToken";
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Endpoints that redeem a PKCE `AuthCode`, tried in order.
|
|
75
|
+
*
|
|
76
|
+
* Distinct from {@link TRAE_EXCHANGE_TOKEN_PATH}, which is the authenticated
|
|
77
|
+
* refresh-token rotation and refuses an `AuthCode` with "Invalid param".
|
|
78
|
+
*
|
|
79
|
+
* The `/trae/api/v3` form is tried first because it is the one that answers:
|
|
80
|
+
* the `/cloudide/api/v3/public` form returns 500 for every input, including a
|
|
81
|
+
* garbage code, with an internal DNS failure (`cloudide.coreservice.apiserver`),
|
|
82
|
+
* so it is kept only as a fallback in case that is repaired.
|
|
83
|
+
*
|
|
84
|
+
* Sourced from the login page's own client (`TraeExchangeTokenV2` and
|
|
85
|
+
* `PublicExchangeToken` in `9839.*.js`).
|
|
86
|
+
*/
|
|
87
|
+
export const TRAE_AUTH_CODE_EXCHANGE_PATHS: readonly string[] = [
|
|
88
|
+
"/trae/api/v3/oauth/ExchangeToken",
|
|
89
|
+
"/cloudide/api/v3/public/oauth/ExchangeToken",
|
|
90
|
+
];
|
|
91
|
+
export const TRAE_TEMP_TOKEN_PATH = "/cloudide/api/v3/trae/GenerateTempToken";
|
|
92
|
+
export const TRAE_GET_USER_INFO_PATH = "/cloudide/api/v3/trae/GetUserInfo";
|
|
93
|
+
|
|
94
|
+
/** Browser-login path on the host `GetLoginGuidance` names. */
|
|
95
|
+
export const TRAE_AUTHORIZATION_PATH = "/authorization";
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Path the login page will accept as `auth_callback_url`.
|
|
99
|
+
*
|
|
100
|
+
* Verified against the live page: it renders the login form only when the
|
|
101
|
+
* callback path is `/authorize`. Any other path — `/callback`, `/auth/callback`,
|
|
102
|
+
* `/`, a trailing slash — is answered with "Login Failed" before the operator
|
|
103
|
+
* gets to sign in, so the gateway's own `/callback` default cannot be used.
|
|
104
|
+
*
|
|
105
|
+
* The donor serves this on its own dynamic port; here the port comes from the
|
|
106
|
+
* gateway's redirect setting and only the path is fixed.
|
|
107
|
+
*/
|
|
108
|
+
export const TRAE_CALLBACK_PATH = "/authorize";
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Login-guidance endpoints, tried in order.
|
|
112
|
+
*
|
|
113
|
+
* This is the one Trae OAuth step that is not per-realm: the guidance call is
|
|
114
|
+
* what *discovers* the login host, so asking a realm's own account host would
|
|
115
|
+
* presuppose the answer. Verified live: all three answer 200 and return
|
|
116
|
+
* `Result.LoginHost` (`www.trae.ai`).
|
|
117
|
+
*/
|
|
118
|
+
export const TRAE_LOGIN_GUIDANCE_URLS: readonly string[] = [
|
|
119
|
+
"https://api.marscode.com/cloudide/api/v3/trae/GetLoginGuidance",
|
|
120
|
+
"https://api.trae.ai/cloudide/api/v3/trae/GetLoginGuidance",
|
|
121
|
+
"https://www.trae.ai/cloudide/api/v3/trae/GetLoginGuidance",
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
/** Usage / entitlement paths, relative to the realm's `pay` host. */
|
|
125
|
+
export const TRAE_SESSION_USAGE_PATH = "/api/v1/commercial/get_session_usage";
|
|
126
|
+
export const TRAE_ACTIVITY_PATH = "/api/v1/commercial/get_user_activity";
|
|
127
|
+
export const TRAE_ENT_USAGE_PATH = "/trae/api/v1/pay/ide_user_ent_usage";
|
|
128
|
+
export const TRAE_PAY_STATUS_PATH = "/trae/api/v1/pay/ide_user_pay_status";
|
|
129
|
+
|
|
130
|
+
/** Growth paths, relative to the realm's `ug` host. CN-only upstream. */
|
|
131
|
+
export const TRAE_CHECKIN_STATUS_PATH = "/trae/api/v2/ug/checkin_credits/status";
|
|
132
|
+
export const TRAE_CHECKIN_CLAIM_PATH = "/trae/api/v2/ug/checkin_credits/claim";
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Realm → deployment.
|
|
136
|
+
*
|
|
137
|
+
* `intl` carries two core fallbacks and an account fallback; `cn` has exactly
|
|
138
|
+
* one host per role. That is upstream fact, not an oversight.
|
|
139
|
+
*
|
|
140
|
+
* `modelListSupported` is false for intl: upstream answers
|
|
141
|
+
* `403 no schema found` there, so discovery must not pretend a live fetch
|
|
142
|
+
* succeeded and must fall back to the static roster.
|
|
143
|
+
*/
|
|
144
|
+
export const TRAE_REALMS: Readonly<Record<TraeRealm, TraeRealmConfig>> = {
|
|
145
|
+
cn: {
|
|
146
|
+
// Resolved dynamically by the client. `api.trae.com.cn` only serves
|
|
147
|
+
// CloudIDE/OAuth/pay — `/api/ide/v1/*` there is a 404.
|
|
148
|
+
core: "https://trae-api-cn.mchost.guru",
|
|
149
|
+
coreFallbacks: [],
|
|
150
|
+
account: "https://api.trae.com.cn",
|
|
151
|
+
accountFallbacks: [],
|
|
152
|
+
pay: "https://api.trae.com.cn",
|
|
153
|
+
// The check-in routes are registered on `api.trae.cn` only; the same path
|
|
154
|
+
// on `api.trae.com.cn` is a 404.
|
|
155
|
+
ug: "https://api.trae.cn",
|
|
156
|
+
userAgent: "Trae CN/3.4.3",
|
|
157
|
+
versionCode: "20260212",
|
|
158
|
+
region: "CN",
|
|
159
|
+
modelListSupported: true,
|
|
160
|
+
},
|
|
161
|
+
intl: {
|
|
162
|
+
core: "https://core-normal.traeapi.us",
|
|
163
|
+
coreFallbacks: ["https://core-normal.trae.ai", "https://coresg-normal.trae.ai"],
|
|
164
|
+
account: "https://grow-normal.trae.ai",
|
|
165
|
+
accountFallbacks: ["https://grow-normal.traeapi.us"],
|
|
166
|
+
pay: "https://grow-normal.traeapi.us",
|
|
167
|
+
ug: "https://grow-normal.traeapi.us",
|
|
168
|
+
userAgent: "Trae/3.6.2",
|
|
169
|
+
versionCode: "20260212",
|
|
170
|
+
region: "US",
|
|
171
|
+
// Verified against an SG account: the region host answers `model_list`.
|
|
172
|
+
modelListSupported: true,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Data-plane host per account region, when the login reported one.
|
|
178
|
+
*
|
|
179
|
+
* An intl account is provisioned into a region and its token only authenticates
|
|
180
|
+
* against that region's core host. Verified: a `userRegion=sg` account sent to
|
|
181
|
+
* `core-normal.traeapi.us` answers `1001 "we are not able to authenticate you"`,
|
|
182
|
+
* while `coresg-normal.trae.ai` accepts the same token and streams normally.
|
|
183
|
+
* Without this, a correct login produced an account that could never dispatch.
|
|
184
|
+
*
|
|
185
|
+
* `store-country-code` follows the same value: the US host was rejecting the
|
|
186
|
+
* token *and* the roster it serves is a different catalog.
|
|
187
|
+
*/
|
|
188
|
+
export const TRAE_REGION_CORE_HOSTS: Readonly<Record<string, string>> = {
|
|
189
|
+
sg: "https://coresg-normal.trae.ai",
|
|
190
|
+
us: "https://core-normal.traeapi.us",
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/** The app id is constant across realms. */
|
|
194
|
+
export const TRAE_APP_ID = "6eefa01c-1036-4c7e-9ca5-d891f63bfcd8";
|
|
195
|
+
|
|
196
|
+
/** Plugin channel, constant across realms. */
|
|
197
|
+
export const TRAE_PLUGIN_CHANNEL = "icube-ai";
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Provider id → realm.
|
|
201
|
+
*
|
|
202
|
+
* CN is a separate provider id rather than a flag on `trae`, matching
|
|
203
|
+
* `qodercn`/`cbcn`: the two deployments share no host, no OAuth client, and no
|
|
204
|
+
* model roster, so a single provider could not describe either one.
|
|
205
|
+
*/
|
|
206
|
+
export function traeRealmForProvider(providerId: string): TraeRealm {
|
|
207
|
+
return providerId === "traecn" ? "cn" : "intl";
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Data-plane host candidates in failover order: primary first, then fallbacks. */
|
|
211
|
+
export function traeCoreCandidates(realm: TraeRealm): readonly string[] {
|
|
212
|
+
const config = TRAE_REALMS[realm];
|
|
213
|
+
return [config.core, ...config.coreFallbacks];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Business host candidates in failover order. */
|
|
217
|
+
export function traeAccountCandidates(realm: TraeRealm): readonly string[] {
|
|
218
|
+
const config = TRAE_REALMS[realm];
|
|
219
|
+
return [config.account, ...config.accountFallbacks];
|
|
220
|
+
}
|