cartethyia 2.7.3 → 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-2TJD37Hb.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-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.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-BKj_r0rp.js +1 -0
- 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-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.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-5SnUSy3j.js +9 -0
- 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-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.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-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.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-Be5Uzamx.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-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.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-q5HwZ8nN.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-BTScrQpe.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.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/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.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/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.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-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
- 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-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.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 +59 -57
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- 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/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -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 -5
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +67 -10
- 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 +8 -4
- 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 +92 -31
- package/src/console/api-keys/routes.ts +131 -6
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +5 -5
- 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 +40 -34
- 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 +11 -9
- package/src/console/backup/export-endpoint.ts +11 -12
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +32 -36
- 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 +4 -4
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +36 -4
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +18 -4
- 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 +5 -1
- package/src/console/observability/live.ts +34 -21
- package/src/console/observability/logs.ts +4 -4
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +36 -20
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +82 -22
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +110 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +30 -6
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1662 -1292
- package/src/console/providers/detail/routes.ts +4 -4
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +95 -16
- 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 +285 -67
- package/src/console/quota/account-test-egress.ts +4 -4
- 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/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +13 -10
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +12 -12
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/routes.ts +3 -3
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +112 -44
- package/src/console/share/share-stats.ts +109 -39
- 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 +42 -6
- 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 +256 -98
- 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 +76 -5
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +55 -51
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +22 -2
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +24 -75
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +286 -214
- 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/telemetry-status.ts +1 -1
- 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 +10 -5
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +19 -7
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +177 -45
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +109 -4
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/stores/telemetry-store.ts +1 -1
- 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 +12 -16
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/image-generation.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 +27 -12
- package/src/protocol/response/codex.ts +2 -2
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +6 -6
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +3 -3
- package/src/protocol/sse-decoder.ts +64 -4
- 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 +8 -8
- 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/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +44 -5
- 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/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/configured-provider.ts +1 -1
- 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 +50 -15
- package/src/providers/discovery/probing-service.ts +18 -20
- package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- 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 +15 -26
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
- 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-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
- package/src/providers/integrations/buddy/buddy-media.ts +16 -16
- 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 +5 -16
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +76 -116
- package/src/providers/integrations/commandcode/quota.ts +132 -48
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
- 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 +53 -20
- 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 +13 -13
- package/src/providers/integrations/gemini.ts +11 -11
- 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 +47 -20
- 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 +88 -42
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- 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 +91 -78
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
- package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -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 +22 -28
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
- package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
- package/src/providers/integrations/qoder/qoder.ts +521 -63
- 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/tokenharbor.ts +2 -2
- 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 +59 -19
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- 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 +22 -6
- package/src/providers/operations/provider-credential-service.ts +35 -6
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +65 -10
- package/src/providers/provider-credential-resolver.ts +1 -1
- 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-registry.ts +4 -4
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +28 -10
- package/src/providers/quota/refresh.ts +29 -6
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +6 -8
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +8 -8
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +20 -8
- 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/coerce.ts +19 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +5 -4
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +28 -25
- package/src/transport/dispatch/generation.ts +33 -53
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +10 -8
- package/src/transport/dispatch/responses-compact.ts +35 -56
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +11 -10
- 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 +27 -55
- package/src/transport/dispatch/token-count.ts +49 -46
- package/src/transport/dispatch/websearch.ts +24 -50
- package/src/transport/drain-endpoint.ts +3 -3
- package/src/transport/failure-policy.ts +19 -6
- package/src/transport/guards/auth-guard.ts +6 -11
- package/src/transport/guards/body-policy.ts +7 -7
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +2 -2
- package/src/transport/guards/pipeline.ts +7 -7
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +15 -20
- package/src/transport/request/preparer.ts +306 -27
- 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 +5 -20
- 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 +113 -19
- package/src/transport/routing/route-model.ts +131 -38
- package/src/transport/routing/router.ts +154 -39
- package/src/transport/shutdown-notice.ts +21 -9
- 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 +3 -3
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/encode.ts +1 -1
- 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-2TJD37Hb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.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-0OsXRGXe.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
- package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
- package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
- package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
- package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
- package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
- package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
- package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
- package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
- package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
- package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
- package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
- package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
- package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
- package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
- package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
- package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
- package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
- package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
- package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
- package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
- package/dist/dashboard/assets/state-DLS7IDMy.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-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.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-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Persisted API-key store: the console API-key domain and gateway authentication both consume this boundary.
|
|
2
2
|
|
|
3
|
-
import { and, asc, count, desc, eq, inArray, isNotNull, isNull, sql } from "drizzle-orm";
|
|
3
|
+
import { and, asc, count, desc, eq, inArray, isNotNull, isNull, lte, or, sql, type SQL } from "drizzle-orm";
|
|
4
4
|
import { nextSortIndex, reorderSortIndex, type SortOrderDb } from "../sort-order";
|
|
5
5
|
import { getDbHandle } from "../boot";
|
|
6
6
|
import type { CartethyiaDatabase } from "../db-handle";
|
|
7
7
|
import { lockRows } from "../dialect";
|
|
8
|
-
import { apiKeys, shareLinks, sharePopups, telemetryUsageTotals, type ApiKeyMode, type ApiKeyModelAccessMode } from "../schema";
|
|
8
|
+
import { apiKeys, shareLinks, sharePopups, telemetryUsageTotals, type ApiKeyMode, type ApiKeyModelAccessMode, type ApiKeyConcurrencyScope } from "../schema";
|
|
9
9
|
import type { AccessScope } from "../../security/access-control";
|
|
10
10
|
|
|
11
11
|
/** Sortable columns for the paged share-child listing (api_keys columns only). */
|
|
@@ -34,6 +34,18 @@ export interface SharedChildrenPage {
|
|
|
34
34
|
readonly consumed: number;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* SQL predicate: the key is not under an active ban. A ban is active while
|
|
39
|
+
* `banned_at` is set AND either `banned_until` is null (permanent) or still in
|
|
40
|
+
* the future — so a timed ban lapses on its own without a sweep.
|
|
41
|
+
*/
|
|
42
|
+
function notBannedSql(): SQL {
|
|
43
|
+
return or(
|
|
44
|
+
isNull(apiKeys.bannedAt),
|
|
45
|
+
and(isNotNull(apiKeys.bannedUntil), lte(apiKeys.bannedUntil, new Date())),
|
|
46
|
+
) as SQL;
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
/** Persisted API key. Only its one-way hash authenticates; plaintext is never stored. */
|
|
38
50
|
export interface ApiKeyRecord {
|
|
39
51
|
readonly id: string;
|
|
@@ -66,6 +78,7 @@ export interface ApiKeyRecord {
|
|
|
66
78
|
readonly monthlyTokenLimit?: number;
|
|
67
79
|
readonly lifetimeTokenBudget?: number;
|
|
68
80
|
readonly maxConcurrentRequests?: number;
|
|
81
|
+
readonly concurrencyScope?: ApiKeyConcurrencyScope;
|
|
69
82
|
/** Share-template recipient slot cap; undefined = unlimited. */
|
|
70
83
|
readonly maxChildren?: number;
|
|
71
84
|
readonly modelPrefix?: string;
|
|
@@ -75,6 +88,11 @@ export interface ApiKeyRecord {
|
|
|
75
88
|
readonly clientRouterDenylist?: readonly string[];
|
|
76
89
|
readonly createdAt: Date;
|
|
77
90
|
readonly revokedAt?: Date;
|
|
91
|
+
/** Ban state; see the schema column for the contract. */
|
|
92
|
+
readonly bannedAt?: Date;
|
|
93
|
+
readonly banReason?: string;
|
|
94
|
+
/** When the ban lifts on its own; null means permanent. */
|
|
95
|
+
readonly bannedUntil?: Date;
|
|
78
96
|
readonly tokensConsumed: number;
|
|
79
97
|
}
|
|
80
98
|
|
|
@@ -128,6 +146,9 @@ export interface ApiKeyPatch
|
|
|
128
146
|
| "lifetimeTokenBudget"
|
|
129
147
|
| "maxConcurrentRequests"
|
|
130
148
|
| "maxChildren"
|
|
149
|
+
| "bannedAt"
|
|
150
|
+
| "banReason"
|
|
151
|
+
| "bannedUntil"
|
|
131
152
|
> {
|
|
132
153
|
readonly keyHash?: string | null;
|
|
133
154
|
readonly keyEncrypted?: Buffer | null;
|
|
@@ -147,6 +168,11 @@ export interface ApiKeyPatch
|
|
|
147
168
|
readonly lifetimeTokenBudget?: number | null;
|
|
148
169
|
readonly maxConcurrentRequests?: number | null;
|
|
149
170
|
readonly maxChildren?: number | null;
|
|
171
|
+
/** Ban state; a ban also disables the key, unban clears both columns entirely. */
|
|
172
|
+
readonly bannedAt?: Date | null;
|
|
173
|
+
readonly banReason?: string | null;
|
|
174
|
+
/** When the ban lifts on its own; null (explicit) means permanent. */
|
|
175
|
+
readonly bannedUntil?: Date | null;
|
|
150
176
|
}
|
|
151
177
|
|
|
152
178
|
interface PopupFields {
|
|
@@ -247,6 +273,20 @@ export interface ApiKeyStore {
|
|
|
247
273
|
reorder(tenantId: string, keyIds: readonly string[]): Promise<void>;
|
|
248
274
|
update(tenantId: string, keyId: string, patch: ApiKeyPatch): Promise<ApiKeyRecord | undefined>;
|
|
249
275
|
revoke(tenantId: string, keyId: string, revokedAt: Date): Promise<boolean>;
|
|
276
|
+
/**
|
|
277
|
+
* Bans a key: records the operator's reason and (optionally) an expiry.
|
|
278
|
+
* `bannedUntil` null means permanent; absent defaults the the caller's
|
|
279
|
+
* choice. Returns false when the key is already revoked (a revoke is
|
|
280
|
+
* terminal).
|
|
281
|
+
*/
|
|
282
|
+
ban(
|
|
283
|
+
tenantId: string,
|
|
284
|
+
keyId: string,
|
|
285
|
+
reason: string,
|
|
286
|
+
bannedUntil?: Date | null,
|
|
287
|
+
): Promise<boolean>;
|
|
288
|
+
/** Unbans a key: clears the ban columns entirely — no metadata remains. */
|
|
289
|
+
unban(tenantId: string, keyId: string): Promise<boolean>;
|
|
250
290
|
remove(tenantId: string, keyId: string): Promise<boolean>;
|
|
251
291
|
findActiveByHash?(hash: string): Promise<typeof apiKeys.$inferSelect | undefined>;
|
|
252
292
|
findActiveById?(keyId: string): Promise<typeof apiKeys.$inferSelect | undefined>;
|
|
@@ -305,6 +345,9 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
305
345
|
...(row.maxConcurrentRequests === null
|
|
306
346
|
? {}
|
|
307
347
|
: { maxConcurrentRequests: row.maxConcurrentRequests }),
|
|
348
|
+
...(row.concurrencyScope
|
|
349
|
+
? { concurrencyScope: row.concurrencyScope as ApiKeyConcurrencyScope }
|
|
350
|
+
: {}),
|
|
308
351
|
...(row.maxChildren === null ? {} : { maxChildren: row.maxChildren }),
|
|
309
352
|
...(row.modelPrefix === null ? {} : { modelPrefix: row.modelPrefix }),
|
|
310
353
|
modelAccessMode: row.modelAccessMode,
|
|
@@ -314,6 +357,9 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
314
357
|
: { clientRouterDenylist: row.clientRouterDenylist as string[] }),
|
|
315
358
|
createdAt: row.createdAt,
|
|
316
359
|
...(row.revokedAt ? { revokedAt: row.revokedAt } : {}),
|
|
360
|
+
...(row.bannedAt ? { bannedAt: row.bannedAt } : {}),
|
|
361
|
+
...(row.banReason ? { banReason: row.banReason } : {}),
|
|
362
|
+
...(row.bannedUntil ? { bannedUntil: row.bannedUntil } : {}),
|
|
317
363
|
tokensConsumed: row.lifetimeTokensConsumed,
|
|
318
364
|
};
|
|
319
365
|
}
|
|
@@ -505,6 +551,7 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
505
551
|
monthlyTokenLimit: record.monthlyTokenLimit ?? null,
|
|
506
552
|
lifetimeTokenBudget: record.lifetimeTokenBudget ?? null,
|
|
507
553
|
maxConcurrentRequests: record.maxConcurrentRequests ?? null,
|
|
554
|
+
concurrencyScope: record.concurrencyScope ?? "family",
|
|
508
555
|
maxChildren: record.maxChildren ?? null,
|
|
509
556
|
modelPrefix: record.modelPrefix ?? null,
|
|
510
557
|
modelAccessMode: record.modelAccessMode ?? "whitelist",
|
|
@@ -573,6 +620,9 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
573
620
|
...(patch.maxConcurrentRequests !== undefined
|
|
574
621
|
? { maxConcurrentRequests: patch.maxConcurrentRequests }
|
|
575
622
|
: {}),
|
|
623
|
+
...(patch.concurrencyScope !== undefined
|
|
624
|
+
? { concurrencyScope: patch.concurrencyScope }
|
|
625
|
+
: {}),
|
|
576
626
|
...(patch.maxChildren !== undefined ? { maxChildren: patch.maxChildren } : {}),
|
|
577
627
|
...(patch.modelPrefix !== undefined ? { modelPrefix: patch.modelPrefix } : {}),
|
|
578
628
|
...(patch.modelAccessMode !== undefined ? { modelAccessMode: patch.modelAccessMode } : {}),
|
|
@@ -586,6 +636,8 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
586
636
|
// silently dropped — a grant like `routing:cli_mapping` can be
|
|
587
637
|
// turned on and never stick.
|
|
588
638
|
...(patch.scopes === undefined ? {} : { scopes: patch.scopes }),
|
|
639
|
+
...(patch.bannedAt !== undefined ? { bannedAt: patch.bannedAt } : {}),
|
|
640
|
+
...(patch.banReason !== undefined ? { banReason: patch.banReason } : {}),
|
|
589
641
|
})
|
|
590
642
|
.where(and(eq(apiKeys.tenantId, tenantId), eq(apiKeys.id, keyId)))
|
|
591
643
|
.returning();
|
|
@@ -622,6 +674,55 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
622
674
|
return true;
|
|
623
675
|
});
|
|
624
676
|
}
|
|
677
|
+
|
|
678
|
+
async ban(
|
|
679
|
+
tenantId: string,
|
|
680
|
+
keyId: string,
|
|
681
|
+
reason: string,
|
|
682
|
+
bannedUntil?: Date | null,
|
|
683
|
+
): Promise<boolean> {
|
|
684
|
+
return this.db.transaction(async (tx) => {
|
|
685
|
+
const rows = await tx
|
|
686
|
+
.update(apiKeys)
|
|
687
|
+
.set({
|
|
688
|
+
bannedAt: new Date(),
|
|
689
|
+
banReason: reason,
|
|
690
|
+
// `banned_until` null = permanent. The key stays `enabled` as-is:
|
|
691
|
+
// the auth path refuses a banned key while `banned_at` is set and
|
|
692
|
+
// `banned_until` is null or future, so a timed ban lapses on its own
|
|
693
|
+
// without a sweep flipping `enabled` back.
|
|
694
|
+
...(bannedUntil === undefined ? {} : { bannedUntil }),
|
|
695
|
+
})
|
|
696
|
+
.where(
|
|
697
|
+
and(
|
|
698
|
+
eq(apiKeys.tenantId, tenantId),
|
|
699
|
+
eq(apiKeys.id, keyId),
|
|
700
|
+
isNull(apiKeys.revokedAt),
|
|
701
|
+
),
|
|
702
|
+
)
|
|
703
|
+
.returning({ id: apiKeys.id });
|
|
704
|
+
return rows.length > 0;
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
async unban(tenantId: string, keyId: string): Promise<boolean> {
|
|
709
|
+
return this.db.transaction(async (tx) => {
|
|
710
|
+
// Unban clears all three ban columns entirely — no tombstone, no leftover
|
|
711
|
+
// reason, no stale expiry. The key returns to its prior enabled state.
|
|
712
|
+
const rows = await tx
|
|
713
|
+
.update(apiKeys)
|
|
714
|
+
.set({ bannedAt: null, banReason: null, bannedUntil: null, enabled: true })
|
|
715
|
+
.where(
|
|
716
|
+
and(
|
|
717
|
+
eq(apiKeys.tenantId, tenantId),
|
|
718
|
+
eq(apiKeys.id, keyId),
|
|
719
|
+
isNull(apiKeys.revokedAt),
|
|
720
|
+
),
|
|
721
|
+
)
|
|
722
|
+
.returning({ id: apiKeys.id });
|
|
723
|
+
return rows.length > 0;
|
|
724
|
+
});
|
|
725
|
+
}
|
|
625
726
|
async remove(tenantId: string, keyId: string): Promise<boolean> {
|
|
626
727
|
return this.db.transaction(async (tx) => {
|
|
627
728
|
const rows = await tx
|
|
@@ -647,7 +748,7 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
647
748
|
const rows = await this.db
|
|
648
749
|
.select()
|
|
649
750
|
.from(apiKeys)
|
|
650
|
-
.where(and(eq(apiKeys.keyHash, hash), eq(apiKeys.enabled, true), isNull(apiKeys.revokedAt)))
|
|
751
|
+
.where(and(eq(apiKeys.keyHash, hash), eq(apiKeys.enabled, true), isNull(apiKeys.revokedAt), notBannedSql()))
|
|
651
752
|
.limit(1);
|
|
652
753
|
return rows[0];
|
|
653
754
|
}
|
|
@@ -658,7 +759,7 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
658
759
|
const rows = await this.db
|
|
659
760
|
.select()
|
|
660
761
|
.from(apiKeys)
|
|
661
|
-
.where(and(eq(apiKeys.id, keyId), eq(apiKeys.enabled, true), isNull(apiKeys.revokedAt)))
|
|
762
|
+
.where(and(eq(apiKeys.id, keyId), eq(apiKeys.enabled, true), isNull(apiKeys.revokedAt), notBannedSql()))
|
|
662
763
|
.limit(1);
|
|
663
764
|
return rows[0];
|
|
664
765
|
}
|
|
@@ -709,6 +810,7 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
709
810
|
lifetimeTokenBudget: integer("lifetime_token_budget"),
|
|
710
811
|
lifetimeTokensConsumed: integer("lifetime_tokens_consumed") ?? 0,
|
|
711
812
|
maxConcurrentRequests: integer("max_concurrent_requests"),
|
|
813
|
+
concurrencyScope: (text("concurrency_scope") ?? "family") as ApiKeyConcurrencyScope,
|
|
712
814
|
modelAccessMode: (text("model_access_mode") ?? "whitelist") as ApiKeyModelAccessMode,
|
|
713
815
|
modelList: json("model_list"),
|
|
714
816
|
clientRouterDenylist: json("client_router_denylist"),
|
|
@@ -716,6 +818,9 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
|
|
|
716
818
|
createdAt: date("created_at") ?? new Date(0),
|
|
717
819
|
enabled: flag("enabled"),
|
|
718
820
|
revokedAt: date("revoked_at"),
|
|
821
|
+
bannedAt: date("banned_at"),
|
|
822
|
+
banReason: text("ban_reason"),
|
|
823
|
+
bannedUntil: date("banned_until"),
|
|
719
824
|
keyPrefix: text("key_prefix"),
|
|
720
825
|
keyEncrypted: raw["key_encrypted"] ?? null,
|
|
721
826
|
notesTitle: text("notes_title"),
|
|
@@ -7,7 +7,7 @@ import { createHash, randomBytes, randomInt } from "node:crypto";
|
|
|
7
7
|
import { and, desc, eq, isNull, or, sql, type SQL } from "drizzle-orm";
|
|
8
8
|
import type { CartethyiaDatabase } from "../db-handle";
|
|
9
9
|
import { lockRows } from "../dialect";
|
|
10
|
-
import { apiKeys, shareLinks, sharePopups, type ShareLinkKind, type ApiKeyModelAccessMode } from "../schema";
|
|
10
|
+
import { apiKeys, shareLinks, sharePopups, type ShareLinkKind, type ApiKeyModelAccessMode, type ApiKeyConcurrencyScope } from "../schema";
|
|
11
11
|
|
|
12
12
|
/** Hashes a share bearer token for storage and lookup. */
|
|
13
13
|
/**
|
|
@@ -38,7 +38,7 @@ function addressMatches(address: string): SQL {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** Newly created share-link metadata. */
|
|
41
|
-
|
|
41
|
+
interface ShareLinkRecord {
|
|
42
42
|
readonly id: string;
|
|
43
43
|
readonly apiKeyId: string;
|
|
44
44
|
readonly kind: ShareLinkKind;
|
|
@@ -61,7 +61,7 @@ export interface ShareLinkSummary {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/** A stable link's encrypted token, or null when it predates token retention. */
|
|
64
|
-
|
|
64
|
+
interface ShareLinkToken {
|
|
65
65
|
readonly id: string;
|
|
66
66
|
readonly apiKeyId: string;
|
|
67
67
|
readonly kind: ShareLinkKind;
|
|
@@ -86,6 +86,7 @@ export interface ShareLinkPolicy {
|
|
|
86
86
|
readonly monthlyTokenLimit: number | null;
|
|
87
87
|
readonly lifetimeTokenBudget: number | null;
|
|
88
88
|
readonly maxConcurrentRequests: number | null;
|
|
89
|
+
readonly concurrencyScope: ApiKeyConcurrencyScope;
|
|
89
90
|
readonly modelAccessMode: ApiKeyModelAccessMode | null;
|
|
90
91
|
readonly modelList: readonly string[] | null;
|
|
91
92
|
readonly modelPrefix: string | null;
|
|
@@ -102,13 +103,13 @@ export interface ShareLinkPolicy {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/** Public template fields authorized for a valid enrollment-link lookup. */
|
|
105
|
-
|
|
106
|
+
interface ShareApiKeyRow extends ShareLinkPolicy {
|
|
106
107
|
readonly active: boolean;
|
|
107
108
|
readonly createdAt: string;
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
/** A personal key revealed by its handoff link. */
|
|
111
|
-
|
|
112
|
+
interface ShareHandoffRow extends ShareLinkPolicy {
|
|
112
113
|
/** Encrypted personal credential; null when the row predates secret storage. */
|
|
113
114
|
readonly keyEncrypted: Buffer | null;
|
|
114
115
|
}
|
|
@@ -118,7 +119,7 @@ export interface ShareHandoffRow extends ShareLinkPolicy {
|
|
|
118
119
|
* public lookup answers with a discriminated result instead of the caller
|
|
119
120
|
* guessing which endpoint to try.
|
|
120
121
|
*/
|
|
121
|
-
|
|
122
|
+
type ShareLinkResolution =
|
|
122
123
|
| { readonly kind: "enroll"; readonly key: ShareApiKeyRow }
|
|
123
124
|
| { readonly kind: "handoff"; readonly key: ShareHandoffRow };
|
|
124
125
|
|
|
@@ -136,7 +137,7 @@ export type ShareLinkOutcome =
|
|
|
136
137
|
| { readonly ok: true; readonly resolution: ShareLinkResolution }
|
|
137
138
|
| { readonly ok: false; readonly refusal: ShareLinkRefusal };
|
|
138
139
|
|
|
139
|
-
|
|
140
|
+
interface SharedApiKeyMaterial {
|
|
140
141
|
readonly keyHash: string;
|
|
141
142
|
readonly keyPrefix: string;
|
|
142
143
|
readonly clientIp: string;
|
|
@@ -145,7 +146,7 @@ export interface SharedApiKeyMaterial {
|
|
|
145
146
|
readonly nameHint?: string;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
|
|
149
|
+
type SharedApiKeyIssueResult =
|
|
149
150
|
| {
|
|
150
151
|
readonly kind: "issued";
|
|
151
152
|
readonly apiKeyId: string;
|
|
@@ -185,6 +186,7 @@ function mapShareRow(key: ApiKeyRow, link: ShareLinkRow, popup?: PopupRow): Shar
|
|
|
185
186
|
monthlyTokenLimit: key.monthlyTokenLimit,
|
|
186
187
|
lifetimeTokenBudget: key.lifetimeTokenBudget,
|
|
187
188
|
maxConcurrentRequests: key.maxConcurrentRequests,
|
|
189
|
+
concurrencyScope: key.concurrencyScope ?? "family",
|
|
188
190
|
modelAccessMode: key.modelAccessMode as ApiKeyModelAccessMode | null,
|
|
189
191
|
modelList: key.modelList as readonly string[] | null,
|
|
190
192
|
modelPrefix: key.modelPrefix,
|
|
@@ -213,6 +215,7 @@ function mapHandoffRow(key: ApiKeyRow, link: ShareLinkRow, popup?: PopupRow): Sh
|
|
|
213
215
|
monthlyTokenLimit: key.monthlyTokenLimit,
|
|
214
216
|
lifetimeTokenBudget: key.lifetimeTokenBudget,
|
|
215
217
|
maxConcurrentRequests: key.maxConcurrentRequests,
|
|
218
|
+
concurrencyScope: key.concurrencyScope ?? "family",
|
|
216
219
|
modelAccessMode: key.modelAccessMode as ApiKeyModelAccessMode | null,
|
|
217
220
|
modelList: key.modelList as readonly string[] | null,
|
|
218
221
|
modelPrefix: key.modelPrefix,
|
|
@@ -557,6 +560,12 @@ export class DrizzleShareLinkStore implements ShareLinkStore {
|
|
|
557
560
|
// Nullable policy columns stay NULL; `model_access_mode` takes its
|
|
558
561
|
// NOT NULL default ('whitelist'). Keep in sync with migration 0029 and
|
|
559
562
|
// `normalizeShareChildPolicy` in `src/console/backup/store.ts`.
|
|
563
|
+
//
|
|
564
|
+
// Exception: a template with `concurrencyScope: "per_key"` lets each
|
|
565
|
+
// recipient carry its own concurrency cap, so the cap is copied to the
|
|
566
|
+
// child at issue (the child's admission identity is its own id, not the
|
|
567
|
+
// parent's). The `family` default keeps child caps NULL — they inherit
|
|
568
|
+
// the parent's cap through auth.
|
|
560
569
|
const [child] = await tx
|
|
561
570
|
.insert(apiKeys)
|
|
562
571
|
.values({
|
|
@@ -571,6 +580,9 @@ export class DrizzleShareLinkStore implements ShareLinkStore {
|
|
|
571
580
|
keyPrefix: material.keyPrefix,
|
|
572
581
|
keyEncrypted: null,
|
|
573
582
|
lifetimeTokensConsumed: 0,
|
|
583
|
+
...(parent.concurrencyScope === "per_key"
|
|
584
|
+
? { concurrencyScope: "per_key" as const, maxConcurrentRequests: parent.maxConcurrentRequests }
|
|
585
|
+
: {}),
|
|
574
586
|
})
|
|
575
587
|
.returning({ id: apiKeys.id, tenantId: apiKeys.tenantId, createdAt: apiKeys.createdAt });
|
|
576
588
|
if (!child) throw new Error("shared API-key insert returned no row");
|
|
@@ -55,7 +55,7 @@ function addUsageDelta(
|
|
|
55
55
|
delta.outputTokens += row.outputTokens ?? 0;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function aggregateUsage(rows: readonly (typeof telemetryEvents.$inferInsert)[]) {
|
|
58
|
+
export function aggregateUsage(rows: readonly (typeof telemetryEvents.$inferInsert)[]) {
|
|
59
59
|
const deltas = new Map<string, UsageTotalDelta>();
|
|
60
60
|
for (const row of rows) {
|
|
61
61
|
addUsageDelta(deltas, row, "account", row.accountId);
|
|
@@ -7,15 +7,20 @@ import { eq } from "drizzle-orm";
|
|
|
7
7
|
import type { CartethyiaDatabase } from "./db-handle";
|
|
8
8
|
import { tenantSystemPrompts } from "./schema";
|
|
9
9
|
import type {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
ScopePromptRef,
|
|
11
|
+
SystemPromptPreset,
|
|
12
12
|
TenantSystemPrompts,
|
|
13
13
|
} from "../console/system-prompt/contracts";
|
|
14
14
|
import {
|
|
15
15
|
EMPTY_TENANT_SYSTEM_PROMPTS,
|
|
16
|
+
SYSTEM_PROMPT_ALIAS_LIMIT,
|
|
17
|
+
SYSTEM_PROMPT_COMBO_LIMIT,
|
|
16
18
|
SYSTEM_PROMPT_MODEL_LIMIT,
|
|
19
|
+
SYSTEM_PROMPT_PRESET_LIMIT,
|
|
17
20
|
SYSTEM_PROMPT_PROVIDER_LIMIT,
|
|
18
21
|
isPromptSetting,
|
|
22
|
+
isScopePromptRef,
|
|
23
|
+
isSystemPromptPreset,
|
|
19
24
|
parseSystemPromptModelKey,
|
|
20
25
|
} from "../console/system-prompt/contracts";
|
|
21
26
|
import { bumpSettingsRevision } from "./settings-revision";
|
|
@@ -23,6 +28,24 @@ import { bumpSettingsRevision } from "./settings-revision";
|
|
|
23
28
|
/** The stored row, normalized. Absent row reads as the empty configuration. */
|
|
24
29
|
export type StoredTenantSystemPrompts = TenantSystemPrompts;
|
|
25
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Normalizes one scope reference value. Accepts the new `{ kind, ... }` shape
|
|
33
|
+
* and the legacy per-provider entry shape (`{ followGlobal, setting? }`) so a
|
|
34
|
+
* hand-edited or pre-migration row stays valid. Invalid values become
|
|
35
|
+
* `undefined` (the caller drops the key).
|
|
36
|
+
*/
|
|
37
|
+
function normalizeScopeRef(value: unknown, legacyProviderEntry?: boolean): ScopePromptRef | undefined {
|
|
38
|
+
if (isScopePromptRef(value)) return value;
|
|
39
|
+
if (legacyProviderEntry && typeof value === "object" && value !== null) {
|
|
40
|
+
const candidate = value as { followGlobal?: unknown; setting?: unknown };
|
|
41
|
+
if (candidate.followGlobal === true) return { kind: "global" };
|
|
42
|
+
if (candidate.followGlobal === false && isPromptSetting(candidate.setting)) {
|
|
43
|
+
return { kind: "custom", setting: candidate.setting };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
26
49
|
/**
|
|
27
50
|
* Reads the tenant's stored configuration, defensively normalized: a
|
|
28
51
|
* hand-edited or legacy row must not poison the dispatch path, so malformed
|
|
@@ -35,8 +58,11 @@ export async function readTenantSystemPrompts(
|
|
|
35
58
|
const rows = await db
|
|
36
59
|
.select({
|
|
37
60
|
global: tenantSystemPrompts.global,
|
|
61
|
+
presets: tenantSystemPrompts.presets,
|
|
38
62
|
providers: tenantSystemPrompts.providers,
|
|
39
63
|
models: tenantSystemPrompts.models,
|
|
64
|
+
combos: tenantSystemPrompts.combos,
|
|
65
|
+
aliases: tenantSystemPrompts.aliases,
|
|
40
66
|
})
|
|
41
67
|
.from(tenantSystemPrompts)
|
|
42
68
|
.where(eq(tenantSystemPrompts.tenantId, tenantId))
|
|
@@ -44,30 +70,43 @@ export async function readTenantSystemPrompts(
|
|
|
44
70
|
const row = rows[0];
|
|
45
71
|
if (row === undefined) return EMPTY_TENANT_SYSTEM_PROMPTS;
|
|
46
72
|
|
|
47
|
-
const
|
|
48
|
-
for (const [
|
|
49
|
-
if (
|
|
50
|
-
const candidate = entry as Partial<ProviderPromptEntry>;
|
|
51
|
-
if (typeof candidate.followGlobal !== "boolean") continue;
|
|
52
|
-
if (candidate.followGlobal === false) {
|
|
53
|
-
if (!isPromptSetting(candidate.setting)) continue;
|
|
54
|
-
providers[providerId] = { followGlobal: false, setting: candidate.setting };
|
|
55
|
-
} else {
|
|
56
|
-
providers[providerId] = { followGlobal: true };
|
|
57
|
-
}
|
|
73
|
+
const presets: Record<string, SystemPromptPreset> = {};
|
|
74
|
+
for (const [id, preset] of Object.entries(row.presets ?? {})) {
|
|
75
|
+
if (isSystemPromptPreset(preset) && preset.id === id) presets[id] = preset;
|
|
58
76
|
}
|
|
59
77
|
|
|
60
|
-
const
|
|
61
|
-
for (const [
|
|
78
|
+
const providers: Record<string, ScopePromptRef> = {};
|
|
79
|
+
for (const [providerId, value] of Object.entries(row.providers ?? {})) {
|
|
80
|
+
const ref = normalizeScopeRef(value, true);
|
|
81
|
+
if (ref !== undefined) providers[providerId] = ref;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const models: Record<string, ScopePromptRef> = {};
|
|
85
|
+
for (const [key, value] of Object.entries(row.models ?? {})) {
|
|
62
86
|
if (parseSystemPromptModelKey(key) === null) continue;
|
|
63
|
-
|
|
64
|
-
models[key] =
|
|
87
|
+
const ref = normalizeScopeRef(value);
|
|
88
|
+
if (ref !== undefined) models[key] = ref;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const combos: Record<string, ScopePromptRef> = {};
|
|
92
|
+
for (const [name, value] of Object.entries(row.combos ?? {})) {
|
|
93
|
+
const ref = normalizeScopeRef(value);
|
|
94
|
+
if (ref !== undefined) combos[name] = ref;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const aliases: Record<string, ScopePromptRef> = {};
|
|
98
|
+
for (const [name, value] of Object.entries(row.aliases ?? {})) {
|
|
99
|
+
const ref = normalizeScopeRef(value);
|
|
100
|
+
if (ref !== undefined) aliases[name] = ref;
|
|
65
101
|
}
|
|
66
102
|
|
|
67
103
|
return {
|
|
68
|
-
global: row.global !== null &&
|
|
104
|
+
global: row.global !== null && isScopePromptRef(row.global) ? row.global : null,
|
|
105
|
+
presets,
|
|
69
106
|
providers,
|
|
70
107
|
models,
|
|
108
|
+
combos,
|
|
109
|
+
aliases,
|
|
71
110
|
};
|
|
72
111
|
}
|
|
73
112
|
|
|
@@ -79,12 +118,18 @@ export async function writeTenantSystemPrompts(
|
|
|
79
118
|
): Promise<void> {
|
|
80
119
|
// Keep the row bounded even though the API already enforces the caps: a
|
|
81
120
|
// direct store caller must not be able to plant an unbounded map.
|
|
121
|
+
const presets = Object.entries(next.presets).slice(0, SYSTEM_PROMPT_PRESET_LIMIT);
|
|
82
122
|
const providers = Object.entries(next.providers).slice(0, SYSTEM_PROMPT_PROVIDER_LIMIT);
|
|
83
123
|
const models = Object.entries(next.models).slice(0, SYSTEM_PROMPT_MODEL_LIMIT);
|
|
124
|
+
const combos = Object.entries(next.combos).slice(0, SYSTEM_PROMPT_COMBO_LIMIT);
|
|
125
|
+
const aliases = Object.entries(next.aliases).slice(0, SYSTEM_PROMPT_ALIAS_LIMIT);
|
|
84
126
|
const values = {
|
|
85
127
|
global: next.global,
|
|
128
|
+
presets: Object.fromEntries(presets),
|
|
86
129
|
providers: Object.fromEntries(providers),
|
|
87
130
|
models: Object.fromEntries(models),
|
|
131
|
+
combos: Object.fromEntries(combos),
|
|
132
|
+
aliases: Object.fromEntries(aliases),
|
|
88
133
|
updatedAt: new Date(),
|
|
89
134
|
};
|
|
90
135
|
await db
|
|
@@ -29,7 +29,7 @@ export class DrizzlePreferencesReader implements PreferencesReader {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
interface CachedPreferencesReaderOptions {
|
|
33
33
|
readonly ttlMs?: number;
|
|
34
34
|
readonly maxEntries?: number;
|
|
35
35
|
readonly revision?: () => number;
|
|
@@ -7,13 +7,13 @@ import { dedupeRequest } from "../network/deduplication";
|
|
|
7
7
|
* - Revision-aware key invalidation
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface TtlCacheOptions {
|
|
11
11
|
readonly ttlMs?: number;
|
|
12
12
|
readonly maxEntries?: number;
|
|
13
13
|
readonly now?: () => number;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
interface TtlCacheEntry<T> {
|
|
17
17
|
readonly value: T;
|
|
18
18
|
readonly at: number;
|
|
19
19
|
}
|
|
@@ -134,7 +134,7 @@ export class TtlCache<T> {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/** One TTL-sized cache inside a {@link TtlCacheFamily}. */
|
|
137
|
-
|
|
137
|
+
interface TtlCacheFamilyOptions {
|
|
138
138
|
/** TTL applied when a caller does not pass its own. */
|
|
139
139
|
readonly defaultTtlMs: number;
|
|
140
140
|
/** Entry cap per TTL-sized cache. */
|
|
@@ -65,14 +65,19 @@ async function writeLifetimeDeltas(
|
|
|
65
65
|
// text and the join against a uuid key fails. SQLite stores the key as text
|
|
66
66
|
// and rejects `UPDATE ... FROM (VALUES ...)` outright, so the same rows are
|
|
67
67
|
// a SELECT with named columns instead.
|
|
68
|
-
const
|
|
68
|
+
const sqlite = sqliteDialect();
|
|
69
|
+
const source = sqlite
|
|
69
70
|
? `(${rows.map((row) => `SELECT '${row.apiKeyId}' AS id, ${row.delta} AS delta`).join(" UNION ALL ")})`
|
|
70
71
|
: `(VALUES ${rows.map((row) => `('${row.apiKeyId}'::uuid, ${row.delta}::bigint)`).join(", ")}) AS v(id, delta)`;
|
|
72
|
+
// PostgreSQL already carries its alias in `source`; only the bare SQLite
|
|
73
|
+
// subquery needs the trailing alias appended. Emitting both (`AS v(id, delta) v`)
|
|
74
|
+
// is a syntax error on Postgres, so the flush silently never ran.
|
|
75
|
+
const from = sqlite ? `${source} v` : source;
|
|
71
76
|
await db.execute(sql.raw(`
|
|
72
77
|
UPDATE "api_keys"
|
|
73
78
|
SET "lifetime_tokens_consumed" =
|
|
74
79
|
COALESCE("api_keys"."lifetime_tokens_consumed", 0) + v.delta
|
|
75
|
-
FROM ${
|
|
80
|
+
FROM ${from}
|
|
76
81
|
WHERE "api_keys"."id" = v.id
|
|
77
82
|
`));
|
|
78
83
|
}
|
|
@@ -614,7 +614,7 @@ export function hasEncryptedReasoning(parts: readonly ContentPart[]): boolean {
|
|
|
614
614
|
* concurrent preflights would be an aliasing hazard; the memoized summary and
|
|
615
615
|
* its extension set are treated as read-only.)
|
|
616
616
|
*/
|
|
617
|
-
|
|
617
|
+
interface PartSummary {
|
|
618
618
|
readonly hasImage: boolean;
|
|
619
619
|
readonly hasDocument: boolean;
|
|
620
620
|
readonly hasAudio: boolean;
|
package/src/protocol/codecs.ts
CHANGED
|
@@ -89,7 +89,7 @@ export function hash32(input: string): number {
|
|
|
89
89
|
return hash;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
const CLAUDE_TOOL_PREFIX = "_";
|
|
93
93
|
|
|
94
94
|
export type ClaudeWireObject = Record<string, unknown>;
|
|
95
95
|
|
|
@@ -408,13 +408,9 @@ export function isClaudeBillingHeaderText(text: string): boolean {
|
|
|
408
408
|
// Well-formed UTF-16 coercion
|
|
409
409
|
|
|
410
410
|
export function toWellFormedString(value: string): string {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
return value.replace(
|
|
415
|
-
/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,
|
|
416
|
-
"\uFFFD",
|
|
417
|
-
);
|
|
411
|
+
// String.prototype.toWellFormed is ES2024; guaranteed by Bun ≥1.4, the only
|
|
412
|
+
// supported runtime.
|
|
413
|
+
return value.toWellFormed();
|
|
418
414
|
}
|
|
419
415
|
|
|
420
416
|
export function toWellFormedDeep(value: unknown): unknown {
|
|
@@ -480,7 +476,7 @@ export function assertBoundedJsonDepth(value: unknown): void {
|
|
|
480
476
|
|
|
481
477
|
function assertBoundedDepthWalk(value: unknown, depth: number): void {
|
|
482
478
|
if (depth > MAX_JSON_DEPTH)
|
|
483
|
-
throw new GatewayError("invalid_request", 400, `The request body is nested deeper than this endpoint accepts (${MAX_JSON_DEPTH} levels). Flatten the payload
|
|
479
|
+
throw new GatewayError("invalid_request", 400, `The request body is nested deeper than this endpoint accepts (${MAX_JSON_DEPTH} levels) (request {requestId}). Flatten the payload. Check the shape of what you sent — then resend 🪞`);
|
|
484
480
|
if (Array.isArray(value)) {
|
|
485
481
|
for (const item of value) assertBoundedDepthWalk(item, depth + 1);
|
|
486
482
|
return;
|
|
@@ -873,7 +869,7 @@ export const BARE_ROOT_ENDPOINTS: Partial<Record<WireFamily, string>> = {
|
|
|
873
869
|
};
|
|
874
870
|
|
|
875
871
|
/** An image part's provider-neutral source, resolved from any supported origin shape. */
|
|
876
|
-
|
|
872
|
+
interface ResolvedImageSource {
|
|
877
873
|
/** URL or `data:` URI. Mutually usable with `fileId`. */
|
|
878
874
|
url?: string;
|
|
879
875
|
/** Files API reference (Chat/Responses `file_id`). */
|
|
@@ -883,7 +879,7 @@ export interface ResolvedImageSource {
|
|
|
883
879
|
}
|
|
884
880
|
|
|
885
881
|
/** An RFC 2397 base64 `data:` URL split into the media type and bytes it carries. */
|
|
886
|
-
|
|
882
|
+
interface DataUrlPayload {
|
|
887
883
|
/** The declared media type (the `image/png` of `data:image/png;base64,…`). */
|
|
888
884
|
readonly mediaType: string;
|
|
889
885
|
/** The base64 payload, with the `data:` prefix and media type removed. */
|
|
@@ -1038,7 +1034,7 @@ export function filterProviderCustomHeaders(
|
|
|
1038
1034
|
for (const [rawName, rawValue] of Object.entries(input)) {
|
|
1039
1035
|
const name = rawName.toLowerCase();
|
|
1040
1036
|
if (!HEADER_TOKEN.test(name)) {
|
|
1041
|
-
throw new GatewayError("invalid_request", 400, `invalid custom header name: ${rawName}
|
|
1037
|
+
throw new GatewayError("invalid_request", 400, `invalid custom header name: ${rawName} (request {requestId}). Check the shape of what you sent — then resend 🪞`, {
|
|
1042
1038
|
header: rawName,
|
|
1043
1039
|
});
|
|
1044
1040
|
}
|
|
@@ -1047,7 +1043,7 @@ export function filterProviderCustomHeaders(
|
|
|
1047
1043
|
extraReserved.has(name) ||
|
|
1048
1044
|
name.startsWith("x-forwarded-")
|
|
1049
1045
|
) {
|
|
1050
|
-
throw new GatewayError("invalid_request", 400, `custom header is protected: ${rawName}
|
|
1046
|
+
throw new GatewayError("invalid_request", 400, `custom header is protected: ${rawName} (request {requestId}). Not allowed by policy — no hard feelings 🫂`, {
|
|
1051
1047
|
header: rawName,
|
|
1052
1048
|
});
|
|
1053
1049
|
}
|
|
@@ -1055,7 +1051,7 @@ export function filterProviderCustomHeaders(
|
|
|
1055
1051
|
throw new GatewayError(
|
|
1056
1052
|
"invalid_request",
|
|
1057
1053
|
400,
|
|
1058
|
-
`custom header value must be a string: ${rawName}
|
|
1054
|
+
`custom header value must be a string: ${rawName} (request {requestId}). Check the shape of what you sent — then resend 🪞`,
|
|
1059
1055
|
{ header: rawName },
|
|
1060
1056
|
);
|
|
1061
1057
|
}
|
|
@@ -1063,7 +1059,7 @@ export function filterProviderCustomHeaders(
|
|
|
1063
1059
|
throw new GatewayError(
|
|
1064
1060
|
"invalid_request",
|
|
1065
1061
|
400,
|
|
1066
|
-
`custom header value exceeds 4KiB: ${rawName}
|
|
1062
|
+
`custom header value exceeds 4KiB: ${rawName} (request {requestId}). We love long reads, but not that long 📚`,
|
|
1067
1063
|
{ header: rawName },
|
|
1068
1064
|
);
|
|
1069
1065
|
}
|
|
@@ -1071,7 +1067,7 @@ export function filterProviderCustomHeaders(
|
|
|
1071
1067
|
throw new GatewayError(
|
|
1072
1068
|
"invalid_request",
|
|
1073
1069
|
400,
|
|
1074
|
-
`custom header value contains control characters: ${rawName}
|
|
1070
|
+
`custom header value contains control characters: ${rawName} (request {requestId}). Check the shape of what you sent — then resend 🪞`,
|
|
1075
1071
|
{ header: rawName },
|
|
1076
1072
|
);
|
|
1077
1073
|
}
|