cartethyia 2.7.4 → 2.7.5
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 +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-ChG5svFb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BIcWQun7.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Bf2VelDe.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-ggeMSjte.js → CustomizationPage-BKj_r0rp.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-U6AKaJcF.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-5SnUSy3j.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-f0DE1h0-.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js +1 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js +1 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-8fiE6tec.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BSAsYsmV.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-CPK47xQx.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js +1 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BLULNqTp.js} +1 -1
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +56 -55
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +48 -12
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +37 -31
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +1 -1
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +1 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +20 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +259 -66
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +4 -4
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +4 -4
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +252 -97
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +59 -291
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +113 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +10 -1
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +6 -10
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +6 -6
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/providers/adapters/compatible-adapter.ts +42 -3
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +23 -35
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +2 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +38 -5
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +9 -9
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +41 -14
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +81 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +85 -8
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +354 -62
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +53 -10
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +16 -4
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +1 -1
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +6 -0
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +1 -13
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +152 -37
- package/src/transport/shutdown-notice.ts +2 -2
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +376 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/{app-BayTTy01.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-BayTTy01.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background execution for the manual daily-growth batch (`POST /quota/daily-growth`).
|
|
3
|
+
*
|
|
4
|
+
* The route used to run the whole batch synchronously — chunked 3-parallel,
|
|
5
|
+
* up to two upstream HTTP calls per account, no global timeout — so a large
|
|
6
|
+
* roster held the console request open and blocked the event loop. This module
|
|
7
|
+
* moves the orchestration to an in-process background job: the route enqueues
|
|
8
|
+
* and returns `{ jobId }` immediately, and a status route reads the job's
|
|
9
|
+
* progress.
|
|
10
|
+
*
|
|
11
|
+
* The per-account runner (report then claim, egress rotation, per-leg timeout)
|
|
12
|
+
* is injected by the route — the job owns only the orchestration: the
|
|
13
|
+
* bounded-concurrency worker loop, the single-result aggregation, and the
|
|
14
|
+
* deduplication guards. This keeps the module free of the route's imports and
|
|
15
|
+
* avoids a cycle between the two quota modules.
|
|
16
|
+
*
|
|
17
|
+
* One job per (tenant, provider) key is allowed at a time. The key is the
|
|
18
|
+
* double-claim guard between two manual triggers: enqueuing again while a job
|
|
19
|
+
* for the same tenant+provider is still queued/running returns the existing
|
|
20
|
+
* job id instead of stamping the same accounts twice. In parallel with the
|
|
21
|
+
* daily sweep, the ledger is the guard — the runner stamps
|
|
22
|
+
* `markCheckinAttempted` after a successful claim, and this runner skips
|
|
23
|
+
* accounts the sweep (or an earlier run) already settled today, so the same
|
|
24
|
+
* account is never claimed twice in one window.
|
|
25
|
+
*
|
|
26
|
+
* The registry is intentionally process-local: the manual trigger is an
|
|
27
|
+
* operator action on a single gateway, so a cross-process queue would buy
|
|
28
|
+
* nothing here.
|
|
29
|
+
*/
|
|
30
|
+
import { randomUUID } from "node:crypto";
|
|
31
|
+
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
32
|
+
import type { RedisClient } from "../../persistence/redis";
|
|
33
|
+
import { checkinAttemptedToday } from "../../workers/daily-checkin";
|
|
34
|
+
import type { ValidatedFetch } from "../../network/outbound-fetch";
|
|
35
|
+
|
|
36
|
+
/** Lifecycle states of a daily-growth job. */
|
|
37
|
+
export type DailyGrowthJobState = "queued" | "running" | "done" | "failed";
|
|
38
|
+
|
|
39
|
+
/** One account the job failed to settle, with the reason. */
|
|
40
|
+
export interface DailyGrowthJobError {
|
|
41
|
+
readonly accountId: string;
|
|
42
|
+
readonly error: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Aggregate outcome of one job. */
|
|
46
|
+
export interface DailyGrowthJobResult {
|
|
47
|
+
/** Accounts the job actually attempted (excludes skipped/already-settled). */
|
|
48
|
+
readonly total: number;
|
|
49
|
+
readonly succeeded: number;
|
|
50
|
+
readonly failed: number;
|
|
51
|
+
readonly errors: readonly DailyGrowthJobError[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Public view of a job, as the status route returns it. */
|
|
55
|
+
export interface DailyGrowthJobView {
|
|
56
|
+
readonly jobId: string;
|
|
57
|
+
readonly state: DailyGrowthJobState;
|
|
58
|
+
readonly providerId: string;
|
|
59
|
+
readonly createdAt: string;
|
|
60
|
+
readonly finishedAt: string | null;
|
|
61
|
+
readonly total: number;
|
|
62
|
+
readonly succeeded: number;
|
|
63
|
+
readonly failed: number;
|
|
64
|
+
readonly errors: readonly DailyGrowthJobError[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** One account the job will run the daily-growth pass over. */
|
|
68
|
+
export interface DailyGrowthJobAccount {
|
|
69
|
+
readonly id: string;
|
|
70
|
+
readonly tenantId: string | null;
|
|
71
|
+
readonly label: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Outcome the route's per-account runner reports. */
|
|
75
|
+
export interface DailyGrowthAccountOutcome {
|
|
76
|
+
readonly ok: boolean;
|
|
77
|
+
/** Present when `ok` is false: the reason the account was not settled. */
|
|
78
|
+
readonly error?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Everything the background runner needs to execute one job. */
|
|
82
|
+
export interface DailyGrowthJobDeps {
|
|
83
|
+
readonly db: CartethyiaDatabase;
|
|
84
|
+
readonly redis: RedisClient | undefined;
|
|
85
|
+
/** Runs one account's daily-growth pass (report then claim). */
|
|
86
|
+
readonly runAccount: (account: DailyGrowthJobAccount) => Promise<DailyGrowthAccountOutcome>;
|
|
87
|
+
/** Invalidated once when the job finishes, so the page re-reads the ledger. */
|
|
88
|
+
readonly snapshotInvalidator?: { invalidate(): Promise<number> };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Bound on accounts processed at once. Mirrors the old synchronous chunk size. */
|
|
92
|
+
const DAILY_GROWTH_CONCURRENCY = 3;
|
|
93
|
+
/**
|
|
94
|
+
* Per-account leg budget. Each individual upstream call already carries its own
|
|
95
|
+
* 15s abort timeout; this bounds the whole leg (credential resolve + report +
|
|
96
|
+
* check-in) so a single stuck account cannot hold a job worker open forever.
|
|
97
|
+
*/
|
|
98
|
+
export const DAILY_GROWTH_LEG_TIMEOUT_MS = 60_000;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Wraps a fetcher so every request also carries `budget`, even when the callee
|
|
102
|
+
* sets its own per-request timeout. The buddy/qoder billing helpers pass an
|
|
103
|
+
* `AbortSignal.timeout(...)` of their own, which would override a plain
|
|
104
|
+
* `signalFetch`-style injection; combining signals intersects both bounds.
|
|
105
|
+
*/
|
|
106
|
+
export function withLegBudget(
|
|
107
|
+
fetcher: ValidatedFetch | typeof fetch,
|
|
108
|
+
budget: AbortSignal,
|
|
109
|
+
): typeof fetch {
|
|
110
|
+
const wrapped = (
|
|
111
|
+
input: Parameters<typeof fetch>[0],
|
|
112
|
+
init?: Parameters<typeof fetch>[1],
|
|
113
|
+
): Promise<Response> =>
|
|
114
|
+
fetcher(input, {
|
|
115
|
+
...init,
|
|
116
|
+
signal: init?.signal ? AbortSignal.any([budget, init.signal]) : budget,
|
|
117
|
+
});
|
|
118
|
+
return Object.assign(wrapped, {
|
|
119
|
+
preconnect: (fetcher as typeof fetch).preconnect,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Registry cap; oldest finished jobs are evicted so the map cannot grow unbounded. */
|
|
124
|
+
const DAILY_GROWTH_JOB_MAX = 64;
|
|
125
|
+
|
|
126
|
+
interface DailyGrowthJobRecord {
|
|
127
|
+
readonly id: string;
|
|
128
|
+
readonly key: string;
|
|
129
|
+
readonly tenantId: string;
|
|
130
|
+
readonly providerId: string;
|
|
131
|
+
readonly createdAt: number;
|
|
132
|
+
state: DailyGrowthJobState;
|
|
133
|
+
finishedAt: number | null;
|
|
134
|
+
readonly accounts: readonly DailyGrowthJobAccount[];
|
|
135
|
+
result: DailyGrowthJobResult;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const jobs = new Map<string, DailyGrowthJobRecord>();
|
|
139
|
+
/** tenant+provider key → job id; the double-trigger guard. */
|
|
140
|
+
const jobsByKey = new Map<string, string>();
|
|
141
|
+
/**
|
|
142
|
+
* Accounts currently being worked by any job. Global accounts (`tenant_id IS
|
|
143
|
+
* NULL`) are visible to every tenant, so two tenants can enqueue jobs over the
|
|
144
|
+
* same global account; this set keeps them from stamping it twice in the same
|
|
145
|
+
* window. Tenant-owned accounts cannot collide this way (they are unique to
|
|
146
|
+
* their tenant), but the set is uniform and cheap.
|
|
147
|
+
*/
|
|
148
|
+
const inFlightAccounts = new Set<string>();
|
|
149
|
+
|
|
150
|
+
function jobView(job: DailyGrowthJobRecord): DailyGrowthJobView {
|
|
151
|
+
return {
|
|
152
|
+
jobId: job.id,
|
|
153
|
+
state: job.state,
|
|
154
|
+
providerId: job.providerId,
|
|
155
|
+
createdAt: new Date(job.createdAt).toISOString(),
|
|
156
|
+
finishedAt: job.finishedAt === null ? null : new Date(job.finishedAt).toISOString(),
|
|
157
|
+
total: job.result.total,
|
|
158
|
+
succeeded: job.result.succeeded,
|
|
159
|
+
failed: job.result.failed,
|
|
160
|
+
errors: job.result.errors,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Evicts the oldest finished jobs once the registry exceeds its cap. */
|
|
165
|
+
function evictFinishedJobs(): void {
|
|
166
|
+
if (jobs.size <= DAILY_GROWTH_JOB_MAX) return;
|
|
167
|
+
const finished: DailyGrowthJobRecord[] = [];
|
|
168
|
+
for (const job of jobs.values()) {
|
|
169
|
+
if (job.state === "done" || job.state === "failed") finished.push(job);
|
|
170
|
+
}
|
|
171
|
+
finished.sort((left, right) => left.createdAt - right.createdAt);
|
|
172
|
+
let excess = jobs.size - DAILY_GROWTH_JOB_MAX;
|
|
173
|
+
for (const job of finished) {
|
|
174
|
+
if (excess <= 0) break;
|
|
175
|
+
jobs.delete(job.id);
|
|
176
|
+
if (jobsByKey.get(job.key) === job.id) jobsByKey.delete(job.key);
|
|
177
|
+
excess -= 1;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Runs one job to completion. Never rejects: every failure is collected into
|
|
183
|
+
* the job's result, so the status route always has an answer.
|
|
184
|
+
*/
|
|
185
|
+
async function runJob(record: DailyGrowthJobRecord, deps: DailyGrowthJobDeps): Promise<void> {
|
|
186
|
+
record.state = "running";
|
|
187
|
+
const succeeded: string[] = [];
|
|
188
|
+
const errors: DailyGrowthJobError[] = [];
|
|
189
|
+
const attempted: string[] = [];
|
|
190
|
+
let cursor = 0;
|
|
191
|
+
const accounts = record.accounts;
|
|
192
|
+
|
|
193
|
+
const runOne = async (account: DailyGrowthJobAccount): Promise<void> => {
|
|
194
|
+
// Double-claim guard with the daily sweep (and any other job): the ledger
|
|
195
|
+
// is shared, so an account the sweep already settled today is skipped
|
|
196
|
+
// rather than claimed again — the same `markCheckinAttempted` semantics
|
|
197
|
+
// the button uses.
|
|
198
|
+
if (inFlightAccounts.has(account.id)) return;
|
|
199
|
+
try {
|
|
200
|
+
if (await checkinAttemptedToday({ db: deps.db, redis: deps.redis, accountId: account.id })) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
// An unreadable ledger reads as "not attempted" — the safe direction for
|
|
205
|
+
// a manual trigger, which asserts "try now".
|
|
206
|
+
}
|
|
207
|
+
inFlightAccounts.add(account.id);
|
|
208
|
+
attempted.push(account.id);
|
|
209
|
+
try {
|
|
210
|
+
const outcome = await deps.runAccount(account);
|
|
211
|
+
if (outcome.ok) {
|
|
212
|
+
succeeded.push(account.id);
|
|
213
|
+
} else {
|
|
214
|
+
errors.push({ accountId: account.id, error: outcome.error ?? "Daily growth failed" });
|
|
215
|
+
}
|
|
216
|
+
} finally {
|
|
217
|
+
inFlightAccounts.delete(account.id);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
await Promise.all(
|
|
222
|
+
Array.from({ length: Math.min(DAILY_GROWTH_CONCURRENCY, accounts.length) }, async () => {
|
|
223
|
+
for (;;) {
|
|
224
|
+
const index = cursor;
|
|
225
|
+
cursor += 1;
|
|
226
|
+
if (index >= accounts.length) return;
|
|
227
|
+
await runOne(accounts[index]!);
|
|
228
|
+
}
|
|
229
|
+
}),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
record.result = {
|
|
233
|
+
total: attempted.length,
|
|
234
|
+
succeeded: succeeded.length,
|
|
235
|
+
failed: errors.length,
|
|
236
|
+
errors,
|
|
237
|
+
};
|
|
238
|
+
record.finishedAt = Date.now();
|
|
239
|
+
record.state = "done";
|
|
240
|
+
if (deps.snapshotInvalidator) {
|
|
241
|
+
try {
|
|
242
|
+
await deps.snapshotInvalidator.invalidate();
|
|
243
|
+
} catch {
|
|
244
|
+
// Best-effort: a failed snapshot invalidate must not fail the job.
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Enqueues a daily-growth job and returns its id immediately. When a job for
|
|
251
|
+
* the same (tenant, provider) is already queued or running, the existing job
|
|
252
|
+
* id is returned instead — the operator pressed the button twice, and running
|
|
253
|
+
* the same accounts twice would double-claim.
|
|
254
|
+
*/
|
|
255
|
+
export function enqueueDailyGrowthJob(args: {
|
|
256
|
+
readonly deps: DailyGrowthJobDeps;
|
|
257
|
+
readonly tenantId: string;
|
|
258
|
+
readonly providerId: string;
|
|
259
|
+
readonly accounts: readonly DailyGrowthJobAccount[];
|
|
260
|
+
}): string {
|
|
261
|
+
const key = `${args.tenantId}:${args.providerId}`;
|
|
262
|
+
const existing = jobsByKey.get(key);
|
|
263
|
+
if (existing !== undefined) {
|
|
264
|
+
const job = jobs.get(existing);
|
|
265
|
+
if (job !== undefined && (job.state === "queued" || job.state === "running")) {
|
|
266
|
+
return job.id;
|
|
267
|
+
}
|
|
268
|
+
jobsByKey.delete(key);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const id = randomUUID();
|
|
272
|
+
const record: DailyGrowthJobRecord = {
|
|
273
|
+
id,
|
|
274
|
+
key,
|
|
275
|
+
tenantId: args.tenantId,
|
|
276
|
+
providerId: args.providerId,
|
|
277
|
+
createdAt: Date.now(),
|
|
278
|
+
state: "queued",
|
|
279
|
+
finishedAt: null,
|
|
280
|
+
accounts: args.accounts,
|
|
281
|
+
result: { total: 0, succeeded: 0, failed: 0, errors: [] },
|
|
282
|
+
};
|
|
283
|
+
jobs.set(id, record);
|
|
284
|
+
jobsByKey.set(key, id);
|
|
285
|
+
evictFinishedJobs();
|
|
286
|
+
void runJob(record, args.deps).catch(() => {
|
|
287
|
+
record.state = "failed";
|
|
288
|
+
record.finishedAt = Date.now();
|
|
289
|
+
});
|
|
290
|
+
return id;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Reads one job's status. Scoped to the requesting tenant: a job enqueued by
|
|
295
|
+
* another tenant is not visible, so one tenant cannot probe another's work.
|
|
296
|
+
*/
|
|
297
|
+
export function getDailyGrowthJob(jobId: string, tenantId: string): DailyGrowthJobView | null {
|
|
298
|
+
const job = jobs.get(jobId);
|
|
299
|
+
if (job === undefined || job.tenantId !== tenantId) return null;
|
|
300
|
+
return jobView(job);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Test seam: clears the registry so suites do not leak state across runs. */
|
|
304
|
+
export function clearDailyGrowthJobsForTests(): void {
|
|
305
|
+
jobs.clear();
|
|
306
|
+
jobsByKey.clear();
|
|
307
|
+
inFlightAccounts.clear();
|
|
308
|
+
}
|
|
@@ -20,7 +20,7 @@ import { ConsoleDomainError, requireGlobalAdmin, requireTenantScope } from "../s
|
|
|
20
20
|
import type { ConsoleAccessResolver } from "../auth/access";
|
|
21
21
|
|
|
22
22
|
/** True when `token` looks like a plausible machine token. */
|
|
23
|
-
|
|
23
|
+
function isValidNativeIdentityToken(token: string): boolean {
|
|
24
24
|
return typeof token === "string" && token.length >= 8 && token.length <= 512;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -120,7 +120,7 @@ export function createNativeIdentityOps(
|
|
|
120
120
|
* (`tenant_id IS NULL`) need platform admin; tenant-owned accounts need the
|
|
121
121
|
* tenant scope for the owning tenant.
|
|
122
122
|
*/
|
|
123
|
-
|
|
123
|
+
interface NativeIdentityScope {
|
|
124
124
|
readonly tenantId: string | null;
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "../../providers/quota/refresh";
|
|
12
12
|
|
|
13
13
|
/** Shape returned for quota data on dashboard account responses. */
|
|
14
|
-
|
|
14
|
+
interface QuotaRefreshView {
|
|
15
15
|
readonly source: string;
|
|
16
16
|
readonly status: "ready" | "error" | "refreshing";
|
|
17
17
|
readonly plan: string | null;
|
|
@@ -14,7 +14,7 @@ import { consoleErrorHandler, requireTenantScope } from "../shared/errors";
|
|
|
14
14
|
import { REMOTE_SUPPORT_TOOLS } from "./contracts";
|
|
15
15
|
import type { RemoteSupportService } from "./service";
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
interface RemoteSupportRoutesConfig {
|
|
18
18
|
readonly accessResolver: ConsoleAccessResolver;
|
|
19
19
|
readonly service: RemoteSupportService;
|
|
20
20
|
}
|
|
@@ -33,7 +33,7 @@ const URL_POLL_MS = 500;
|
|
|
33
33
|
/** Cached install detection so we don't spawn `where`/`which` on every poll. */
|
|
34
34
|
const installedCache = new Map<RemoteSupportTool, boolean>();
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
interface RemoteSupportServiceConfig {
|
|
37
37
|
/** Defaults to the gateway's configured port. */
|
|
38
38
|
readonly port: number;
|
|
39
39
|
/** Overridable for tests. Defaults to a subdir of the user config dir. */
|
|
@@ -245,7 +245,7 @@ function tailscaleDnsName(): string | null {
|
|
|
245
245
|
* nothing about this: stopping the tunnel leaves the node up and empties the
|
|
246
246
|
* status, which is why an online node alone was read as "still active".
|
|
247
247
|
*/
|
|
248
|
-
|
|
248
|
+
function tailscaleServing(): boolean {
|
|
249
249
|
const result = spawnSync("tailscale", ["serve", "status", "--json"], { encoding: "utf8", windowsHide: true });
|
|
250
250
|
if (result.status !== 0) return false;
|
|
251
251
|
let parsed: unknown;
|
|
@@ -102,7 +102,7 @@ export interface ModelRoutingStore {
|
|
|
102
102
|
|
|
103
103
|
/** Structurally satisfied by `InMemoryRouteSnapshotService` as-is — no import
|
|
104
104
|
* of routing internals into the console domain layer. */
|
|
105
|
-
|
|
105
|
+
interface ModelRoutingSnapshotInvalidator {
|
|
106
106
|
invalidate(): Promise<number>;
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -223,7 +223,7 @@ export type PoolStatus = (typeof POOL_STATUSES)[number];
|
|
|
223
223
|
* stay aligned with the `pool_routing_strategy` enum in `schema.ts`.
|
|
224
224
|
*/
|
|
225
225
|
export const POOL_ROUTING_STRATEGIES = ["least_loaded", "round_robin", "random"] as const;
|
|
226
|
-
|
|
226
|
+
type PoolRoutingStrategyValue = (typeof POOL_ROUTING_STRATEGIES)[number];
|
|
227
227
|
|
|
228
228
|
export interface PoolStrategySetting {
|
|
229
229
|
strategy: PoolRoutingStrategyValue;
|
|
@@ -456,7 +456,7 @@ export const MAX_BATCH_PROBE_TARGETS = 100;
|
|
|
456
456
|
*/
|
|
457
457
|
export const PROVIDER_PROBE_BATCH_CONCURRENCY = 4;
|
|
458
458
|
|
|
459
|
-
|
|
459
|
+
interface NetworkPoolAgentReleaser {
|
|
460
460
|
releasePool(poolId: string, tenantId: string): Promise<void>;
|
|
461
461
|
}
|
|
462
462
|
export interface NetworkPoolConfig {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { POOL_KINDS, POOL_STATUSES } from "./contracts";
|
|
19
19
|
|
|
20
20
|
/** One row that parsed, ready to become a pool. */
|
|
21
|
-
|
|
21
|
+
interface ParsedProxyRow {
|
|
22
22
|
readonly kind: (typeof POOL_KINDS)[number];
|
|
23
23
|
readonly endpoint: string;
|
|
24
24
|
readonly credential: string | null;
|
|
@@ -26,12 +26,12 @@ export interface ParsedProxyRow {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/** One row that did not parse, with the reason. */
|
|
29
|
-
|
|
29
|
+
interface SkippedProxyRow {
|
|
30
30
|
readonly line: number;
|
|
31
31
|
readonly reason: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
interface ParsedProxyFile {
|
|
35
35
|
readonly rows: readonly ParsedProxyRow[];
|
|
36
36
|
readonly skipped: readonly SkippedProxyRow[];
|
|
37
37
|
}
|
|
@@ -36,7 +36,7 @@ export const RELAY_TARGETS: readonly RelayTarget[] = ["cloudflare", "vercel", "d
|
|
|
36
36
|
* The same handler body runs on Workers and Vercel Edge (`export default {
|
|
37
37
|
* fetch }`); the Deno entry wraps it in `Deno.serve`.
|
|
38
38
|
*/
|
|
39
|
-
|
|
39
|
+
const RELAY_WORKER_SOURCE = `// Cartethyia hosted relay.
|
|
40
40
|
//
|
|
41
41
|
// Forwards a request to the origin named by the x-relay-target header, at the
|
|
42
42
|
// path named by x-relay-path. Deployed to Cloudflare Workers / Vercel Edge.
|
|
@@ -83,7 +83,7 @@ export default handler;
|
|
|
83
83
|
`;
|
|
84
84
|
|
|
85
85
|
/** A Deno Deploy entry wraps the shared handler in `Deno.serve`. */
|
|
86
|
-
|
|
86
|
+
const RELAY_DENO_SOURCE = `${RELAY_WORKER_SOURCE}
|
|
87
87
|
Deno.serve((request) => handler.fetch(request));
|
|
88
88
|
`;
|
|
89
89
|
|
|
@@ -111,7 +111,7 @@ export interface RelayDeployResult {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** Outbound fetch seam (the validated fetch), so tests can stub deploys. */
|
|
114
|
-
|
|
114
|
+
type RelayFetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
115
115
|
|
|
116
116
|
function generateName(prefix: string): string {
|
|
117
117
|
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
@@ -53,6 +53,12 @@ export interface RuntimeSettingsResponse {
|
|
|
53
53
|
readonly telemetryWindowMinutes: number | null;
|
|
54
54
|
/** Per-tenant inline attachment ceiling in bytes; null means deployment default. */
|
|
55
55
|
readonly imageClampMaxBytes: number | null;
|
|
56
|
+
/**
|
|
57
|
+
* Ordered vision-model ids used to describe images when the selected model
|
|
58
|
+
* is not image-capable. Empty means the legacy `[image]` placeholder is
|
|
59
|
+
* used instead.
|
|
60
|
+
*/
|
|
61
|
+
readonly visionHandoffModels: readonly string[];
|
|
56
62
|
readonly updatedAt: string;
|
|
57
63
|
/*
|
|
58
64
|
* What each `null` actually resolves to, in the same units the row stores.
|
|
@@ -67,7 +73,7 @@ export interface RuntimeSettingsResponse {
|
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
/** The deployment-level values a `null` runtime setting falls back to. */
|
|
70
|
-
|
|
76
|
+
interface RuntimeSettingsDefaults {
|
|
71
77
|
/** Seconds; from `CARTETHYIA_UPSTREAM_TIMEOUT_MS`. */
|
|
72
78
|
readonly upstreamTimeoutSeconds: number;
|
|
73
79
|
/** Minutes; what the payload store expires frames after. */
|
|
@@ -122,7 +128,7 @@ export type TelemetryPayloadMode = (typeof TELEMETRY_PAYLOAD_MODES)[number];
|
|
|
122
128
|
export const TELEMETRY_PAYLOAD_DEPTHS = ["minimum", "moderate", "maximum"] as const;
|
|
123
129
|
/** Capture depth for `full` mode accepted by the runtime-settings PATCH. */
|
|
124
130
|
export type TelemetryPayloadDepth = (typeof TELEMETRY_PAYLOAD_DEPTHS)[number];
|
|
125
|
-
|
|
131
|
+
const PRIVACY_MODES = ["masked", "full"] as const;
|
|
126
132
|
/** Provider/model label privacy accepted by the runtime-settings PATCH. */
|
|
127
133
|
export type PrivacyMode = (typeof PRIVACY_MODES)[number];
|
|
128
134
|
/** PonyTail directive intensity accepted by the runtime-settings PATCH. */
|
|
@@ -130,6 +136,9 @@ export type PonyTailLevel = (typeof PONYTAIL_LEVELS)[number];
|
|
|
130
136
|
/** RTK prune strength accepted by the runtime-settings PATCH. */
|
|
131
137
|
export type RtkLevel = (typeof RTK_LEVELS)[number];
|
|
132
138
|
|
|
139
|
+
/** Upper bound on the number of ordered vision-handoff models. */
|
|
140
|
+
export const VISION_HANDOFF_MAX_MODELS = 8;
|
|
141
|
+
|
|
133
142
|
export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
|
|
134
143
|
const operations = {
|
|
135
144
|
async get(access: AccessDecision | undefined): Promise<RuntimeSettingsResponse> {
|
|
@@ -178,6 +187,23 @@ export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
|
|
|
178
187
|
if (patch.rtkPruneLevel !== undefined && !RTK_LEVELS.includes(patch.rtkPruneLevel)) {
|
|
179
188
|
throw new ConsoleDomainError("invalid_request", 400, "Invalid rtkPruneLevel");
|
|
180
189
|
}
|
|
190
|
+
if (patch.visionHandoffModels !== undefined) {
|
|
191
|
+
if (!Array.isArray(patch.visionHandoffModels)) {
|
|
192
|
+
throw new ConsoleDomainError("invalid_request", 400, "visionHandoffModels must be an array of model ids");
|
|
193
|
+
}
|
|
194
|
+
if (patch.visionHandoffModels.length > VISION_HANDOFF_MAX_MODELS) {
|
|
195
|
+
throw new ConsoleDomainError(
|
|
196
|
+
"invalid_request",
|
|
197
|
+
400,
|
|
198
|
+
`visionHandoffModels may list at most ${VISION_HANDOFF_MAX_MODELS} models`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
for (const model of patch.visionHandoffModels) {
|
|
202
|
+
if (typeof model !== "string" || model.trim().length === 0) {
|
|
203
|
+
throw new ConsoleDomainError("invalid_request", 400, "visionHandoffModels entries must be non-empty model ids");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
181
207
|
const updated = await deps.store.update(a.tenantId, patch);
|
|
182
208
|
await deps.auditSink?.record({
|
|
183
209
|
access: a,
|
|
@@ -245,4 +271,5 @@ export const runtimeUpdateBody = t.Object({
|
|
|
245
271
|
imageClampMaxBytes: t.Optional(
|
|
246
272
|
t.Union([t.Null(), t.Number({ minimum: 64 * 1024, maximum: 100 * 1024 * 1024 })]),
|
|
247
273
|
),
|
|
274
|
+
visionHandoffModels: t.Optional(t.Array(t.String())),
|
|
248
275
|
});
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
RTK_LEVELS,
|
|
16
16
|
TELEMETRY_PAYLOAD_DEPTHS,
|
|
17
17
|
TELEMETRY_PAYLOAD_MODES,
|
|
18
|
+
VISION_HANDOFF_MAX_MODELS,
|
|
18
19
|
type PonyTailLevel,
|
|
19
20
|
type ResponsesReasoningSummary,
|
|
20
21
|
type RtkLevel,
|
|
@@ -115,6 +116,22 @@ function normalizeConcurrencyTolerance(value: unknown): number | null {
|
|
|
115
116
|
return normalizeBoundedNumber(value, 0, 1024);
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
/** Ordered vision-handoff models: non-empty strings, trimmed, de-duplicated. */
|
|
120
|
+
function normalizeVisionHandoffModels(value: unknown): readonly string[] {
|
|
121
|
+
if (!Array.isArray(value)) return [];
|
|
122
|
+
const seen = new Set<string>();
|
|
123
|
+
const out: string[] = [];
|
|
124
|
+
for (const entry of value) {
|
|
125
|
+
if (typeof entry !== "string") continue;
|
|
126
|
+
const model = entry.trim();
|
|
127
|
+
if (model.length === 0 || seen.has(model)) continue;
|
|
128
|
+
seen.add(model);
|
|
129
|
+
out.push(model);
|
|
130
|
+
if (out.length >= VISION_HANDOFF_MAX_MODELS) break;
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
function mapRuntimeSettingsRow(
|
|
119
136
|
row: typeof consoleSettings.$inferSelect | undefined,
|
|
120
137
|
redisBackend: RedisBackend,
|
|
@@ -148,6 +165,7 @@ function mapRuntimeSettingsRow(
|
|
|
148
165
|
upstreamTimeoutSeconds: normalizeUpstreamTimeoutSeconds(prefs.upstreamTimeoutSeconds),
|
|
149
166
|
telemetryWindowMinutes: normalizeTelemetryWindowMinutes(prefs.telemetryWindowMinutes),
|
|
150
167
|
imageClampMaxBytes: normalizeImageClampMaxBytes(prefs.imageClampMaxBytes),
|
|
168
|
+
visionHandoffModels: normalizeVisionHandoffModels(prefs.visionHandoffModels),
|
|
151
169
|
updatedAt,
|
|
152
170
|
/*
|
|
153
171
|
* Resolved at request time, not at module load: all four come from env the
|
|
@@ -211,6 +229,8 @@ export class DrizzleRuntimeSettingsStore implements RuntimeSettingsStore {
|
|
|
211
229
|
);
|
|
212
230
|
if (patch.imageClampMaxBytes !== undefined)
|
|
213
231
|
patchPrefs.imageClampMaxBytes = normalizeImageClampMaxBytes(patch.imageClampMaxBytes);
|
|
232
|
+
if (patch.visionHandoffModels !== undefined)
|
|
233
|
+
patchPrefs.visionHandoffModels = normalizeVisionHandoffModels(patch.visionHandoffModels);
|
|
214
234
|
|
|
215
235
|
const rows = await this.db
|
|
216
236
|
.insert(consoleSettings)
|