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
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { PACKAGE_ROOT, resolvePackagePath } from "../../../src/config/package-paths";
|
|
4
|
+
|
|
5
|
+
/** Opens a URL in the operator's default browser, ignoring a missing handler. */
|
|
6
|
+
export function openUrl(url: string): Promise<void> {
|
|
7
|
+
const command = process.platform === "win32"
|
|
8
|
+
? "explorer.exe"
|
|
9
|
+
: process.platform === "darwin"
|
|
10
|
+
? "open"
|
|
11
|
+
: "xdg-open";
|
|
12
|
+
const child = Bun.spawn([command, url], {
|
|
13
|
+
stdin: "ignore",
|
|
14
|
+
stdout: "ignore",
|
|
15
|
+
stderr: "ignore",
|
|
16
|
+
});
|
|
17
|
+
return child.exited.then(() => undefined);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Starts a detached process and resolves once it reports readiness.
|
|
22
|
+
*
|
|
23
|
+
* The child owns no stdio, so a signal file is the only channel that survives
|
|
24
|
+
* the parent. Without it the caller prints "running in the background" for a
|
|
25
|
+
* child that may already have died — which is exactly what a port clash looks
|
|
26
|
+
* like, because the child's own startup probe fails while the parent's gateway
|
|
27
|
+
* still holds the port.
|
|
28
|
+
*
|
|
29
|
+
* Resolves with the URL the child reported. Rejects with the child's message
|
|
30
|
+
* when it reports failure, or with a timeout if it never reports at all.
|
|
31
|
+
*/
|
|
32
|
+
export function launchBackgroundWithStatus(
|
|
33
|
+
args: readonly string[],
|
|
34
|
+
options: { readonly signalPath: string; readonly timeoutMs?: number } = { signalPath: "" },
|
|
35
|
+
): Promise<string> {
|
|
36
|
+
return new Promise<string>((resolve, reject) => {
|
|
37
|
+
const signalPath = options.signalPath;
|
|
38
|
+
const deadline = Date.now() + (options.timeoutMs ?? 60_000);
|
|
39
|
+
let settled = false;
|
|
40
|
+
const finish = (error: Error | null, url?: string): void => {
|
|
41
|
+
if (settled) return;
|
|
42
|
+
settled = true;
|
|
43
|
+
clearInterval(timer);
|
|
44
|
+
try {
|
|
45
|
+
unlinkSync(signalPath);
|
|
46
|
+
} catch {}
|
|
47
|
+
if (error) reject(error);
|
|
48
|
+
else resolve(url ?? "");
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const env = { ...process.env, CARTETHYIA_TRAY_STARTUP_SIGNAL: signalPath };
|
|
52
|
+
spawnBackground(args, env);
|
|
53
|
+
|
|
54
|
+
const timer = setInterval(() => {
|
|
55
|
+
if (settled) return;
|
|
56
|
+
let raw: string;
|
|
57
|
+
try {
|
|
58
|
+
raw = readFileSync(signalPath, "utf8");
|
|
59
|
+
} catch {
|
|
60
|
+
if (Date.now() >= deadline) finish(new Error("the background process did not report startup within the deadline"));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const signal = JSON.parse(raw) as { status?: string; url?: string; message?: string };
|
|
65
|
+
if (signal.status === "ready" && typeof signal.url === "string") finish(null, signal.url);
|
|
66
|
+
else if (signal.status === "failed") finish(new Error(signal.message ?? "the background process failed to start"));
|
|
67
|
+
} catch {
|
|
68
|
+
// A partially written file is read again on the next tick.
|
|
69
|
+
}
|
|
70
|
+
}, 150);
|
|
71
|
+
timer.unref?.();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Starts a Bun process that owns no console window.
|
|
77
|
+
*
|
|
78
|
+
* On Windows, spawning the child directly is not enough: a console-subsystem
|
|
79
|
+
* child of a window-less parent allocates a new console, and when the parent is
|
|
80
|
+
* inside Windows Terminal the tab is already on screen before any flag can take
|
|
81
|
+
* effect — measured: `detached: true` plus `windowsHide: true` still produced a
|
|
82
|
+
* Windows Terminal window titled with the bun path. `wscript.exe` is a
|
|
83
|
+
* GUI-subsystem host, so it never allocates a console, and a child launched
|
|
84
|
+
* from it with window style 0 inherits that. The VBS is that launcher.
|
|
85
|
+
*
|
|
86
|
+
* Elsewhere `detached` is enough and the helper is not used.
|
|
87
|
+
*/
|
|
88
|
+
export function launchBackground(args: readonly string[]): void {
|
|
89
|
+
spawnBackground(args, process.env);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function spawnBackground(args: readonly string[], env: NodeJS.ProcessEnv): void {
|
|
93
|
+
if (process.platform !== "win32") {
|
|
94
|
+
const child = spawn(process.execPath, [...args], {
|
|
95
|
+
cwd: PACKAGE_ROOT,
|
|
96
|
+
detached: true,
|
|
97
|
+
stdio: "ignore",
|
|
98
|
+
env,
|
|
99
|
+
});
|
|
100
|
+
child.unref();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const launcher = resolvePackagePath("scripts", "cli", "tray", "hidden-window.vbs");
|
|
104
|
+
const child = spawn(
|
|
105
|
+
"wscript.exe",
|
|
106
|
+
["//nologo", launcher, process.execPath, ...args],
|
|
107
|
+
{ cwd: PACKAGE_ROOT, detached: true, stdio: "ignore", windowsHide: true, env },
|
|
108
|
+
);
|
|
109
|
+
child.unref();
|
|
110
|
+
}
|
package/src/boot/app.ts
CHANGED
|
@@ -34,14 +34,21 @@ import {
|
|
|
34
34
|
import { createResponsesCompactHandler } from "../transport/dispatch/responses-compact";
|
|
35
35
|
import { createSystemoneHandler } from "../transport/dispatch/systemone";
|
|
36
36
|
import { createWebsearchHandler } from "../transport/dispatch/websearch";
|
|
37
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
createAudioSpeechHandler,
|
|
39
|
+
createAudioTranscriptionHandler,
|
|
40
|
+
createEmbeddingHandler,
|
|
41
|
+
createImageGenerationHandler,
|
|
42
|
+
createVideoGenerationHandler,
|
|
43
|
+
} from "../transport/dispatch/generation";
|
|
38
44
|
import { createTokenCountHandler } from "../transport/dispatch/token-count";
|
|
39
45
|
|
|
40
46
|
import { createTransportPipeline } from "../transport/guards/pipeline";
|
|
41
47
|
import { createDrainHandler, isLoopbackPeer, tokenMatches } from "../transport/drain-endpoint";
|
|
42
48
|
import { createConsoleAccountResetHandler } from "../console/auth/account-reset";
|
|
49
|
+
import { resetConsoleAuthState } from "../console/auth/account-reset-store";
|
|
43
50
|
import { createBackupExportHandler } from "../console/backup/export-endpoint";
|
|
44
|
-
import {
|
|
51
|
+
import { tenants } from "../persistence/schema";
|
|
45
52
|
import { PublicModelCatalogStore, type AllowedModelEntry } from "../console/providers/catalog/public-model-store";
|
|
46
53
|
import type { IpAbuseProtectionService } from "../security/abuse";
|
|
47
54
|
import type { ReadinessCheckResult } from "../persistence/readiness";
|
|
@@ -196,7 +203,9 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
196
203
|
const peerAddresses = new WeakMap<Request, string>();
|
|
197
204
|
const serveDashboard = async ({ request }: { request: Request }): Promise<Response> => {
|
|
198
205
|
const pathname = fastPathname(request.url);
|
|
199
|
-
|
|
206
|
+
// The client's `accept-encoding` is what decides whether a precompressed
|
|
207
|
+
// `.gz` sibling can be served instead of the plain asset.
|
|
208
|
+
const result = await staticHandler(pathname, request.headers.get("accept-encoding"));
|
|
200
209
|
return new Response(result.body ? (result.body as unknown as BodyInit) : null, {
|
|
201
210
|
status: result.status,
|
|
202
211
|
headers: result.headers,
|
|
@@ -512,6 +521,13 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
512
521
|
// Mounted with no model behind it on purpose: the surface exists so it can
|
|
513
522
|
// be finished when an upstream is known, rather than 404ing like a typo.
|
|
514
523
|
const handleVideoGeneration = createVideoGenerationHandler(generationDeps);
|
|
524
|
+
// Passthrough surfaces: routed and accounted for, but never interpreted.
|
|
525
|
+
// No bundled adapter implements embeddings or audio, and inventing a wire
|
|
526
|
+
// for them would be a guess; what these need to be useful is the route, the
|
|
527
|
+
// pipeline, and a compatible upstream behind it.
|
|
528
|
+
const handleEmbedding = createEmbeddingHandler(generationDeps);
|
|
529
|
+
const handleAudioTranscription = createAudioTranscriptionHandler(generationDeps);
|
|
530
|
+
const handleAudioSpeech = createAudioSpeechHandler(generationDeps);
|
|
515
531
|
// Shares the generation dependency bundle: same pipeline, and a count is
|
|
516
532
|
// neither chat-shaped nor generation-shaped, so it needs exactly the same
|
|
517
533
|
// routing, leasing, and accounting surface.
|
|
@@ -533,6 +549,9 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
533
549
|
routes.post("/search", handleWebsearch);
|
|
534
550
|
routes.post("/images/generations", handleImageGeneration);
|
|
535
551
|
routes.post("/videos/generations", handleVideoGeneration);
|
|
552
|
+
routes.post("/embeddings", handleEmbedding);
|
|
553
|
+
routes.post("/audio/transcriptions", handleAudioTranscription);
|
|
554
|
+
routes.post("/audio/speech", handleAudioSpeech);
|
|
536
555
|
routes.get("/models", handleModelsList);
|
|
537
556
|
routes.get("/models/info", handleModelsDetail);
|
|
538
557
|
routes.get("/models/*", handleModelsDetail);
|
|
@@ -580,10 +599,7 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
580
599
|
createConsoleAccountResetHandler({
|
|
581
600
|
token: deps.drainToken,
|
|
582
601
|
resolvePeerAddress: (request) => peerAddresses.get(request) ?? null,
|
|
583
|
-
resetConsoleAccount:
|
|
584
|
-
const users = await deps.db.delete(consoleUsers);
|
|
585
|
-
return users.rowCount ?? 0;
|
|
586
|
-
},
|
|
602
|
+
resetConsoleAccount: () => resetConsoleAuthState(deps.db),
|
|
587
603
|
}),
|
|
588
604
|
);
|
|
589
605
|
}
|
package/src/boot/dependencies.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { RedisClient } from "../persistence/redis";
|
|
|
7
7
|
import {
|
|
8
8
|
bundledModelCatalog,
|
|
9
9
|
liveProviderUpstreamHosts,
|
|
10
|
+
markCatalogSeeded,
|
|
10
11
|
refreshActiveProviderCatalogs,
|
|
11
12
|
registerByokProviders,
|
|
12
13
|
retireUnbundledProviders,
|
|
@@ -31,6 +32,9 @@ import { RedisAdmissionCounterStore } from "../security/admission/redis-store";
|
|
|
31
32
|
import { sweepLeases } from "../security/admission/lease-sweep";
|
|
32
33
|
import { DrizzleApiKeyStore } from "../persistence/stores/api-key-store";
|
|
33
34
|
import { UsageFlushBuffer } from "../persistence/usage-flush";
|
|
35
|
+
import { setPendingUsageSource } from "../security/api-key-auth";
|
|
36
|
+
import { HealthEventFlushBuffer, setHealthEventBuffer } from "../persistence/health-event-flush";
|
|
37
|
+
import { subscribePeerSettingsRevisions } from "../persistence/settings-revision";
|
|
34
38
|
import { deleteExpiredSessions } from "../console/auth/session-service";
|
|
35
39
|
import { InMemoryIpAbuseStore, IpAbuseProtectionService, RedisIpAbuseStore } from "../security/abuse";
|
|
36
40
|
import {
|
|
@@ -61,6 +65,7 @@ import {
|
|
|
61
65
|
} from "../workers/buddy-growth-tasks";
|
|
62
66
|
import { createAccountSecretResolver } from "../providers/operations/provider-credential-service";
|
|
63
67
|
import { quotaCacheSize } from "../console/quota/cache";
|
|
68
|
+
import { modelCatalogCacheSize } from "../providers/operations/model-catalog-cache";
|
|
64
69
|
import { preferencesReaderFor } from "../persistence/tenant-preferences";
|
|
65
70
|
import { sweepExpiredCooldowns } from "../providers/operations/account-health-service";
|
|
66
71
|
import { DrizzleTelemetryStore } from "../persistence/stores/telemetry-store";
|
|
@@ -109,6 +114,7 @@ export interface ProductionDeps {
|
|
|
109
114
|
poolSelector: NetworkPoolSelector;
|
|
110
115
|
telemetryBuffer: TelemetryBatchBuffer;
|
|
111
116
|
usageFlush: UsageFlushBuffer;
|
|
117
|
+
healthEventFlush: HealthEventFlushBuffer;
|
|
112
118
|
trustedProxyBoundary: TrustedProxyBoundary;
|
|
113
119
|
providerRegistry: ProviderRegistry;
|
|
114
120
|
resolveOAuthRefresher: (providerId: string) => Promise<OAuthTokenRefresher | undefined>;
|
|
@@ -130,7 +136,9 @@ const OAUTH_REFRESH_INTER_ITEM_DELAY_MS = 1_500;
|
|
|
130
136
|
* Client identities are refreshed out of band so provider dispatch never waits
|
|
131
137
|
* on npm/provider version endpoints. Resolver TTLs deduplicate the sources.
|
|
132
138
|
*/
|
|
133
|
-
|
|
139
|
+
// Matched to the resolver's own 30 min cache TTL: at 15 min every second pass
|
|
140
|
+
// was a pure cache read that could not discover anything new.
|
|
141
|
+
const CLIENT_VERSION_MONITOR_INTERVAL_MS = 30 * 60_000;
|
|
134
142
|
const CLIENT_VERSION_INITIAL_DELAY_MS = 10_000;
|
|
135
143
|
|
|
136
144
|
export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
@@ -166,6 +174,9 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
166
174
|
await measureStartupPhase("bundled-model-seed", () =>
|
|
167
175
|
seedBundledModels(db, bundledCatalog.modelsByProvider),
|
|
168
176
|
);
|
|
177
|
+
// The boot seed above is unconditional, so record it: the snapshot builder's
|
|
178
|
+
// first rebuild would otherwise re-seed an identical catalog.
|
|
179
|
+
markCatalogSeeded();
|
|
169
180
|
const snapshotService = new InMemoryRouteSnapshotService(
|
|
170
181
|
createDatabaseSnapshotBuilder(db, () => refreshActiveProviderCatalogs(db, registry)),
|
|
171
182
|
);
|
|
@@ -197,7 +208,21 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
197
208
|
}
|
|
198
209
|
return [...totals].map(([accountId, inflight]) => ({ accountId, inflight }));
|
|
199
210
|
};
|
|
211
|
+
// Peer processes' settings mutations arrive here. Subscribed once for the
|
|
212
|
+
// process: the revision counter is process-global, and the handler only
|
|
213
|
+
// moves it forward.
|
|
214
|
+
subscribePeerSettingsRevisions();
|
|
200
215
|
const usageFlush = new UsageFlushBuffer(db);
|
|
216
|
+
// Auth reads lifetime usage to seed the admission counter. The persisted
|
|
217
|
+
// value lags by up to one flush interval, so hand it the in-flight deltas
|
|
218
|
+
// too — otherwise the seed is systematically under what has actually been
|
|
219
|
+
// spent, worst exactly when the key is busiest.
|
|
220
|
+
setPendingUsageSource((apiKeyId) => usageFlush.pendingFor(apiKeyId));
|
|
221
|
+
// Health audit rows are append-only and read by nobody in dispatch, so they
|
|
222
|
+
// no longer land inside the request. The status write they accompany stays
|
|
223
|
+
// synchronous — routing reads it.
|
|
224
|
+
const healthEventFlush = new HealthEventFlushBuffer(db);
|
|
225
|
+
setHealthEventBuffer(healthEventFlush);
|
|
201
226
|
const admissionStore = redis
|
|
202
227
|
? new RedisAdmissionCounterStore(redis)
|
|
203
228
|
: new InMemoryAdmissionCounterStore();
|
|
@@ -280,7 +305,11 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
280
305
|
});
|
|
281
306
|
scheduledTasks.register({
|
|
282
307
|
name: "console-session-sweep",
|
|
283
|
-
|
|
308
|
+
// Sessions live 48 h, so an hourly sweep deleted nothing 47 times out of
|
|
309
|
+
// 48 — each pass a full scan of the session table for rows that cannot yet
|
|
310
|
+
// be expired. Half the session lifetime is the point where a pass can
|
|
311
|
+
// actually find work, and still leaves a full grace period after it.
|
|
312
|
+
intervalMs: 12 * 60 * 60_000,
|
|
284
313
|
run: () => deleteExpiredSessions(db),
|
|
285
314
|
});
|
|
286
315
|
scheduledTasks.register({
|
|
@@ -309,6 +338,7 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
309
338
|
routingRoundRobinEntries: () => routingEngine.roundRobinEntries(),
|
|
310
339
|
ipAbuseKeys: () => (ipStore instanceof InMemoryIpAbuseStore ? ipStore.keyCount() : 0),
|
|
311
340
|
quotaCacheEntries: () => quotaCacheSize(),
|
|
341
|
+
modelCatalogEntries: () => modelCatalogCacheSize(),
|
|
312
342
|
// Late-bound via bindInFlight once the request state store exists.
|
|
313
343
|
inFlight: () => 0,
|
|
314
344
|
});
|
|
@@ -322,7 +352,11 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
322
352
|
});
|
|
323
353
|
scheduledTasks.register({
|
|
324
354
|
name: "pool-agent-sweep",
|
|
325
|
-
|
|
355
|
+
// `resolveAgent` already self-sweeps every 30 s, so a 1 min timer added a
|
|
356
|
+
// second pass that almost always found nothing to release. 5 min is well
|
|
357
|
+
// inside the idle timeout this exists to enforce, and outside the path
|
|
358
|
+
// that already runs.
|
|
359
|
+
intervalMs: 5 * 60_000,
|
|
326
360
|
run: () => poolAgentResolver.sweepIdleAgents(),
|
|
327
361
|
});
|
|
328
362
|
const oauthRefreshService = new OAuthRefreshService(db);
|
|
@@ -338,7 +372,12 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
338
372
|
// credential — no upstream call — so it runs often and cheaply.
|
|
339
373
|
scheduledTasks.register({
|
|
340
374
|
name: "account-enrichment-sweep",
|
|
341
|
-
|
|
375
|
+
// Converges via `identity_checked_at`: once every stored credential has
|
|
376
|
+
// been parsed, the listing query returns nothing — and a 30 s timer kept
|
|
377
|
+
// running that empty query forever, two million times a year. 10 min is
|
|
378
|
+
// still instant for the case that matters (a batch import, where the label
|
|
379
|
+
// and email show up on the next tick) and costs nothing when idle.
|
|
380
|
+
intervalMs: 10 * 60_000,
|
|
342
381
|
run: () => accountIdentitySweep({ db }),
|
|
343
382
|
});
|
|
344
383
|
scheduledTasks.register({
|
|
@@ -434,9 +473,11 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
434
473
|
// an account unhealthy — a growth endpoint that 404s is a display miss, not a
|
|
435
474
|
// dispatch failure.
|
|
436
475
|
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
476
|
+
// Daily, not hourly: each claim is once per account per day and is
|
|
477
|
+
// ledger-guarded, so at an hourly cadence 23 of every 24 passes were a full
|
|
478
|
+
// scan of active accounts plus a ledger read per account that resolved to
|
|
479
|
+
// nothing. Once a day is the shortest interval where a pass can still find
|
|
480
|
+
// work, which is the only reason to run at all.
|
|
440
481
|
const buddyTaskDeps = {
|
|
441
482
|
db,
|
|
442
483
|
resolveCredential: quotaResolveCredential,
|
|
@@ -447,37 +488,37 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
447
488
|
};
|
|
448
489
|
scheduledTasks.register({
|
|
449
490
|
name: "buddy-streak",
|
|
450
|
-
intervalMs: 60 * 60_000,
|
|
491
|
+
intervalMs: 24 * 60 * 60_000,
|
|
451
492
|
initialDelayMs: 5 * 60_000,
|
|
452
493
|
run: () => buddyStreakTask(buddyTaskDeps),
|
|
453
494
|
});
|
|
454
495
|
scheduledTasks.register({
|
|
455
496
|
name: "buddy-redeem",
|
|
456
|
-
intervalMs: 60 * 60_000,
|
|
497
|
+
intervalMs: 24 * 60 * 60_000,
|
|
457
498
|
initialDelayMs: 8 * 60_000,
|
|
458
499
|
run: () => buddyRedeemTask(buddyTaskDeps),
|
|
459
500
|
});
|
|
460
501
|
scheduledTasks.register({
|
|
461
502
|
name: "buddy-lottery",
|
|
462
|
-
intervalMs: 60 * 60_000,
|
|
503
|
+
intervalMs: 24 * 60 * 60_000,
|
|
463
504
|
initialDelayMs: 11 * 60_000,
|
|
464
505
|
run: () => buddyLotteryTask(buddyTaskDeps),
|
|
465
506
|
});
|
|
466
507
|
scheduledTasks.register({
|
|
467
508
|
name: "buddy-travel",
|
|
468
|
-
intervalMs: 60 * 60_000,
|
|
509
|
+
intervalMs: 24 * 60 * 60_000,
|
|
469
510
|
initialDelayMs: 14 * 60_000,
|
|
470
511
|
run: () => buddyTravelTask(buddyTaskDeps),
|
|
471
512
|
});
|
|
472
513
|
scheduledTasks.register({
|
|
473
514
|
name: "buddy-adopt",
|
|
474
|
-
intervalMs: 60 * 60_000,
|
|
515
|
+
intervalMs: 24 * 60 * 60_000,
|
|
475
516
|
initialDelayMs: 17 * 60_000,
|
|
476
517
|
run: () => buddyAdoptTask(buddyTaskDeps),
|
|
477
518
|
});
|
|
478
519
|
scheduledTasks.register({
|
|
479
520
|
name: "buddy-trial",
|
|
480
|
-
intervalMs:
|
|
521
|
+
intervalMs: 24 * 60 * 60_000,
|
|
481
522
|
initialDelayMs: 20 * 60_000,
|
|
482
523
|
run: () => buddyTrialTask(buddyTaskDeps),
|
|
483
524
|
});
|
|
@@ -525,6 +566,7 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
|
|
|
525
566
|
poolAgentResolver,
|
|
526
567
|
telemetryBuffer,
|
|
527
568
|
usageFlush,
|
|
569
|
+
healthEventFlush,
|
|
528
570
|
poolSelector,
|
|
529
571
|
admissionService,
|
|
530
572
|
trustedProxyBoundary: resolveTrustedProxyBoundary(),
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires Elysia's TypeBox bridge statically, for a source-run process.
|
|
3
|
+
*
|
|
4
|
+
* Elysia reaches TypeBox through a lazy `require("typebox/schema")` that
|
|
5
|
+
* resolves to a partial namespace outside a bundler. The bridge supplies all
|
|
6
|
+
* five namespaces explicitly so the lookup never has to happen.
|
|
7
|
+
*
|
|
8
|
+
* This is necessary but **not sufficient** for `NODE_ENV=production`: it does
|
|
9
|
+
* not stop the eager validator branch from reading
|
|
10
|
+
* `this.tb.buildResult.external`, which is still absent on a source run. That
|
|
11
|
+
* half is owned by `resolveElysiaPrecompile()`, which turns precompilation off
|
|
12
|
+
* for a source run. Keep both — wiring the bridge without the precompile guard
|
|
13
|
+
* still crashes, and the guard without the bridge leaves the lazy require in
|
|
14
|
+
* place for anything else that needs it.
|
|
15
|
+
*
|
|
16
|
+
* Keep this import first in `main.ts` so it runs before any module builds a
|
|
17
|
+
* route.
|
|
18
|
+
*/
|
|
19
|
+
import { setupTypebox } from "elysia";
|
|
20
|
+
import * as typeboxType from "typebox";
|
|
21
|
+
import * as typeboxCompile from "typebox/compile";
|
|
22
|
+
import * as typeboxSchema from "typebox/schema";
|
|
23
|
+
import * as typeboxSystem from "typebox/system";
|
|
24
|
+
import * as typeboxValue from "typebox/value";
|
|
25
|
+
|
|
26
|
+
setupTypebox({
|
|
27
|
+
typebox: {
|
|
28
|
+
// The type side (`type` + `system`) and the value side (`value` + `schema`
|
|
29
|
+
// + `compile`) must both be complete; Elysia rejects a partial set because
|
|
30
|
+
// a half-wired bridge fails later with a less obvious error.
|
|
31
|
+
type: typeboxType,
|
|
32
|
+
system: typeboxSystem,
|
|
33
|
+
value: typeboxValue,
|
|
34
|
+
schema: typeboxSchema,
|
|
35
|
+
compile: typeboxCompile,
|
|
36
|
+
},
|
|
37
|
+
});
|
package/src/boot/lifecycle.ts
CHANGED
|
@@ -220,12 +220,14 @@ export async function bootstrap(): Promise<CartethyiaBoot> {
|
|
|
220
220
|
flushTelemetry: async () => {
|
|
221
221
|
await deps.telemetryBuffer.flush();
|
|
222
222
|
await deps.usageFlush.flush();
|
|
223
|
+
await deps.healthEventFlush.flush();
|
|
223
224
|
},
|
|
224
225
|
closePools: async () => {
|
|
225
226
|
await boot.server?.stop();
|
|
226
227
|
await deps.scheduledTasks.stop();
|
|
227
228
|
await deps.telemetryBuffer.stop({ flush: true });
|
|
228
229
|
await deps.usageFlush.stop({ flush: true });
|
|
230
|
+
await deps.healthEventFlush.stop();
|
|
229
231
|
await Promise.allSettled([
|
|
230
232
|
closeDb(),
|
|
231
233
|
closeRedis(),
|