cartethyia 2.4.5 → 2.5.0
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 +4 -1
- package/README.md +63 -13
- package/dist/dashboard/assets/AccountCooldown-ChpdzxRH.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-9igfctfZ.js +1 -0
- package/dist/dashboard/assets/BannedPage-9igfctfZ.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BzGevuql.js → CliToolDetailPage-ZRr2K2sF.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-ZRr2K2sF.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BaRM_7We.js → CliToolsPage-D3xqZbN-.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-D3xqZbN-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CVnQPBBz.js +1 -0
- package/dist/dashboard/assets/CombosPage-CVnQPBBz.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-DqUGcoH0.js → CustomizationPage-CTTpCb25.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-CTTpCb25.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-DbhTGfhb.js → LoginPage-DHoB3Agn.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DHoB3Agn.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js +1 -0
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.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-Df77V5I5.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js +7 -0
- package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-YxRml8AT.js → SettingsPage-CvjZxHE4.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-CvjZxHE4.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-D3nza7LH.js +1 -0
- package/dist/dashboard/assets/SetupPage-D3nza7LH.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-Bol1ASmI.js → SortableList-Dtt12WT1.js} +1 -1
- package/dist/dashboard/assets/SortableList-Dtt12WT1.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-CbK3hXvU.js +31 -0
- package/dist/dashboard/assets/StudioPage-CbK3hXvU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BQlWbWg2.js +56 -0
- package/dist/dashboard/assets/UsagePage-BQlWbWg2.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BmDbsior.js +1 -0
- 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/{app-BDJWMThu.js → app-C-Y4oZT0.js} +3 -3
- package/dist/dashboard/assets/app-C-Y4oZT0.js.gz +0 -0
- package/dist/dashboard/assets/arrow-left-CpBxkoxI.js.gz +0 -0
- package/dist/dashboard/assets/arrow-right-Dl5yKnp8.js.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/backup-file-gdKIlg8U.js.gz +0 -0
- package/dist/dashboard/assets/{badge-BwmwEOgA.js → badge-DpXT4Ixt.js} +1 -1
- package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BKBfKTqn.js → banner-CSAAWLJv.js} +1 -1
- package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
- package/dist/dashboard/assets/brain-Cwed_72z.js.gz +0 -0
- package/dist/dashboard/assets/{card-BTdbRFFg.js → card-DEhRXXw4.js} +1 -1
- package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
- package/dist/dashboard/assets/check-D69FzdvC.js.gz +0 -0
- package/dist/dashboard/assets/chevron-right-BXIy5l3h.js.gz +0 -0
- package/dist/dashboard/assets/circle-alert-CntYHV5v.js.gz +0 -0
- package/dist/dashboard/assets/circle-check-BU3v_mFW.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-B0wBq-RL.js +1 -0
- package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js +1 -0
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
- package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
- package/dist/dashboard/assets/copy-ngmQ9zcZ.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-iocKA3jr.js → dialog-BvQeBMfP.js} +1 -1
- package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
- package/dist/dashboard/assets/dialog-actions-B1axpEg7.js.gz +0 -0
- package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
- package/dist/dashboard/assets/download-dRFo5_Wu.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CDKOTZdY.js → drawer-CMwPXwQB.js} +1 -1
- package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
- package/dist/dashboard/assets/entry-D4O2229X.js +1 -0
- package/dist/dashboard/assets/entry-D4O2229X.js.gz +0 -0
- package/dist/dashboard/assets/entry-DKvr5Q9K.js +1 -0
- package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
- package/dist/dashboard/assets/entry-FQCejoDY.js +4 -0
- package/dist/dashboard/assets/entry-FQCejoDY.js.gz +0 -0
- package/dist/dashboard/assets/eye-C_hawfH4.js.gz +0 -0
- package/dist/dashboard/assets/eye-off-Bk5g2luF.js.gz +0 -0
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/gauge-BbIMy7MB.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-zliLvBwr.js +1 -0
- package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
- package/dist/dashboard/assets/image-BPaouViG.js.gz +0 -0
- package/dist/dashboard/assets/{index-C4hXfAMs.js → index-CXm3FIyt.js} +2 -2
- package/dist/dashboard/assets/index-CXm3FIyt.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DCejSLlA.js → inline-D1IlZZ8H.js} +1 -1
- package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
- package/dist/dashboard/assets/{input-Bn4_kNav.js → input-BpVZdrbc.js} +1 -1
- package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
- package/dist/dashboard/assets/jsx-runtime-CXUMngI_.js.gz +0 -0
- package/dist/dashboard/assets/key-round-UkavsGxp.js.gz +0 -0
- package/dist/dashboard/assets/layers-2-glBbjl.js.gz +0 -0
- package/dist/dashboard/assets/{layout-Bv-NxqZ5.js → layout-BioLW7gN.js} +1 -1
- package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BLA0sldD.js → list-row-CwVcGAIc.js} +1 -1
- package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
- package/dist/dashboard/assets/{live-YlifPYox.js → live-yfEJFF9T.js} +1 -1
- package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
- package/dist/dashboard/assets/log-in-BmRjy60c.js.gz +0 -0
- package/dist/dashboard/assets/message-circle-CCxGwRvv.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CT-TxSNX.js +1 -0
- package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
- package/dist/dashboard/assets/mutation-BkoLM5Ba.js +1 -0
- package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CFL4_Dqc.js.gz +0 -0
- package/dist/dashboard/assets/pencil-768ysRAr.js.gz +0 -0
- package/dist/dashboard/assets/plus-DIdX_g-x.js.gz +0 -0
- package/dist/dashboard/assets/power-off-Yjw9GMJh.js +1 -0
- package/dist/dashboard/assets/power-off-Yjw9GMJh.js.gz +0 -0
- package/dist/dashboard/assets/privacy-mode-GM8bej85.js +1 -0
- package/dist/dashboard/assets/privacy-mode-GM8bej85.js.gz +0 -0
- package/dist/dashboard/assets/providers/agentrouter.svg.gz +0 -0
- package/dist/dashboard/assets/providers/aihubmix.svg.gz +0 -0
- package/dist/dashboard/assets/providers/alibaba-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/providers/alibaba.svg.gz +0 -0
- package/dist/dashboard/assets/providers/antigravity.svg.gz +0 -0
- package/dist/dashboard/assets/providers/bai.svg.gz +0 -0
- package/dist/dashboard/assets/providers/blackbox.svg.gz +0 -0
- package/dist/dashboard/assets/providers/claude-code.svg.gz +0 -0
- package/dist/dashboard/assets/providers/cursor.webp +0 -0
- package/dist/dashboard/assets/providers/exa.svg.gz +0 -0
- package/dist/dashboard/assets/providers/gmi-cloud.svg.gz +0 -0
- package/dist/dashboard/assets/providers/inferhub.svg.gz +0 -0
- package/dist/dashboard/assets/providers/muse.svg.gz +0 -0
- package/dist/dashboard/assets/providers/nous-research.svg.gz +0 -0
- package/dist/dashboard/assets/providers/openai-light.svg.gz +0 -0
- package/dist/dashboard/assets/providers/parallel.svg.gz +0 -0
- package/dist/dashboard/assets/providers/tokenharbor.svg.gz +0 -0
- package/dist/dashboard/assets/providers/trae.webp +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/{query-keys-ROf-g1Wi.js → query-keys-D7wX2vM5.js} +1 -1
- package/dist/dashboard/assets/query-keys-D7wX2vM5.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/refresh-cw-BqJFk4sU.js.gz +0 -0
- package/dist/dashboard/assets/rotate-ccw-CufcqITi.js.gz +0 -0
- package/dist/dashboard/assets/route-CNSNj9Zm.js.gz +0 -0
- package/dist/dashboard/assets/routing-Cs6jl49L.js +1 -0
- package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
- package/dist/dashboard/assets/scaling-nWE4Cx8f.js.gz +0 -0
- package/dist/dashboard/assets/search-JOKus9rb.js.gz +0 -0
- package/dist/dashboard/assets/select-C0Rer3gF.js +41 -0
- package/dist/dashboard/assets/select-C0Rer3gF.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/shield-check-DVS0Foqh.js.gz +0 -0
- package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
- package/dist/dashboard/assets/{stack-C1kTcOmP.js → stack-C9_uUjaA.js} +1 -1
- package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
- package/dist/dashboard/assets/{state-DHKdg-pm.js → state-DA0B95UQ.js} +1 -1
- package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DIDeAiz9.js → switch-Cittxy-x.js} +1 -1
- package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
- package/dist/dashboard/assets/terminal-Bd4ee8TA.js +1 -0
- package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
- package/dist/dashboard/assets/toast-CNt_jzO4.js +2 -0
- package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
- package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
- package/dist/dashboard/assets/trash-Cppn5c3R.js.gz +0 -0
- package/dist/dashboard/assets/use-clipboard-B0Sy03fE.js.gz +0 -0
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
- package/dist/dashboard/assets/use-reduced-motion-C4B0R6Sz.js.gz +0 -0
- package/dist/dashboard/assets/use-timeout-BwcBBRrT.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-DVmwOKU-.js → useMutation-BQKMcJ1H.js} +1 -1
- package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
- package/dist/dashboard/assets/wrench-B6E1oEuY.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 +122 -0
- package/migrations/full-postgres/0013_add_providers_tenant_id_index.sql +22 -0
- package/migrations/full-postgres/0014_add_provider_accounts_tenant_id_index.sql +21 -0
- 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/0000_baseline.sql +4 -2
- package/migrations/lite-sqlite3/0013_add_providers_tenant_id_index.sql +4 -0
- package/migrations/lite-sqlite3/0014_add_provider_accounts_tenant_id_index.sql +4 -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 +26 -30
- package/scripts/cli/{carte-launcher.cjs → bin/carte.cjs} +8 -8
- package/scripts/cli/{cli-launcher.cjs → bin/cartethyia.cjs} +167 -134
- package/scripts/cli/command/admin.ts +65 -0
- package/scripts/cli/command/console-menu.ts +246 -0
- package/scripts/cli/{doctor.ts → command/doctor.ts} +257 -258
- package/scripts/cli/{setup.ts → command/setup.ts} +8 -12
- package/scripts/cli/command/start.ts +77 -0
- package/scripts/cli/command/tray.ts +165 -0
- package/scripts/cli/command/update.ts +23 -0
- package/scripts/cli/{runtime-state.ts → gateway/drain-token.ts} +78 -78
- package/scripts/cli/gateway/instance-lock.ts +105 -0
- package/scripts/cli/gateway/spawn.ts +173 -0
- package/scripts/cli/gateway/stop.ts +51 -0
- package/scripts/cli/main.ts +94 -0
- package/scripts/cli/support/bun.ts +27 -0
- package/scripts/cli/support/env.ts +99 -0
- package/scripts/cli/support/platform.ts +64 -0
- package/scripts/cli/support/services.ts +88 -0
- package/scripts/cli/support/startup-signal.ts +23 -0
- package/scripts/cli/{version.ts → support/version.ts} +1 -1
- package/scripts/cli/tray/hidden-window.vbs +28 -0
- package/scripts/cli/{tray.ts → tray/index.ts} +3 -3
- package/scripts/cli/{tray.ps1 → tray/windows.ps1} +107 -98
- package/scripts/cli/ui/background.ts +110 -0
- package/src/boot/app.ts +23 -7
- package/src/boot/dependencies.ts +55 -13
- 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 +31 -5
- package/src/config/runtime.ts +19 -3
- package/src/config/spec.ts +20 -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/backup/routes.ts +8 -0
- package/src/console/cli-tools/contracts.ts +6 -6
- package/src/console/console-router.ts +5 -3
- package/src/console/dashboard-assets.ts +81 -11
- package/src/console/domain-registration.ts +9 -2
- package/src/console/observability/contracts.ts +475 -423
- package/src/console/observability/routes.ts +31 -6
- package/src/console/observability/store.ts +1015 -912
- package/src/console/providers/catalog/contracts.ts +31 -0
- package/src/console/providers/catalog/provider-operations.ts +8 -0
- package/src/console/providers/catalog/public-model-store.ts +91 -60
- package/src/console/providers/catalog/store.ts +1532 -1521
- package/src/console/providers/oauth/callback-listener.ts +18 -3
- package/src/console/providers/oauth/routes.ts +38 -15
- package/src/console/quota/account-quota-tenant.ts +9 -6
- package/src/console/quota/refresh.ts +19 -0
- package/src/console/response-compression.ts +69 -2
- package/src/console/routing/pools/probe-verdict.ts +72 -0
- package/src/console/share/share-router.ts +7 -4
- package/src/console/share/share-stats.ts +12 -5
- package/src/console/shared/read-through-cache.ts +133 -7
- package/src/console/updates/repository-updates.ts +39 -34
- 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 +87 -24
- 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/event-log.ts +29 -8
- package/src/observability/metrics.ts +40 -13
- package/src/observability/payload-capture.ts +344 -324
- package/src/observability/runtime-metrics.ts +7 -0
- 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 -1257
- 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/codex.ts +9 -75
- package/src/protocol/response/omp-tool-call-markup.ts +221 -0
- package/src/protocol/tool-identity.ts +43 -9
- package/src/providers/authentication/jwt-validator.ts +130 -187
- package/src/providers/authentication/oauth-flow-store.ts +16 -9
- package/src/providers/default-registry.ts +177 -1
- package/src/providers/discovery/catalog/catalog-format.ts +20 -0
- package/src/providers/discovery/models-dev-catalog.ts +39 -22
- package/src/providers/discovery/openai-model-discovery.ts +39 -3
- package/src/providers/discovery/openrouter-free-roster.ts +98 -0
- package/src/providers/discovery/probe-wire.ts +29 -5
- package/src/providers/discovery/probing-service.ts +1022 -1001
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +42 -2
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +88 -0
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +22 -5
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +33 -1
- package/src/providers/integrations/buddy/buddy-identity.ts +46 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +37 -1
- package/src/providers/integrations/buddy/buddy-usage-store.ts +40 -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 +115 -73
- package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +5 -2
- package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
- package/src/providers/integrations/buddy/workbuddy/quota.ts +172 -159
- package/src/providers/integrations/buddy/workbuddy/shared.ts +200 -171
- 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/codex/codex.ts +5 -8
- 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/factory/factory-oauth.ts +7 -11
- 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/opencode-go-quota.ts +130 -0
- package/src/providers/integrations/opencode/provider.ts +532 -499
- 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 -263
- 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/shared/install-id.ts +29 -2
- 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 +143 -6
- package/src/providers/operations/model-catalog-cache.ts +18 -3
- package/src/providers/operations/provider-catalog-seeder.ts +95 -86
- package/src/providers/operations/provider-catalog-service.ts +76 -0
- 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 +39 -2
- package/src/transport/dispatch/attempt-loop.ts +530 -513
- package/src/transport/dispatch/empty-stream.ts +17 -26
- 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/failure-policy.ts +20 -0
- 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 -738
- package/src/transport/routing/route-model.ts +52 -1
- package/src/transport/routing/router.ts +51 -0
- package/src/transport/surface/chat/encode.ts +18 -31
- package/src/transport/surface/messages/stream.ts +3 -2
- package/src/transport/translation/tool-repair.ts +37 -16
- package/src/workers/account-identity-sweep.ts +14 -12
- package/src/workers/daily-checkin.ts +62 -0
- package/src/workers/quota-refresh-sweep.ts +27 -2
- package/dist/dashboard/assets/BannedPage-DUrXpp-X.js +0 -1
- package/dist/dashboard/assets/CombosPage-B7ETkfVH.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-AisQzCI1.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BNflDm_f.js +0 -1
- package/dist/dashboard/assets/ModelPicker-CAiPR5Vd.js +0 -1
- package/dist/dashboard/assets/OverviewPage-C3NIzsDx.js +0 -1
- package/dist/dashboard/assets/ProviderDetailPage-BK8i5B5X.js +0 -3
- package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-CvCVgm1e.js +0 -1
- package/dist/dashboard/assets/ProxyPage-DjabkuzP.js +0 -7
- package/dist/dashboard/assets/QuotaPage-Dxe0B0iu.js +0 -1
- package/dist/dashboard/assets/SetupPage-BaD4P_jW.js +0 -1
- package/dist/dashboard/assets/StudioPage-CLP4le3U.js +0 -31
- package/dist/dashboard/assets/UsagePage-Djes-Epy.js +0 -56
- package/dist/dashboard/assets/api-keys-CjIGQhe7.js +0 -1
- package/dist/dashboard/assets/app-Cjgo4-Lf.css +0 -2
- package/dist/dashboard/assets/assert-Bwh6DJTK.js +0 -1
- package/dist/dashboard/assets/cli-tools-DccaOfhX.js +0 -1
- package/dist/dashboard/assets/clipboard-button-DQmMikNQ.js +0 -1
- package/dist/dashboard/assets/entry-Cpi0rph1.js +0 -4
- package/dist/dashboard/assets/entry-Dm3PpDCO.js +0 -1
- package/dist/dashboard/assets/entry-keNDZ_Wz.js +0 -1
- package/dist/dashboard/assets/github-badge-BuAJr8oU.js +0 -1
- package/dist/dashboard/assets/model-bans-CVvHZ-qz.js +0 -1
- package/dist/dashboard/assets/mutation-BIizFZav.js +0 -1
- package/dist/dashboard/assets/privacy-mode-CQjbZ3xk.js +0 -1
- package/dist/dashboard/assets/routing-jntw9-Ui.js +0 -1
- package/dist/dashboard/assets/select-CGUuTQmj.js +0 -41
- package/dist/dashboard/assets/settings-BAS6QbmJ.js +0 -1
- package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
- package/dist/dashboard/assets/terminal-C9mZ4Ugm.js +0 -1
- package/dist/dashboard/assets/toast-DKAdP2mo.js +0 -2
- package/scripts/cli/cli.ts +0 -771
- package/scripts/operations/probe-egress.ts +0 -335
- package/scripts/operations/restart.ts +0 -120
- package/scripts/shared/env.ts +0 -269
- /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.ico +0 -0
- /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.png +0 -0
- /package/scripts/cli/{menu.ts → ui/prompt.ts} +0 -0
- /package/scripts/cli/{wordmark.ts → ui/wordmark.ts} +0 -0
|
@@ -25,11 +25,65 @@ async function qoderVersion(response: Response): Promise<string | null> {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Newest Trae IDE version from the official changelog.
|
|
30
|
+
*
|
|
31
|
+
* Trae publishes no npm package and no release API, so the changelog is the
|
|
32
|
+
* only machine-readable source of the current client version. It matters
|
|
33
|
+
* upstream: intl `llm_raw_chat` answers `4015` unless the request carries the
|
|
34
|
+
* *current* client version, so a stale pin is an outage, not a cosmetic drift.
|
|
35
|
+
*
|
|
36
|
+
* The page embeds its content as JSON, and its newest entry is the first dated
|
|
37
|
+
* heading — not the highest semver, because this vendor's hotfix line numbers
|
|
38
|
+
* past its feature line (3.5.104 shipped after 3.5.97). Taking `max(semver)`
|
|
39
|
+
* would report an old build as current, so this walks the document in order and
|
|
40
|
+
* returns the first version following a dated release heading.
|
|
41
|
+
*/
|
|
42
|
+
async function traeVersion(response: Response): Promise<string | null> {
|
|
43
|
+
try {
|
|
44
|
+
const text = await response.text();
|
|
45
|
+
const heading =
|
|
46
|
+
/"insert":"((?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2},\s*\d{4}\s*\([^)]*\))\\n"/g;
|
|
47
|
+
for (const match of text.matchAll(heading)) {
|
|
48
|
+
const window = text.slice(match.index ?? 0, (match.index ?? 0) + 900);
|
|
49
|
+
const found = window.match(/TraeCode\s+v?(\d+\.\d+\.\d+)|v?(\d+\.\d+\.\d+)\s+is\s+released/i);
|
|
50
|
+
const version = found?.[1] ?? found?.[2];
|
|
51
|
+
if (version && isSemverish(version)) return version.trim();
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
28
59
|
async function clineClientVersion(response: Response): Promise<string | null> {
|
|
29
60
|
const data = (await response.json()) as { version?: unknown };
|
|
30
61
|
return isSemverish(data.version) ? data.version.trim() : null;
|
|
31
62
|
}
|
|
32
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Newest Cursor build from the official download API, as the IDE's own header
|
|
66
|
+
* spells it.
|
|
67
|
+
*
|
|
68
|
+
* Cursor's `x-cursor-client-version` is not the IDE semver on its own: the wire
|
|
69
|
+
* value is `cli-<version>-<sha7>` (observed live as `cli-2026.10.19-6bbf2c4`).
|
|
70
|
+
* The download API is the only source that returns both halves — `version` and
|
|
71
|
+
* `commitSha` — so they are joined here rather than guessed. A semver alone
|
|
72
|
+
* would be a different identity than the one the IDE sends.
|
|
73
|
+
*/
|
|
74
|
+
async function cursorVersion(response: Response): Promise<string | null> {
|
|
75
|
+
try {
|
|
76
|
+
const data = (await response.json()) as { version?: unknown; commitSha?: unknown };
|
|
77
|
+
const version = data.version;
|
|
78
|
+
if (!isSemverish(version)) return null;
|
|
79
|
+
const sha = typeof data.commitSha === "string" ? data.commitSha.trim() : "";
|
|
80
|
+
const short = sha.length >= 7 ? sha.slice(0, 7) : "";
|
|
81
|
+
return short.length > 0 ? `cli-${version.trim()}-${short}` : version.trim();
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
33
87
|
async function clineSdkVersion(response: Response): Promise<string | null> {
|
|
34
88
|
const data = (await response.json()) as {
|
|
35
89
|
version?: unknown;
|
|
@@ -177,7 +231,7 @@ export const VERSION_SOURCES = {
|
|
|
177
231
|
},
|
|
178
232
|
qoder: {
|
|
179
233
|
key: "qoder",
|
|
180
|
-
fallback: "1.1.
|
|
234
|
+
fallback: "1.1.74",
|
|
181
235
|
sources: [
|
|
182
236
|
{
|
|
183
237
|
url: "https://registry.npmjs.org/@qoder-ai/qodercli/latest",
|
|
@@ -222,6 +276,11 @@ export const VERSION_SOURCES = {
|
|
|
222
276
|
fallback: "0.0.95",
|
|
223
277
|
sources: [{ url: "https://registry.npmjs.org/@cline/sdk/latest", extract: clineSdkVersion }],
|
|
224
278
|
},
|
|
279
|
+
clineCli: {
|
|
280
|
+
key: "cline-cli",
|
|
281
|
+
fallback: "3.0.73",
|
|
282
|
+
sources: [{ url: "https://registry.npmjs.org/cline/latest", extract: clineSdkVersion }],
|
|
283
|
+
},
|
|
225
284
|
codex: {
|
|
226
285
|
key: "codex",
|
|
227
286
|
fallback: "0.163.0",
|
|
@@ -257,7 +316,7 @@ export const VERSION_SOURCES = {
|
|
|
257
316
|
},
|
|
258
317
|
workbuddyCli: {
|
|
259
318
|
key: "workbuddy",
|
|
260
|
-
fallback: "2.
|
|
319
|
+
fallback: "2.169.0",
|
|
261
320
|
sources: [
|
|
262
321
|
{ url: "https://registry.npmjs.org/@tencent-ai/codebuddy-code/latest" },
|
|
263
322
|
{ url: "https://registry.npmmirror.com/@tencent-ai/codebuddy-code/latest" },
|
|
@@ -284,7 +343,7 @@ export const VERSION_SOURCES = {
|
|
|
284
343
|
},
|
|
285
344
|
codebuddy: {
|
|
286
345
|
key: "codebuddy",
|
|
287
|
-
fallback: "2.
|
|
346
|
+
fallback: "2.169.0",
|
|
288
347
|
sources: [
|
|
289
348
|
{ url: "https://registry.npmjs.org/@tencent-ai/codebuddy-code/latest" },
|
|
290
349
|
{ url: "https://registry.npmmirror.com/@tencent-ai/codebuddy-code/latest" },
|
|
@@ -342,6 +401,46 @@ export const VERSION_SOURCES = {
|
|
|
342
401
|
fallback: "1.4.10-R6427.1",
|
|
343
402
|
sources: [{ url: "https://api.meta.ai/muse-code/channels/muse-stable" }],
|
|
344
403
|
},
|
|
404
|
+
trae: {
|
|
405
|
+
key: "trae",
|
|
406
|
+
// Fallback = the donor's last observed client version. Discovery replaces
|
|
407
|
+
// it within one monitor tick; it is only what a cold process sends before
|
|
408
|
+
// the first successful fetch.
|
|
409
|
+
fallback: "3.6.2",
|
|
410
|
+
sources: [
|
|
411
|
+
{
|
|
412
|
+
url: "https://docs.trae.ai/ide/changelog?_lang=en",
|
|
413
|
+
extract: traeVersion,
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
cursor: {
|
|
418
|
+
key: "cursor",
|
|
419
|
+
// Fallback = the last observed wire version. Discovery replaces it within
|
|
420
|
+
// one monitor tick; it is only what a cold process sends before the first
|
|
421
|
+
// successful fetch.
|
|
422
|
+
fallback: "cli-2026.10.19-6bbf2c4",
|
|
423
|
+
sources: [
|
|
424
|
+
{
|
|
425
|
+
url: "https://cursor.com/api/download?platform=win32-x64-user&releaseTrack=stable",
|
|
426
|
+
extract: cursorVersion,
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
},
|
|
430
|
+
hermes: {
|
|
431
|
+
key: "hermes",
|
|
432
|
+
// Hermes DOES publish a semver: the GitHub release tag is `v0.22.2`. The
|
|
433
|
+
// comment this replaced claimed no semver existed and pinned a date tag
|
|
434
|
+
// (`v2026.9.24`) as the version — a tag, not a release, and not the
|
|
435
|
+
// identity the client sends.
|
|
436
|
+
fallback: "0.22.2",
|
|
437
|
+
sources: [
|
|
438
|
+
{
|
|
439
|
+
url: "https://api.github.com/repos/NousResearch/hermes-agent/releases/latest",
|
|
440
|
+
extract: githubReleaseVersion,
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
},
|
|
345
444
|
zcode: {
|
|
346
445
|
key: "zcode",
|
|
347
446
|
fallback: "3.14.3",
|
|
@@ -357,11 +456,15 @@ export const VERSION_SOURCES = {
|
|
|
357
456
|
const resolvers = {
|
|
358
457
|
antigravity: createClientVersionResolver({ ...VERSION_SOURCES.antigravity, client: "antigravity" }),
|
|
359
458
|
qoder: createClientVersionResolver({ ...VERSION_SOURCES.qoder, client: "qoder" }),
|
|
459
|
+
hermes: createClientVersionResolver({ ...VERSION_SOURCES.hermes, client: "hermes" }),
|
|
460
|
+
trae: createClientVersionResolver({ ...VERSION_SOURCES.trae, client: "trae" }),
|
|
461
|
+
cursor: createClientVersionResolver({ ...VERSION_SOURCES.cursor, client: "cursor" }),
|
|
360
462
|
opencode: createClientVersionResolver({ ...VERSION_SOURCES.opencode, client: "opencode" }),
|
|
361
463
|
commandcode: createClientVersionResolver({ ...VERSION_SOURCES.commandcode, client: "command-code" }),
|
|
362
464
|
grok: createClientVersionResolver({ ...VERSION_SOURCES.grok, client: "grok" }),
|
|
363
465
|
clineClient: createClientVersionResolver({ ...VERSION_SOURCES.clineClient, client: "cline" }),
|
|
364
466
|
clineSdk: createClientVersionResolver({ ...VERSION_SOURCES.clineSdk, client: "cline-sdk" }),
|
|
467
|
+
clineCli: createClientVersionResolver({ ...VERSION_SOURCES.clineCli, client: "cline-cli" }),
|
|
365
468
|
codex: createClientVersionResolver({ ...VERSION_SOURCES.codex, client: "codex" }),
|
|
366
469
|
workbuddyClient: createClientVersionResolver({ ...VERSION_SOURCES.workbuddyClient, client: "workbuddy" }),
|
|
367
470
|
workbuddyCnClient: createClientVersionResolver({ ...VERSION_SOURCES.workbuddyCnClient, client: "workbuddy-cn" }),
|
|
@@ -384,9 +487,16 @@ const PROVIDER_VERSION_RESOLVERS: Readonly<Record<string, ClientVersionResolver>
|
|
|
384
487
|
opencode: resolvers.opencode,
|
|
385
488
|
"opencode-zen": resolvers.opencode,
|
|
386
489
|
"opencode-go": resolvers.opencode,
|
|
387
|
-
|
|
490
|
+
// The identity this provider is dispatched under is the CLI's, so the route
|
|
491
|
+
// identity reports the CLI version. `clineClient` (the VS Code extension
|
|
492
|
+
// manifest) is no longer the client this provider claims to be.
|
|
493
|
+
cline: resolvers.clineCli,
|
|
388
494
|
codex: resolvers.codex,
|
|
389
495
|
qoder: resolvers.qoder,
|
|
496
|
+
hermes: resolvers.hermes,
|
|
497
|
+
trae: resolvers.trae,
|
|
498
|
+
traecn: resolvers.trae,
|
|
499
|
+
cursor: resolvers.cursor,
|
|
390
500
|
commandcode: resolvers.commandcode,
|
|
391
501
|
workbuddy: resolvers.workbuddyClient,
|
|
392
502
|
workbuddycn: resolvers.workbuddyCnClient,
|
|
@@ -484,6 +594,8 @@ function accessor<K extends keyof typeof resolvers>(name: K): {
|
|
|
484
594
|
}
|
|
485
595
|
|
|
486
596
|
export const getQoderVersion = accessor("qoder").get;
|
|
597
|
+
export const getHermesVersion = accessor("hermes").get;
|
|
598
|
+
export const getTraeVersion = accessor("trae").get;
|
|
487
599
|
export const getAntigravityVersion = accessor("antigravity").get;
|
|
488
600
|
|
|
489
601
|
export const getOpenCodeVersion = accessor("opencode").get;
|
|
@@ -503,8 +615,10 @@ export function buildGrokAuthUserAgent(version = getGrokVersion()): string {
|
|
|
503
615
|
|
|
504
616
|
export const getClineClientVersion = accessor("clineClient").get;
|
|
505
617
|
export const getClineSdkVersion = accessor("clineSdk").get;
|
|
618
|
+
export const getClineCliVersion = accessor("clineCli").get;
|
|
506
619
|
export const _resetClineClientVersionCache = accessor("clineClient").reset;
|
|
507
620
|
export const _resetClineSdkVersionCache = accessor("clineSdk").reset;
|
|
621
|
+
export const _resetClineCliVersionCache = accessor("clineCli").reset;
|
|
508
622
|
|
|
509
623
|
export const getCodexVersion = accessor("codex").get;
|
|
510
624
|
|
|
@@ -513,9 +627,16 @@ export const getWorkBuddyCnClientVersion = accessor("workbuddyCnClient").get;
|
|
|
513
627
|
export const getWorkBuddyCliVersion = accessor("workbuddyCli").get;
|
|
514
628
|
|
|
515
629
|
export function buildWorkBuddyUserAgent(
|
|
630
|
+
identity = "WB",
|
|
516
631
|
clientVersion = getWorkBuddyClientVersion(),
|
|
517
632
|
cliVersion = getWorkBuddyCliVersion(),
|
|
518
633
|
): string {
|
|
634
|
+
// 1:1 with codebuddy2api. The CLI profile is `CLI/<v> CodeBuddy/<v>` and the
|
|
635
|
+
// WorkBuddy profile is `WorkBuddy/<wb> WorkBuddy AI/<wb> CLI/<cli>`, which
|
|
636
|
+
// sends NO SDK headers — two separate profiles, not one string with a switch.
|
|
637
|
+
// Whether SDK headers ride along is decided by `workbuddyHeaders`, not here, so
|
|
638
|
+
// this builder only decides the UA text.
|
|
639
|
+
if (identity === "CLI") return `CLI/${cliVersion} CodeBuddy/${cliVersion}`;
|
|
519
640
|
return `WorkBuddy/${clientVersion} WorkBuddy AI/${clientVersion} CLI/${cliVersion}`;
|
|
520
641
|
}
|
|
521
642
|
|
|
@@ -525,11 +646,27 @@ export const getKimiCliVersion = accessor("kimiCli").get;
|
|
|
525
646
|
|
|
526
647
|
export const getCodeBuddyVersion = accessor("codebuddy").get;
|
|
527
648
|
export const getCodeBuddyCnVersion = accessor("codebuddyCn").get;
|
|
649
|
+
/**
|
|
650
|
+
* The CodeBuddy User-Agent.
|
|
651
|
+
*
|
|
652
|
+
* Both identities share the CLI *package* version, which is why `codebuddy`
|
|
653
|
+
* (the npm `@tencent-ai/codebuddy-code` pin, `2.169.0` — the value both
|
|
654
|
+
* references use for CLI) is the version for each. `codebuddyCn` is the CN
|
|
655
|
+
* *desktop* build (`4.12.2.…`), and using it here produced
|
|
656
|
+
* `CLI/4.12.2.41207131 CodeBuddy/4.12.2.41207131` — a CLI claiming a desktop
|
|
657
|
+
* version, which no official client emits.
|
|
658
|
+
*/
|
|
528
659
|
export function buildCodeBuddyUserAgent(
|
|
529
660
|
identity: "IDE" | "CLI",
|
|
530
|
-
version =
|
|
661
|
+
version = getCodeBuddyVersion(),
|
|
662
|
+
cnVersion = getCodeBuddyCnVersion(),
|
|
531
663
|
): string {
|
|
532
|
-
|
|
664
|
+
// 1:1 with codebuddy2api's identity_headers(): the CLI profile uses ONE version
|
|
665
|
+
// (the npm CLI package pin) for both halves, and the IDE profile uses the other
|
|
666
|
+
// product's version. This previously always used `version`, so "CLI" produced
|
|
667
|
+
// `CLI/<ide> CodeBuddy/<ide>` — a CLI claiming the desktop build.
|
|
668
|
+
const resolved = identity === "CLI" ? cnVersion : version;
|
|
669
|
+
return `${identity}/${resolved} CodeBuddy/${resolved}`;
|
|
533
670
|
}
|
|
534
671
|
|
|
535
672
|
export const _resetCodeBuddyVersionCache = accessor("codebuddy").reset;
|
|
@@ -14,10 +14,14 @@ import { cachedRead } from "../../console/shared/read-through-cache";
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Provider entries retained before the least-recently-used catalog is evicted.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
*
|
|
18
|
+
* Must clear the bundled provider count — there are **73** of them — because a
|
|
19
|
+
* cap below it thrashes: a catalog rebuild evicts a provider it is about to
|
|
20
|
+
* need again, so `bundledModelCatalog()` re-imports a module it already had.
|
|
21
|
+
* Sized above that count with room for BYOK rows. One entry is a model list, so
|
|
22
|
+
* the bound still matters; it just has to be the right one.
|
|
19
23
|
*/
|
|
20
|
-
export const MODEL_CATALOG_CACHE_MAX_ENTRIES =
|
|
24
|
+
export const MODEL_CATALOG_CACHE_MAX_ENTRIES = 128;
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
27
|
* Cross-instance tier lifetime. Static provider catalogs change only on a
|
|
@@ -82,3 +86,14 @@ export function getCachedModels(
|
|
|
82
86
|
export function resetModelCatalogCacheForTesting(): void {
|
|
83
87
|
catalogCache.clear();
|
|
84
88
|
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Live entry count, for the `/metrics` gauge.
|
|
92
|
+
*
|
|
93
|
+
* Exported so a rebuild that saturates the cap is visible from outside the
|
|
94
|
+
* process: before this, every in-process cache except the quota tier was
|
|
95
|
+
* invisible, and growth could only be found with a heap dump.
|
|
96
|
+
*/
|
|
97
|
+
export function modelCatalogCacheSize(): number {
|
|
98
|
+
return catalogCache.size;
|
|
99
|
+
}
|
|
@@ -1,86 +1,95 @@
|
|
|
1
|
-
/** Durable materialization of bundled provider model metadata. */
|
|
2
|
-
import { and, eq, sql } from "drizzle-orm";
|
|
3
|
-
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
4
|
-
import { models } from "../../persistence/schema";
|
|
5
|
-
import type { ModelDefinition } from "../provider-model-definition";
|
|
6
|
-
|
|
7
|
-
/** Persists compiled provider model metadata and reconciles drifted capability flags. */
|
|
8
|
-
export async function seedBundledModels(
|
|
9
|
-
db: CartethyiaDatabase,
|
|
10
|
-
builtinModels: ReadonlyMap<string, readonly ModelDefinition[]>,
|
|
11
|
-
): Promise<void> {
|
|
12
|
-
for (const [providerId, definitions] of builtinModels) {
|
|
13
|
-
if (definitions.length === 0) continue;
|
|
14
|
-
// Drop builtin rows the current catalog no longer declares, including rows
|
|
15
|
-
// whose model id is still present but whose endpoint path drifted (e.g. a
|
|
16
|
-
// provider that moved from `/chat/completions` to `/v2/chat/completions`).
|
|
17
|
-
// The unique key is (provider, model, endpoint), so a changed endpoint
|
|
18
|
-
// would otherwise insert a second row and leave the stale route behind —
|
|
19
|
-
// the model list would then show duplicates and probes could hit the dead
|
|
20
|
-
// path. Composite `NOT IN` keeps exactly the (model, endpoint) pairs the
|
|
21
|
-
// catalog owns.
|
|
22
|
-
const pairs = definitions.map(
|
|
23
|
-
(definition) => sql`(${definition.modelId}, ${definition.endpointPath})`,
|
|
24
|
-
);
|
|
25
|
-
await db
|
|
26
|
-
.delete(models)
|
|
27
|
-
.where(
|
|
28
|
-
and(
|
|
29
|
-
eq(models.providerId, providerId),
|
|
30
|
-
eq(models.source, "builtin"),
|
|
31
|
-
sql`(${models.modelId}, ${models.endpointPath}) NOT IN (${sql.join(pairs, sql`, `)})`,
|
|
32
|
-
),
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
const now = new Date();
|
|
36
|
-
const seen = new Set<string>();
|
|
37
|
-
const rows = [...builtinModels.entries()].flatMap(([providerId, definitions]) =>
|
|
38
|
-
definitions.flatMap((definition) => {
|
|
39
|
-
const key = `${providerId}\u0000${definition.modelId}\u0000${definition.endpointPath}`;
|
|
40
|
-
if (seen.has(key)) return [];
|
|
41
|
-
seen.add(key);
|
|
42
|
-
return [
|
|
43
|
-
{
|
|
44
|
-
providerId,
|
|
45
|
-
modelId: definition.modelId,
|
|
46
|
-
wireFamily: definition.wireFamily,
|
|
47
|
-
serviceKind: definition.serviceKind ?? "llm",
|
|
48
|
-
endpointPath: definition.endpointPath,
|
|
49
|
-
contextLimit: definition.contextLimit,
|
|
50
|
-
outputLimit: definition.outputLimit,
|
|
51
|
-
modalities: definition.modalities,
|
|
52
|
-
reasoning: definition.reasoning,
|
|
53
|
-
toolCall: definition.toolCall,
|
|
54
|
-
cost: definition.cost,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
1
|
+
/** Durable materialization of bundled provider model metadata. */
|
|
2
|
+
import { and, eq, sql } from "drizzle-orm";
|
|
3
|
+
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
4
|
+
import { models } from "../../persistence/schema";
|
|
5
|
+
import type { ModelDefinition } from "../provider-model-definition";
|
|
6
|
+
|
|
7
|
+
/** Persists compiled provider model metadata and reconciles drifted capability flags. */
|
|
8
|
+
export async function seedBundledModels(
|
|
9
|
+
db: CartethyiaDatabase,
|
|
10
|
+
builtinModels: ReadonlyMap<string, readonly ModelDefinition[]>,
|
|
11
|
+
): Promise<void> {
|
|
12
|
+
for (const [providerId, definitions] of builtinModels) {
|
|
13
|
+
if (definitions.length === 0) continue;
|
|
14
|
+
// Drop builtin rows the current catalog no longer declares, including rows
|
|
15
|
+
// whose model id is still present but whose endpoint path drifted (e.g. a
|
|
16
|
+
// provider that moved from `/chat/completions` to `/v2/chat/completions`).
|
|
17
|
+
// The unique key is (provider, model, endpoint), so a changed endpoint
|
|
18
|
+
// would otherwise insert a second row and leave the stale route behind —
|
|
19
|
+
// the model list would then show duplicates and probes could hit the dead
|
|
20
|
+
// path. Composite `NOT IN` keeps exactly the (model, endpoint) pairs the
|
|
21
|
+
// catalog owns.
|
|
22
|
+
const pairs = definitions.map(
|
|
23
|
+
(definition) => sql`(${definition.modelId}, ${definition.endpointPath})`,
|
|
24
|
+
);
|
|
25
|
+
await db
|
|
26
|
+
.delete(models)
|
|
27
|
+
.where(
|
|
28
|
+
and(
|
|
29
|
+
eq(models.providerId, providerId),
|
|
30
|
+
eq(models.source, "builtin"),
|
|
31
|
+
sql`(${models.modelId}, ${models.endpointPath}) NOT IN (${sql.join(pairs, sql`, `)})`,
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const now = new Date();
|
|
36
|
+
const seen = new Set<string>();
|
|
37
|
+
const rows = [...builtinModels.entries()].flatMap(([providerId, definitions]) =>
|
|
38
|
+
definitions.flatMap((definition) => {
|
|
39
|
+
const key = `${providerId}\u0000${definition.modelId}\u0000${definition.endpointPath}`;
|
|
40
|
+
if (seen.has(key)) return [];
|
|
41
|
+
seen.add(key);
|
|
42
|
+
return [
|
|
43
|
+
{
|
|
44
|
+
providerId,
|
|
45
|
+
modelId: definition.modelId,
|
|
46
|
+
wireFamily: definition.wireFamily,
|
|
47
|
+
serviceKind: definition.serviceKind ?? "llm",
|
|
48
|
+
endpointPath: definition.endpointPath,
|
|
49
|
+
contextLimit: definition.contextLimit,
|
|
50
|
+
outputLimit: definition.outputLimit,
|
|
51
|
+
modalities: definition.modalities,
|
|
52
|
+
reasoning: definition.reasoning,
|
|
53
|
+
toolCall: definition.toolCall,
|
|
54
|
+
cost: definition.cost,
|
|
55
|
+
// A static row either knows it is refused on a free plan or says
|
|
56
|
+
// nothing. Never coerced to false: the column is three-valued, and
|
|
57
|
+
// "nobody established it" is not the same claim as "served on free".
|
|
58
|
+
proOnly: definition.proOnly ?? null,
|
|
59
|
+
// Same three-valued rule for the inverse: the provider's directory
|
|
60
|
+
// states the tier, so the flag is carried rather than derived.
|
|
61
|
+
freeTier: definition.freeTier ?? null,
|
|
62
|
+
source: "builtin",
|
|
63
|
+
sourceUpdatedAt: now,
|
|
64
|
+
enabled: true,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
if (rows.length === 0) return;
|
|
70
|
+
// Reconcile static metadata on conflict rather than skipping: capability
|
|
71
|
+
// flags (toolCall/reasoning/webSearch), wire family, limits, and cost can
|
|
72
|
+
// drift on a pre-existing row (e.g. a discovery seeded `tool_call=false`
|
|
73
|
+
// before the static catalog was corrected). `source` is forced back to
|
|
74
|
+
// `builtin`: a fetch upsert may have flipped a builtin row to `discovered`,
|
|
75
|
+
// and the static catalog owns these ids. `enabled` is operator-owned and
|
|
76
|
+
// must never be reset here.
|
|
77
|
+
await db.insert(models).values(rows).onConflictDoUpdate({
|
|
78
|
+
target: [models.providerId, models.modelId, models.endpointPath],
|
|
79
|
+
set: {
|
|
80
|
+
wireFamily: sql`excluded.wire_family`,
|
|
81
|
+
serviceKind: sql`excluded.service_kind`,
|
|
82
|
+
endpointPath: sql`excluded.endpoint_path`,
|
|
83
|
+
contextLimit: sql`excluded.context_limit`,
|
|
84
|
+
outputLimit: sql`excluded.output_limit`,
|
|
85
|
+
modalities: sql`excluded.modalities`,
|
|
86
|
+
reasoning: sql`excluded.reasoning`,
|
|
87
|
+
toolCall: sql`excluded.tool_call`,
|
|
88
|
+
cost: sql`excluded.cost`,
|
|
89
|
+
proOnly: sql`excluded.pro_only`,
|
|
90
|
+
freeTier: sql`excluded.free_tier`,
|
|
91
|
+
source: sql`'builtin'`,
|
|
92
|
+
sourceUpdatedAt: sql`excluded.source_updated_at`,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -20,6 +20,57 @@ import { GATEWAY_USER_AGENT } from "./gateway-user-agent";
|
|
|
20
20
|
import { isIP } from "node:net";
|
|
21
21
|
import { isAddressAllowed } from "../../network/ssrf";
|
|
22
22
|
import type { SsrfPolicy } from "../../config/network";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Revision of the compiled provider catalog, and the last one materialized.
|
|
26
|
+
*
|
|
27
|
+
* The snapshot builder re-seeds on **every** rebuild, and a re-seed is a
|
|
28
|
+
* `DELETE` per provider plus a full `INSERT … ON CONFLICT DO UPDATE` of the
|
|
29
|
+
* compiled catalog. That is correct work when the catalog changed and pure
|
|
30
|
+
* waste when it did not — and the invalidation that triggers it is a timer
|
|
31
|
+
* (`account-health-sweep`), so the waste lands on a proxy request.
|
|
32
|
+
*
|
|
33
|
+
* Only the paths that can actually change the compiled catalog bump this: a
|
|
34
|
+
* provider registered, edited, or retired. A cooldown recovery changes account
|
|
35
|
+
* health, not the catalog, so it must not.
|
|
36
|
+
*/
|
|
37
|
+
let catalogRevision = 0;
|
|
38
|
+
let lastSeededCatalogRevision = -1;
|
|
39
|
+
|
|
40
|
+
/** Current catalog revision. */
|
|
41
|
+
export function currentCatalogRevision(): number {
|
|
42
|
+
return catalogRevision;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Bumps the revision after a change to the compiled catalog. */
|
|
46
|
+
export function bumpCatalogRevision(): number {
|
|
47
|
+
catalogRevision += 1;
|
|
48
|
+
return catalogRevision;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Forces the next re-seed to run even when the revision is unchanged.
|
|
53
|
+
*
|
|
54
|
+
* For the boot path and for tests: the compiled catalog has to be materialized
|
|
55
|
+
* once for the current process regardless of what an earlier call already
|
|
56
|
+
* applied, because a fresh database or a fresh registry is not a revision
|
|
57
|
+
* change.
|
|
58
|
+
*/
|
|
59
|
+
export function invalidateCatalogSeed(): void {
|
|
60
|
+
lastSeededCatalogRevision = -1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Records that the caller has just materialized the current catalog itself, so
|
|
65
|
+
* the next `refreshActiveProviderCatalogs` is a no-op.
|
|
66
|
+
*
|
|
67
|
+
* The boot path seeds directly rather than through the gate — it must always
|
|
68
|
+
* run, against a database and a registry that are both fresh — and without this
|
|
69
|
+
* the first snapshot build would pay for a second identical seed.
|
|
70
|
+
*/
|
|
71
|
+
export function markCatalogSeeded(): void {
|
|
72
|
+
lastSeededCatalogRevision = catalogRevision;
|
|
73
|
+
}
|
|
23
74
|
/**
|
|
24
75
|
* Idempotently materializes every shipped provider in the database catalog.
|
|
25
76
|
* The canonical provider definition supplies identity and routing defaults;
|
|
@@ -60,6 +111,9 @@ export async function seedBundledProviders(db: CartethyiaDatabase): Promise<void
|
|
|
60
111
|
),
|
|
61
112
|
},
|
|
62
113
|
});
|
|
114
|
+
// The compiled catalog is derived from the registry, and this is the path
|
|
115
|
+
// that reconciles the bundle's own rows into it.
|
|
116
|
+
bumpCatalogRevision();
|
|
63
117
|
}
|
|
64
118
|
|
|
65
119
|
/**
|
|
@@ -94,6 +148,9 @@ export async function retireUnbundledProviders(db: CartethyiaDatabase): Promise<
|
|
|
94
148
|
.delete(providers)
|
|
95
149
|
.where(and(isNull(providers.tenantId), notInArray(providers.id, bundledIds)))
|
|
96
150
|
.returning({ id: providers.id });
|
|
151
|
+
// Deleting a provider removes it from the compiled catalog, so the next
|
|
152
|
+
// re-seed must run even though no revision bumped on this path otherwise.
|
|
153
|
+
if (retired.length > 0) bumpCatalogRevision();
|
|
97
154
|
return retired.length;
|
|
98
155
|
}
|
|
99
156
|
|
|
@@ -172,13 +229,22 @@ export async function bundledModelCatalog(
|
|
|
172
229
|
* boot. Seeding is no longer account-gated, so this is now simply the same walk
|
|
173
230
|
* as at boot; it exists because an account makes the rows routable and the
|
|
174
231
|
* snapshot must reflect that without waiting for a restart.
|
|
232
|
+
*
|
|
233
|
+
* Gated on {@link currentCatalogRevision}: this is the seeder the snapshot
|
|
234
|
+
* builder calls on every rebuild, and the invalidation that triggers it is a
|
|
235
|
+
* 30 s timer, so an ungated re-seed could be pushed onto the next proxy
|
|
236
|
+
* request for a catalog that had not changed. The paths that do change the
|
|
237
|
+
* catalog bump the revision; everything else is a no-op here.
|
|
175
238
|
*/
|
|
176
239
|
export async function refreshActiveProviderCatalogs(
|
|
177
240
|
db: CartethyiaDatabase,
|
|
178
241
|
registry: ProviderRegistry,
|
|
179
242
|
): Promise<void> {
|
|
243
|
+
const revision = currentCatalogRevision();
|
|
244
|
+
if (revision === lastSeededCatalogRevision) return;
|
|
180
245
|
const catalog = await bundledModelCatalog(registry);
|
|
181
246
|
await seedBundledModels(db, catalog.modelsByProvider);
|
|
247
|
+
lastSeededCatalogRevision = revision;
|
|
182
248
|
}
|
|
183
249
|
|
|
184
250
|
|
|
@@ -217,6 +283,7 @@ export async function registerByokProviders(
|
|
|
217
283
|
.from(providers)
|
|
218
284
|
.where(and(isNotNull(providers.baseUrl), eq(providers.enabled, true)));
|
|
219
285
|
const hosts = new Map<string, ByokUpstreamHost>();
|
|
286
|
+
let registered = false;
|
|
220
287
|
for (const row of rows) {
|
|
221
288
|
if (!row.baseUrl) continue;
|
|
222
289
|
const baseUrl = row.baseUrl;
|
|
@@ -307,7 +374,13 @@ export async function registerByokProviders(
|
|
|
307
374
|
}),
|
|
308
375
|
});
|
|
309
376
|
byokRegistrationFingerprints.set(providerId, fingerprint);
|
|
377
|
+
registered = true;
|
|
310
378
|
}
|
|
379
|
+
// A changed BYOK registration changes what the compiled catalog contains, so
|
|
380
|
+
// the next re-seed must run. An unchanged one deliberately does not: this
|
|
381
|
+
// function is called on every console provider mutation, most of which do not
|
|
382
|
+
// alter the registration at all.
|
|
383
|
+
if (registered) bumpCatalogRevision();
|
|
311
384
|
return hosts;
|
|
312
385
|
}
|
|
313
386
|
|
|
@@ -341,6 +414,9 @@ export async function syncByokProvider(
|
|
|
341
414
|
if (host === undefined && fingerprintBefore !== undefined) {
|
|
342
415
|
registry.unregister(normalized);
|
|
343
416
|
byokRegistrationFingerprints.delete(normalized);
|
|
417
|
+
// The provider left the registry, so the compiled catalog no longer
|
|
418
|
+
// contains it: the next re-seed must run to prune its rows.
|
|
419
|
+
bumpCatalogRevision();
|
|
344
420
|
}
|
|
345
421
|
return host;
|
|
346
422
|
}
|