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
package/src/security/abuse.ts
CHANGED
|
@@ -57,7 +57,7 @@ const BAN_ROUTE_KEY = "#ban";
|
|
|
57
57
|
* `banThreshold` is both the identity-wide escalation threshold and the
|
|
58
58
|
* highest count either ring must keep observable.
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
interface IpAbuseAttempt {
|
|
61
61
|
readonly identity: string;
|
|
62
62
|
readonly route: string;
|
|
63
63
|
readonly now: number;
|
|
@@ -67,7 +67,7 @@ export interface IpAbuseAttempt {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/** What one admission step decided. */
|
|
70
|
-
|
|
70
|
+
interface IpAbuseOutcome {
|
|
71
71
|
/** The identity was already banned; nothing was incremented. */
|
|
72
72
|
readonly banned: boolean;
|
|
73
73
|
/** Attempts now in this route's window, including this one. `0` when banned. */
|
|
@@ -115,13 +115,13 @@ export interface IpAbuseStore {
|
|
|
115
115
|
checkAndRecord(attempt: IpAbuseAttempt): Promise<IpAbuseOutcome>;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
interface IpAbuseProtectionServiceOptions {
|
|
119
119
|
readonly maxRequestsPerWindow?: number;
|
|
120
120
|
readonly banThreshold?: number;
|
|
121
121
|
readonly banDurationMs?: number;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
interface InMemoryIpAbuseStoreOptions {
|
|
125
125
|
readonly windowMs?: number;
|
|
126
126
|
readonly clock?: () => number;
|
|
127
127
|
/** Distinct (identity, route) keys tracked before the oldest is evicted. */
|
|
@@ -399,7 +399,7 @@ export class IpAbuseProtectionService {
|
|
|
399
399
|
readonly signal?: AbortSignal;
|
|
400
400
|
}): Promise<void> {
|
|
401
401
|
if (input.signal?.aborted) {
|
|
402
|
-
throw new GatewayError("admission_unavailable", 503, "The abuse check never ran because the request was already cancelled.", {
|
|
402
|
+
throw new GatewayError("admission_unavailable", 503, "The abuse check never ran because the request was already cancelled (request {requestId}). Send it again whenever ready 🙃", {
|
|
403
403
|
reason: "admission-unavailable",
|
|
404
404
|
});
|
|
405
405
|
}
|
|
@@ -436,7 +436,7 @@ export class IpAbuseProtectionService {
|
|
|
436
436
|
|
|
437
437
|
if (outcome.banned) {
|
|
438
438
|
metrics.cartethyia_ip_abuse_refusals_total.inc(1, { reason: "ip-banned" });
|
|
439
|
-
throw new GatewayError("
|
|
439
|
+
throw new GatewayError("rate_limited", 429, "This address is temporarily banned for too many failed requests (request {requestId}). Wait for the ban to lift, then slow down. You've maxed this window — pace yourself ⏳", {
|
|
440
440
|
// Distinguishes this from the rate-limited 429 below and from an API
|
|
441
441
|
// key's own budget. It used to be "admission-unavailable", which is
|
|
442
442
|
// the label for an *unreachable* store — so every refusal from this
|
|
@@ -454,7 +454,7 @@ export class IpAbuseProtectionService {
|
|
|
454
454
|
|
|
455
455
|
if (outcome.count > this.maxRequestsPerWindow) {
|
|
456
456
|
metrics.cartethyia_ip_abuse_refusals_total.inc(1, { reason: "ip-rate-limited" });
|
|
457
|
-
throw new GatewayError("
|
|
457
|
+
throw new GatewayError("rate_limited", 429, "This address is sending requests faster than the abuse limit allows (request {requestId}). Back off and retry after the window rolls over. You've maxed this window — pace yourself ⏳", {
|
|
458
458
|
reason: "ip-rate-limited",
|
|
459
459
|
ip,
|
|
460
460
|
route: input.route,
|
|
@@ -464,7 +464,7 @@ export class IpAbuseProtectionService {
|
|
|
464
464
|
}
|
|
465
465
|
} catch (err) {
|
|
466
466
|
if (err instanceof GatewayError) throw err;
|
|
467
|
-
throw new GatewayError("admission_unavailable", 503, "The abuse-tracking store is unreachable, so requests from this address cannot be checked. Retry shortly.", {
|
|
467
|
+
throw new GatewayError("admission_unavailable", 503, "The abuse-tracking store is unreachable, so requests from this address cannot be checked (request {requestId}). Retry shortly. The bouncer stepped out — come back shortly 🚪", {
|
|
468
468
|
reason: "admission-unavailable",
|
|
469
469
|
cause: (err as Error).message,
|
|
470
470
|
});
|
|
@@ -19,26 +19,26 @@ const REASON_META: Record<
|
|
|
19
19
|
}
|
|
20
20
|
> = {
|
|
21
21
|
"rpm-exhausted": {
|
|
22
|
-
message: "This key has used its requests-per-minute allowance. Wait for the window to roll over, or raise the key's rpm limit.",
|
|
23
|
-
code: "
|
|
22
|
+
message: "This key has used its requests-per-minute allowance (request {requestId}). Wait for the window to roll over, or raise the key's rpm limit. You've maxed this window — pace yourself ⏳",
|
|
23
|
+
code: "rate_limited",
|
|
24
24
|
status: 429,
|
|
25
25
|
metricLabel: "rpm_exhausted",
|
|
26
26
|
},
|
|
27
27
|
"daily-token-limit": {
|
|
28
|
-
message: "This key has used today's token allowance. It resets at the start of the next day, or raise the key's daily limit.",
|
|
29
|
-
code: "
|
|
28
|
+
message: "This key has used today's token allowance (request {requestId}). It resets at the start of the next day, or raise the key's daily limit. Pace yourself — it resets tomorrow ⏳",
|
|
29
|
+
code: "rate_limited",
|
|
30
30
|
status: 429,
|
|
31
31
|
metricLabel: "daily",
|
|
32
32
|
},
|
|
33
33
|
"monthly-token-limit": {
|
|
34
|
-
message: "This key has used this month's token allowance. It resets at the start of the next month, or raise the key's monthly limit.",
|
|
35
|
-
code: "
|
|
34
|
+
message: "This key has used this month's token allowance (request {requestId}). It resets at the start of the next month, or raise the key's monthly limit. Saving up for next month? It resets then ⏳",
|
|
35
|
+
code: "rate_limited",
|
|
36
36
|
status: 429,
|
|
37
37
|
metricLabel: "monthly",
|
|
38
38
|
},
|
|
39
39
|
"lifetime-token-budget": {
|
|
40
|
-
message: "This key has spent its lifetime token budget. Raise the budget, or issue a new key.",
|
|
41
|
-
code: "
|
|
40
|
+
message: "This key has spent its lifetime token budget (request {requestId}). Raise the budget, or issue a new key. The piggy bank is empty — top it up or mint a new key 🐷",
|
|
41
|
+
code: "rate_limited",
|
|
42
42
|
status: 429,
|
|
43
43
|
metricLabel: "lifetime",
|
|
44
44
|
},
|
|
@@ -47,27 +47,27 @@ const REASON_META: Record<
|
|
|
47
47
|
// max-inflight. The two used to be indistinguishable and both named the
|
|
48
48
|
// provider, sending operators to the wrong setting after a rejection. The
|
|
49
49
|
// `details` carry the limit so a client can tell which one fired.
|
|
50
|
-
message: "This API key is already serving its maximum number of concurrent requests. Retry shortly, or raise the key's concurrency limit.",
|
|
50
|
+
message: "This API key is already serving its maximum number of concurrent requests (request {requestId}). Retry shortly, or raise the key's concurrency limit. It'll free up in a moment 🌊",
|
|
51
51
|
code: "capacity_exhausted",
|
|
52
52
|
status: 429,
|
|
53
53
|
metricLabel: "concurrency",
|
|
54
54
|
},
|
|
55
55
|
"tenant-capacity-exhausted": {
|
|
56
|
-
message: "This tenant is already serving its maximum number of concurrent requests. Retry shortly, or raise the tenant's concurrency limit.",
|
|
56
|
+
message: "This tenant is already serving its maximum number of concurrent requests (request {requestId}). Retry shortly, or raise the tenant's concurrency limit. The tenant is maxed out right now — chill 🏠",
|
|
57
57
|
code: "tenant_capacity_exhausted",
|
|
58
58
|
status: 429,
|
|
59
59
|
metricLabel: "tenant_capacity",
|
|
60
60
|
},
|
|
61
61
|
"model-not-allowed": {
|
|
62
|
-
message: "This key is not allowed to use this model under its allowlist. Add the model to the key, or use one it already allows.",
|
|
63
|
-
code: "
|
|
64
|
-
status:
|
|
62
|
+
message: "This key is not allowed to use this model under its allowlist (request {requestId}). Add the model to the key, or use one it already allows. The door's locked for this one — check the allowlist 🔒",
|
|
63
|
+
code: "model_not_allowed",
|
|
64
|
+
status: 403,
|
|
65
65
|
metricLabel: "model_not_allowed",
|
|
66
66
|
},
|
|
67
67
|
"model-denied": {
|
|
68
|
-
message: "This key's denylist blocks this model. Remove the model from the denylist, or use a different model.",
|
|
69
|
-
code: "
|
|
70
|
-
status:
|
|
68
|
+
message: "This key's denylist blocks this model (request {requestId}). Remove the model from the denylist, or use a different model. This one's on the banned list — pick another 🚫",
|
|
69
|
+
code: "model_not_allowed",
|
|
70
|
+
status: 403,
|
|
71
71
|
metricLabel: "model_not_allowed",
|
|
72
72
|
},
|
|
73
73
|
"admission-unavailable": {
|
|
@@ -76,7 +76,7 @@ const REASON_META: Record<
|
|
|
76
76
|
// `invalid_counter`, `unknown_reservation`, `invalid_actual_usage`). The
|
|
77
77
|
// second is not an outage, so the text must not claim one — it names the
|
|
78
78
|
// effect the client can act on, and the diagnostic stays in `details.reason`.
|
|
79
|
-
message: "The gateway could not admit this request. Retry shortly; if it persists, check the instance's Redis or coordination backend.",
|
|
79
|
+
message: "The gateway could not admit this request (request {requestId}). Retry shortly; if it persists, check the instance's Redis or coordination backend. The bouncer stepped out — come back shortly 🚪",
|
|
80
80
|
code: "admission_unavailable",
|
|
81
81
|
status: 503,
|
|
82
82
|
metricLabel: "admission_unavailable",
|
|
@@ -12,7 +12,7 @@ import { admissionMetricLabel, reasonToGatewayError } from "./reasons";
|
|
|
12
12
|
|
|
13
13
|
/** Persists reconciled lifetime token consumption for an API key. Injected so
|
|
14
14
|
* the in-memory admission service stays decoupled from Drizzle/Postgres. */
|
|
15
|
-
|
|
15
|
+
type LifetimeUsagePersister = (input: {
|
|
16
16
|
apiKeyId: string;
|
|
17
17
|
delta: number;
|
|
18
18
|
}) => Promise<void>;
|
|
@@ -23,7 +23,7 @@ export type LifetimeUsagePersister = (input: {
|
|
|
23
23
|
* counter store only when it is about to seed a missing lifetime counter, so
|
|
24
24
|
* the ≤3s-stale snapshot value cannot freeze a low baseline in place. Injected
|
|
25
25
|
* so the admission module stays decoupled from Drizzle/Postgres. */
|
|
26
|
-
|
|
26
|
+
type FreshLifetimeConsumedReader = (apiKeyId: string) => Promise<number | undefined>;
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
export class ApiKeyAdmissionService {
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lease and counter lifetimes, and the calendar bucket keys they key on.
|
|
3
|
-
*
|
|
4
|
-
* These live together because they are one arithmetic problem: a counter
|
|
5
|
-
* must outlive the lease that charges it, and a lease key must outlive the
|
|
6
|
-
* horizon the sweeper reads. Splitting them across files is how those
|
|
7
|
-
* invariants silently break, so the reasoning stays in one place.
|
|
8
|
-
*/
|
|
9
|
-
export const LEASE_TTL_MS = 3600_000;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The lease key's own Redis TTL is deliberately LONGER than the `expires_at`
|
|
13
|
-
* horizon the lease hash records.
|
|
14
|
-
*
|
|
15
|
-
* `expires_at` is the moment the sweeper becomes allowed to reclaim the lease.
|
|
16
|
-
* If the key TTL equalled that horizon, Redis would evict the hash at exactly
|
|
17
|
-
* the moment `reapIfExpired` wanted to read it: `HGET state` would return nil,
|
|
18
|
-
* the sweep would skip it, and a crash between reserve and release would strand
|
|
19
|
-
* the held concurrency / tenant-concurrency slot for as long as the counter
|
|
20
|
-
* keeps being refreshed — every later reserve re-arms the counter's own TTL, so
|
|
21
|
-
* a key still in use never recovers its leaked slot. The grace window keeps the
|
|
22
|
-
* hash readable for several sweep intervals past the horizon, so the recovery
|
|
23
|
-
* path actually runs.
|
|
24
|
-
*
|
|
25
|
-
* Exported so the invariant is asserted directly: a lease key must outlive the
|
|
26
|
-
* horizon it records by more than one sweep interval (`sweepLeases` runs every
|
|
27
|
-
* 30s, wired in `runtime/dependencies.ts`).
|
|
28
|
-
*/
|
|
29
|
-
const LEASE_TTL_GRACE_SECONDS = 120;
|
|
30
|
-
/** Seconds after which a lease hash is reapable (`expires_at` horizon). */
|
|
31
|
-
export const LEASE_REAP_HORIZON_SECONDS = Math.ceil(LEASE_TTL_MS / 1000);
|
|
32
|
-
/** Seconds the lease hash itself lives; must exceed the reap horizon. */
|
|
33
|
-
export const LEASE_KEY_TTL_SECONDS = LEASE_REAP_HORIZON_SECONDS + LEASE_TTL_GRACE_SECONDS;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* TTLs for the token-budget counters, in seconds.
|
|
37
|
-
*
|
|
38
|
-
* Daily lives just past two days (a day bucket plus slack to cover a request
|
|
39
|
-
* straddling midnight); monthly and lifetime share one 35-day window — long
|
|
40
|
-
* enough that a month bucket is never evicted mid-month, and it is the *only*
|
|
41
|
-
* thing bounding `admission:lifetime:*`, which has no natural bucket to expire
|
|
42
|
-
* on. Every writer below must therefore carry its own TTL: a plain `SET`
|
|
43
|
-
* without one creates a key that outlives every bucket it feeds.
|
|
44
|
-
*/
|
|
45
|
-
export const DAILY_COUNTER_TTL_SECONDS = 172800;
|
|
46
|
-
export const MONTHLY_COUNTER_TTL_SECONDS = 3024000;
|
|
47
|
-
export const LIFETIME_COUNTER_TTL_SECONDS = 3024000;
|
|
48
|
-
/** RPM window plus slack; concurrency slots are held for the request duration. */
|
|
49
|
-
export const RPM_WINDOW_TTL_SECONDS = 65;
|
|
50
|
-
// Must exceed the lease's own lifetime (`LEASE_TTL_MS` + grace), otherwise a
|
|
51
|
-
// request held longer than this lets its concurrency counter expire underneath
|
|
52
|
-
// a still-live lease and a later reserve INCRs from zero. Derived rather than
|
|
53
|
-
// hard-coded so raising the lease TTL cannot silently reopen the hole.
|
|
54
|
-
export const CONCURRENCY_TTL_SECONDS = LEASE_KEY_TTL_SECONDS;
|
|
55
|
-
|
|
56
|
-
export function dailyBucket(now: number): string {
|
|
57
|
-
return new Date(now).toISOString().slice(0, 10);
|
|
58
|
-
}
|
|
59
|
-
export function monthlyBucket(now: number): string {
|
|
60
|
-
return new Date(now).toISOString().slice(0, 7);
|
|
61
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Lease and counter lifetimes, and the calendar bucket keys they key on.
|
|
3
|
+
*
|
|
4
|
+
* These live together because they are one arithmetic problem: a counter
|
|
5
|
+
* must outlive the lease that charges it, and a lease key must outlive the
|
|
6
|
+
* horizon the sweeper reads. Splitting them across files is how those
|
|
7
|
+
* invariants silently break, so the reasoning stays in one place.
|
|
8
|
+
*/
|
|
9
|
+
export const LEASE_TTL_MS = 3600_000;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The lease key's own Redis TTL is deliberately LONGER than the `expires_at`
|
|
13
|
+
* horizon the lease hash records.
|
|
14
|
+
*
|
|
15
|
+
* `expires_at` is the moment the sweeper becomes allowed to reclaim the lease.
|
|
16
|
+
* If the key TTL equalled that horizon, Redis would evict the hash at exactly
|
|
17
|
+
* the moment `reapIfExpired` wanted to read it: `HGET state` would return nil,
|
|
18
|
+
* the sweep would skip it, and a crash between reserve and release would strand
|
|
19
|
+
* the held concurrency / tenant-concurrency slot for as long as the counter
|
|
20
|
+
* keeps being refreshed — every later reserve re-arms the counter's own TTL, so
|
|
21
|
+
* a key still in use never recovers its leaked slot. The grace window keeps the
|
|
22
|
+
* hash readable for several sweep intervals past the horizon, so the recovery
|
|
23
|
+
* path actually runs.
|
|
24
|
+
*
|
|
25
|
+
* Exported so the invariant is asserted directly: a lease key must outlive the
|
|
26
|
+
* horizon it records by more than one sweep interval (`sweepLeases` runs every
|
|
27
|
+
* 30s, wired in `runtime/dependencies.ts`).
|
|
28
|
+
*/
|
|
29
|
+
const LEASE_TTL_GRACE_SECONDS = 120;
|
|
30
|
+
/** Seconds after which a lease hash is reapable (`expires_at` horizon). */
|
|
31
|
+
export const LEASE_REAP_HORIZON_SECONDS = Math.ceil(LEASE_TTL_MS / 1000);
|
|
32
|
+
/** Seconds the lease hash itself lives; must exceed the reap horizon. */
|
|
33
|
+
export const LEASE_KEY_TTL_SECONDS = LEASE_REAP_HORIZON_SECONDS + LEASE_TTL_GRACE_SECONDS;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* TTLs for the token-budget counters, in seconds.
|
|
37
|
+
*
|
|
38
|
+
* Daily lives just past two days (a day bucket plus slack to cover a request
|
|
39
|
+
* straddling midnight); monthly and lifetime share one 35-day window — long
|
|
40
|
+
* enough that a month bucket is never evicted mid-month, and it is the *only*
|
|
41
|
+
* thing bounding `admission:lifetime:*`, which has no natural bucket to expire
|
|
42
|
+
* on. Every writer below must therefore carry its own TTL: a plain `SET`
|
|
43
|
+
* without one creates a key that outlives every bucket it feeds.
|
|
44
|
+
*/
|
|
45
|
+
export const DAILY_COUNTER_TTL_SECONDS = 172800;
|
|
46
|
+
export const MONTHLY_COUNTER_TTL_SECONDS = 3024000;
|
|
47
|
+
export const LIFETIME_COUNTER_TTL_SECONDS = 3024000;
|
|
48
|
+
/** RPM window plus slack; concurrency slots are held for the request duration. */
|
|
49
|
+
export const RPM_WINDOW_TTL_SECONDS = 65;
|
|
50
|
+
// Must exceed the lease's own lifetime (`LEASE_TTL_MS` + grace), otherwise a
|
|
51
|
+
// request held longer than this lets its concurrency counter expire underneath
|
|
52
|
+
// a still-live lease and a later reserve INCRs from zero. Derived rather than
|
|
53
|
+
// hard-coded so raising the lease TTL cannot silently reopen the hole.
|
|
54
|
+
export const CONCURRENCY_TTL_SECONDS = LEASE_KEY_TTL_SECONDS;
|
|
55
|
+
|
|
56
|
+
export function dailyBucket(now: number): string {
|
|
57
|
+
return new Date(now).toISOString().slice(0, 10);
|
|
58
|
+
}
|
|
59
|
+
export function monthlyBucket(now: number): string {
|
|
60
|
+
return new Date(now).toISOString().slice(0, 7);
|
|
61
|
+
}
|
|
@@ -118,7 +118,7 @@ function freezeList(
|
|
|
118
118
|
return arr;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
function freezeSnapshot(s: ApiKeyAuthorizationSnapshot): ApiKeyAuthorizationSnapshot {
|
|
122
122
|
const model_list = freezeList(s.model_list);
|
|
123
123
|
const model_access_mode: ApiKeyModelAccessMode =
|
|
124
124
|
s.model_access_mode === "blacklist" ? "blacklist" : "whitelist";
|
|
@@ -239,7 +239,7 @@ export interface ResolvedApiKey {
|
|
|
239
239
|
* indistinguishable — distinguishing them would let a caller confirm that a
|
|
240
240
|
* guessed token once existed — so there is no variant carrying a reason.
|
|
241
241
|
*/
|
|
242
|
-
const INVALID_KEY_MESSAGE = "Invalid API key. Check the key you are sending, or create a new one in API Keys and send it as: Authorization: Bearer <key>.";
|
|
242
|
+
const INVALID_KEY_MESSAGE = "Invalid API key (request {requestId}). Check the key you are sending, or create a new one in API Keys and send it as: Authorization: Bearer <key>. Wrong key, right door — double-check and come back 🔑";
|
|
243
243
|
|
|
244
244
|
export function unknownApiKeyError(): GatewayError {
|
|
245
245
|
return new GatewayError("authentication_failed", 401, INVALID_KEY_MESSAGE);
|
|
@@ -258,7 +258,7 @@ export function modelsListApiKeyError(): GatewayError {
|
|
|
258
258
|
return new GatewayError(
|
|
259
259
|
"authentication_failed",
|
|
260
260
|
401,
|
|
261
|
-
"Invalid API key. Rotate the default Cartethyia API key in API Keys and send the new key as: Authorization: Bearer <key>.",
|
|
261
|
+
"Invalid API key (request {requestId}). Rotate the default Cartethyia API key in API Keys and send the new key as: Authorization: Bearer <key>. Wrong key, right door — rotate and try again 🔑",
|
|
262
262
|
);
|
|
263
263
|
}
|
|
264
264
|
|
|
@@ -279,12 +279,12 @@ export function requestToken(headers: Headers | Record<string, string>): string
|
|
|
279
279
|
// *different* credentials stay a conflict — accepting whichever came first
|
|
280
280
|
// would silently decide the request's identity.
|
|
281
281
|
if (bearerMatch?.[1] === apiKey) return apiKey;
|
|
282
|
-
throw new GatewayError("invalid_request", 400, "The request sends two different credentials. Send exactly one key, either as Authorization: Bearer <key> or as the api-key header.");
|
|
282
|
+
throw new GatewayError("invalid_request", 400, "The request sends two different credentials (request {requestId}). Send exactly one key, either as Authorization: Bearer <key> or as the api-key header. Pick one key and stick with it 🤝");
|
|
283
283
|
}
|
|
284
284
|
if (hasApiKey) return apiKey;
|
|
285
285
|
const token = bearerMatch?.[1];
|
|
286
286
|
if (token === undefined)
|
|
287
|
-
throw new GatewayError("
|
|
287
|
+
throw new GatewayError("authentication_failed", 401, "The request carries no usable credential (request {requestId}). Send the key as: Authorization: Bearer <key>. We need a key to let you in 🔑");
|
|
288
288
|
return token;
|
|
289
289
|
}
|
|
290
290
|
|
|
@@ -363,6 +363,12 @@ export async function resolveApiKeyAuthorization(
|
|
|
363
363
|
// keep the issued row values forever. Reading the parent here makes denylist /
|
|
364
364
|
// one-time / recurring edits apply on the next cache miss.
|
|
365
365
|
const policy = parent ?? row;
|
|
366
|
+
// Share-template concurrency scope decides whether the family shares one
|
|
367
|
+
// admission counter (default `family`) or each recipient has its own
|
|
368
|
+
// (`per_key`). In `family` mode the child participates in the parent's
|
|
369
|
+
// counter namespace; in `per_key` mode the child is its own admission
|
|
370
|
+
// identity and carries its own cap.
|
|
371
|
+
const perKeyConcurrency = parent !== undefined && row.concurrencyScope === "per_key";
|
|
366
372
|
// Only a `share` parent can have children, and children are the only thing
|
|
367
373
|
// the SUM below can find. Every other key ran an aggregate that was certain
|
|
368
374
|
// to return 0 — one extra Postgres round-trip on the auth path for nothing.
|
|
@@ -381,8 +387,10 @@ export async function resolveApiKeyAuthorization(
|
|
|
381
387
|
api_key_id: row.id,
|
|
382
388
|
tenant_id: row.tenantId,
|
|
383
389
|
// Family quota / concurrency share one admission counter namespace so two
|
|
384
|
-
// recipients cannot each burn a full lifetime/daily budget.
|
|
385
|
-
|
|
390
|
+
// recipients cannot each burn a full lifetime/daily budget. In `per_key`
|
|
391
|
+
// concurrency mode the child is its own admission identity so its cap and
|
|
392
|
+
// counter are independent.
|
|
393
|
+
...(parent && !perKeyConcurrency ? { admission_identity: parent.id } : {}),
|
|
386
394
|
...(policy.modelList ? { model_list: policy.modelList as string[] } : {}),
|
|
387
395
|
model_access_mode: policy.modelAccessMode ?? "whitelist",
|
|
388
396
|
...(policy.clientRouterDenylist
|
|
@@ -393,7 +401,11 @@ export async function resolveApiKeyAuthorization(
|
|
|
393
401
|
...(policy.monthlyTokenLimit != null ? { monthly_tokens: policy.monthlyTokenLimit } : {}),
|
|
394
402
|
...(policy.lifetimeTokenBudget != null ? { lifetime_token_budget: policy.lifetimeTokenBudget } : {}),
|
|
395
403
|
lifetime_tokens_consumed: consumed,
|
|
396
|
-
|
|
404
|
+
// `per_key` mode: the child carries its own cap (copied at issue); family
|
|
405
|
+
// mode inherits the parent's cap.
|
|
406
|
+
...((perKeyConcurrency ? row.maxConcurrentRequests : policy.maxConcurrentRequests) != null
|
|
407
|
+
? { max_concurrent: perKeyConcurrency ? row.maxConcurrentRequests : policy.maxConcurrentRequests }
|
|
408
|
+
: {}),
|
|
397
409
|
scopes,
|
|
398
410
|
});
|
|
399
411
|
|
|
@@ -21,7 +21,7 @@ import { getHeader as readRecordHeader } from "../protocol/canonical-model";
|
|
|
21
21
|
* `id` is the stable token an operator stores in a key's denylist, so it is
|
|
22
22
|
* part of the persisted contract and must not be renamed casually.
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
interface ClientRouterDefinition {
|
|
25
25
|
readonly id: string;
|
|
26
26
|
/** Human label for the dashboard. */
|
|
27
27
|
readonly label: string;
|
|
@@ -40,7 +40,7 @@ export interface ClientRouterDefinition {
|
|
|
40
40
|
* safe for a uniquely named header; a header whose value distinguishes it must
|
|
41
41
|
* name a matcher instead.
|
|
42
42
|
*/
|
|
43
|
-
|
|
43
|
+
interface ClientRouterSignal {
|
|
44
44
|
/** Header name, lowercase. */
|
|
45
45
|
readonly field: string;
|
|
46
46
|
/** Human description shown when the signal matches. */
|
|
@@ -114,14 +114,14 @@ export const CLIENT_ROUTERS: readonly ClientRouterDefinition[] = [
|
|
|
114
114
|
const LEGACY_CLIENT_ROUTER_IDS: Readonly<Record<string, string>> = { omniroute: "9router" };
|
|
115
115
|
|
|
116
116
|
/** A signal that matched, with the product it belongs to. */
|
|
117
|
-
|
|
117
|
+
interface MatchedClientSignal {
|
|
118
118
|
readonly field: string;
|
|
119
119
|
readonly description: string;
|
|
120
120
|
/** The observed value, capped, for the operator's audit trail. */
|
|
121
121
|
readonly value: string;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
interface ClientRouterMatch {
|
|
125
125
|
readonly routerId: string;
|
|
126
126
|
readonly label: string;
|
|
127
127
|
readonly signals: readonly MatchedClientSignal[];
|
|
@@ -22,7 +22,7 @@ import { isRecord } from "../protocol/codecs";
|
|
|
22
22
|
const TEXT_DECODER = new TextDecoder();
|
|
23
23
|
|
|
24
24
|
/** Decodes a stored secret: UTF-8 bytes or a string, trimmed. */
|
|
25
|
-
|
|
25
|
+
function decodeSecretText(secret: Uint8Array | string): string {
|
|
26
26
|
const raw = typeof secret === "string" ? secret : TEXT_DECODER.decode(secret);
|
|
27
27
|
return raw.trim();
|
|
28
28
|
}
|
|
@@ -48,7 +48,7 @@ function fieldString(record: Record<string, unknown>, key: string): string {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/** `{accessToken, orgId?}` — the Kilo Code envelope (org scoping is optional). */
|
|
51
|
-
|
|
51
|
+
interface KiloEnvelope {
|
|
52
52
|
readonly accessToken: string;
|
|
53
53
|
/** Organization the gateway scopes requests to; absent when the profile omits one. */
|
|
54
54
|
readonly orgId?: string;
|
|
@@ -79,7 +79,7 @@ export function decodeKiloCredential(secret: Uint8Array | string): KiloEnvelope
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/** `{oauthAccessToken, apiKey}` — the Muse Code (Meta) envelope. */
|
|
82
|
-
|
|
82
|
+
interface MuseEnvelope {
|
|
83
83
|
readonly oauthAccessToken: string;
|
|
84
84
|
readonly apiKey: string;
|
|
85
85
|
}
|
package/src/security/csrf.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { isRecord } from "../protocol/codecs";
|
|
|
16
16
|
* but cannot read the cookie value, so it cannot forge the header.
|
|
17
17
|
*/
|
|
18
18
|
export const CSRF_COOKIE_NAME = "csrf_token";
|
|
19
|
-
|
|
19
|
+
const CSRF_HEADER_NAME = "x-csrf-token";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Name of the session cookie whose presence gates CSRF validation in
|
|
@@ -30,7 +30,7 @@ export const SESSION_COOKIE_NAME = "session_token";
|
|
|
30
30
|
* `SessionCookiePolicy` structurally satisfies this (extra fields are fine),
|
|
31
31
|
* so call sites keep passing their existing policy object unchanged.
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
interface CsrfCookiePolicy {
|
|
34
34
|
readonly maxAge: number;
|
|
35
35
|
readonly sameSite: "Lax" | "Strict" | "None";
|
|
36
36
|
readonly secure: boolean;
|