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,5 +1,5 @@
|
|
|
1
1
|
import { asCookie, clearCsrfCookie, newCsrfToken, setCsrfCookie } from "../../security/csrf";
|
|
2
|
-
import { Elysia, t } from "elysia";
|
|
2
|
+
import { Elysia, t, type AnyElysia } from "elysia";
|
|
3
3
|
import { eq } from "drizzle-orm";
|
|
4
4
|
import { isRecord } from "../../protocol/codecs";
|
|
5
5
|
import { userRecord, returningRows, assertMutationApplied } from "./row-coercion";
|
|
@@ -13,12 +13,13 @@ import type { FirstBootSetupService } from "./setup-service";
|
|
|
13
13
|
import { isTrustedProxyPeer, resolveClientIdentity } from "../../security/ip-boundary";
|
|
14
14
|
import type { TrustedProxyBoundary } from "../../config/network";
|
|
15
15
|
import { readSessionCookie } from "./session-resolver";
|
|
16
|
-
|
|
16
|
+
import { asConsoleDb } from "../shared/transaction";
|
|
17
|
+
interface LoginRequest {
|
|
17
18
|
readonly username: string;
|
|
18
19
|
readonly password: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
interface LoginResponse {
|
|
22
23
|
readonly status: "success" | "failed";
|
|
23
24
|
/** Session secrets are cookie-only and are never returned in JSON. */
|
|
24
25
|
readonly session_id?: never;
|
|
@@ -47,13 +48,13 @@ export type SessionStatusResponse =
|
|
|
47
48
|
}
|
|
48
49
|
| { readonly status: "unauthenticated" };
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
interface SetupRequest {
|
|
51
52
|
readonly password: string;
|
|
52
53
|
readonly username?: string;
|
|
53
54
|
readonly display_name?: string;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
interface SetupResponse {
|
|
57
58
|
readonly status: "success" | "failed";
|
|
58
59
|
readonly message?: string;
|
|
59
60
|
}
|
|
@@ -65,7 +66,7 @@ export interface SetupResponse {
|
|
|
65
66
|
* report that instead of only "it worked".
|
|
66
67
|
*/
|
|
67
68
|
/** A backup's cover, so the wizard can show what a file holds before restoring. */
|
|
68
|
-
|
|
69
|
+
interface SetupInspectResponse extends SetupResponse {
|
|
69
70
|
readonly format?: "native" | "nine_router";
|
|
70
71
|
readonly version?: number;
|
|
71
72
|
readonly exportedAt?: string;
|
|
@@ -79,18 +80,18 @@ export interface SetupInspectResponse extends SetupResponse {
|
|
|
79
80
|
readonly warnings?: readonly { readonly table?: string; readonly column?: string; readonly reason: string }[];
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
interface SetupRestoreResponse extends SetupResponse {
|
|
83
84
|
readonly restored?: Record<string, number>;
|
|
84
85
|
readonly warnings?: readonly { readonly table?: string; readonly column?: string; readonly reason: string }[];
|
|
85
86
|
/** The administrator this install was given, since the file carries none. */
|
|
86
87
|
readonly createdAdmin?: boolean;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
interface LogoutResponse {
|
|
90
91
|
readonly status: "success";
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
interface ChangePasswordResponse {
|
|
94
95
|
readonly status: "success" | "failed";
|
|
95
96
|
readonly message?: string;
|
|
96
97
|
}
|
|
@@ -183,6 +184,31 @@ async function lockedOutMessage(
|
|
|
183
184
|
};
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Records one failed login and answers with the 429/401 result, including the
|
|
189
|
+
* `retry-after` header when the failure took the lock. Shared by the unknown /
|
|
190
|
+
* inactive-user and wrong-password branches, which differ only in the audit
|
|
191
|
+
* reason string.
|
|
192
|
+
*/
|
|
193
|
+
async function rejectLogin(
|
|
194
|
+
lockoutService: ConsoleLockoutService,
|
|
195
|
+
lockoutKey: string,
|
|
196
|
+
reason: string,
|
|
197
|
+
set: { status?: number | string; headers: Record<string, string | number | string[]> },
|
|
198
|
+
): Promise<LoginResponse> {
|
|
199
|
+
const isBanned = await lockoutService.recordFailure(lockoutKey, reason);
|
|
200
|
+
if (isBanned) {
|
|
201
|
+
set.status = 429;
|
|
202
|
+
set.headers["retry-after"] = String(await lockoutService.remainingLockSeconds(lockoutKey));
|
|
203
|
+
return lockedOutMessage(lockoutService, lockoutKey);
|
|
204
|
+
}
|
|
205
|
+
set.status = 401;
|
|
206
|
+
return {
|
|
207
|
+
status: "failed",
|
|
208
|
+
message: await failedLoginMessage(lockoutService, lockoutKey),
|
|
209
|
+
} satisfies LoginResponse;
|
|
210
|
+
}
|
|
211
|
+
|
|
186
212
|
function parseLoginRequest(body: unknown): LoginRequest | undefined {
|
|
187
213
|
if (!isRecord(body) || typeof body.password !== "string") {
|
|
188
214
|
return undefined;
|
|
@@ -217,7 +243,7 @@ export function createConsoleAuthRoutes(
|
|
|
217
243
|
cookiePolicy: SessionCookiePolicy = defaultSessionCookiePolicy,
|
|
218
244
|
trustedProxyBoundary: TrustedProxyBoundary = { mode: "disabled" },
|
|
219
245
|
resolvePeerAddress: (request: Request) => string | undefined = () => undefined,
|
|
220
|
-
):
|
|
246
|
+
): AnyElysia {
|
|
221
247
|
const app = new Elysia({ prefix: "/auth" });
|
|
222
248
|
const policy = cookiePolicy;
|
|
223
249
|
const database = db;
|
|
@@ -266,32 +292,12 @@ export function createConsoleAuthRoutes(
|
|
|
266
292
|
const rows = await readUsers(database, input.username);
|
|
267
293
|
const user = userRecord(rows[0]);
|
|
268
294
|
if (!user || !user.isActive) {
|
|
269
|
-
|
|
270
|
-
if (isBanned) {
|
|
271
|
-
set.status = 429;
|
|
272
|
-
set.headers["retry-after"] = String(await lockoutService.remainingLockSeconds(lockoutKey));
|
|
273
|
-
return lockedOutMessage(lockoutService, lockoutKey);
|
|
274
|
-
}
|
|
275
|
-
set.status = 401;
|
|
276
|
-
return {
|
|
277
|
-
status: "failed",
|
|
278
|
-
message: await failedLoginMessage(lockoutService, lockoutKey),
|
|
279
|
-
} satisfies LoginResponse;
|
|
295
|
+
return rejectLogin(lockoutService, lockoutKey, "unknown_user", set);
|
|
280
296
|
}
|
|
281
297
|
|
|
282
298
|
const valid = await credentialService.verifyPassword(input.password, user.passwordHash);
|
|
283
299
|
if (!valid) {
|
|
284
|
-
|
|
285
|
-
if (isBanned) {
|
|
286
|
-
set.status = 429;
|
|
287
|
-
set.headers["retry-after"] = String(await lockoutService.remainingLockSeconds(lockoutKey));
|
|
288
|
-
return lockedOutMessage(lockoutService, lockoutKey);
|
|
289
|
-
}
|
|
290
|
-
set.status = 401;
|
|
291
|
-
return {
|
|
292
|
-
status: "failed",
|
|
293
|
-
message: await failedLoginMessage(lockoutService, lockoutKey),
|
|
294
|
-
} satisfies LoginResponse;
|
|
300
|
+
return rejectLogin(lockoutService, lockoutKey, "invalid_password", set);
|
|
295
301
|
}
|
|
296
302
|
|
|
297
303
|
await lockoutService.clearFailures(lockoutKey);
|
|
@@ -526,11 +532,11 @@ export function createConsoleAuthRoutes(
|
|
|
526
532
|
});
|
|
527
533
|
return invalidated;
|
|
528
534
|
};
|
|
529
|
-
await database.transaction((tx) => rotateInScope(tx
|
|
535
|
+
await database.transaction((tx) => rotateInScope(asConsoleDb(tx)));
|
|
530
536
|
set.status = 200;
|
|
531
537
|
return { status: "success" } satisfies ChangePasswordResponse;
|
|
532
538
|
},
|
|
533
539
|
);
|
|
534
540
|
|
|
535
|
-
return app
|
|
541
|
+
return app;
|
|
536
542
|
}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import { resolveDefaultGatewayApiKey } from "../../config/runtime";
|
|
21
21
|
import { TENANT_KEY_SCOPES } from "../../security/access-control";
|
|
22
22
|
import { isRecord } from "../../protocol/codecs";
|
|
23
|
+
import { asConsoleDb } from "../shared/transaction";
|
|
23
24
|
import { BackupService } from "../backup/service";
|
|
24
25
|
import type { BackupInspection } from "../backup/service";
|
|
25
26
|
import type { RestoreWarning } from "../backup/contracts";
|
|
@@ -28,14 +29,14 @@ import type { RestoreWarning } from "../backup/contracts";
|
|
|
28
29
|
// and the mode is read from the environment the process was started with.
|
|
29
30
|
|
|
30
31
|
/** The administrator to create when a restored backup left none to sign in with. */
|
|
31
|
-
|
|
32
|
+
interface AdminBootstrap {
|
|
32
33
|
readonly password: string;
|
|
33
34
|
readonly username?: string;
|
|
34
35
|
readonly displayName?: string;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/** What a first-boot restore did, so the console can report it honestly. */
|
|
38
|
-
|
|
39
|
+
interface RestoreBackupOutcome {
|
|
39
40
|
readonly restored: Record<string, number>;
|
|
40
41
|
readonly skipped: Record<string, number>;
|
|
41
42
|
/** Parts of the file that could not be restored. */
|
|
@@ -171,7 +172,7 @@ export class FirstBootSetupService {
|
|
|
171
172
|
// unserialized lets two boots both pass the check.
|
|
172
173
|
await this.db.transaction(async (transaction) => {
|
|
173
174
|
await transaction.execute(firstBootSetupLock());
|
|
174
|
-
await create(transaction
|
|
175
|
+
await create(asConsoleDb(transaction));
|
|
175
176
|
});
|
|
176
177
|
}
|
|
177
178
|
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
|
|
7
7
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
class ConsoleUserStore {
|
|
10
10
|
constructor(private readonly db: CartethyiaDatabase) {}
|
|
11
11
|
|
|
12
12
|
async findByUsername(username: string): Promise<unknown | undefined> {
|
|
@@ -106,9 +106,6 @@ export const PROXY_KINDS: Readonly<Record<string, "http" | "https" | "socks5">>
|
|
|
106
106
|
workers: "https",
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
/** Proxy types we recognize but cannot represent, with the reason. */
|
|
110
|
-
export const REFUSED_PROXY_KINDS: Readonly<Record<string, string>> = {};
|
|
111
|
-
|
|
112
109
|
/**
|
|
113
110
|
* Router combo strategy → ours.
|
|
114
111
|
*
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
poolRoutingSettings,
|
|
34
34
|
providerAccounts,
|
|
35
35
|
providerOauthStates,
|
|
36
|
+
providerPoolAccounts,
|
|
36
37
|
providerRoutingSettings,
|
|
37
38
|
providers,
|
|
38
39
|
shareLinks,
|
|
@@ -96,6 +97,7 @@ export const CONFIG_TABLES = [
|
|
|
96
97
|
models,
|
|
97
98
|
providerRoutingSettings,
|
|
98
99
|
providerAccounts,
|
|
100
|
+
providerPoolAccounts,
|
|
99
101
|
poolRoutingSettings,
|
|
100
102
|
networkPools,
|
|
101
103
|
providers,
|
|
@@ -168,9 +170,15 @@ const SEALED_COLUMNS: Readonly<Record<string, Readonly<Record<string, string>>>>
|
|
|
168
170
|
share_links: { token_encrypted: "share_token" },
|
|
169
171
|
};
|
|
170
172
|
|
|
173
|
+
const SEALED_COLUMNS_CACHE = new WeakMap<Table, ReadonlySet<string>>();
|
|
174
|
+
|
|
171
175
|
/** The sealed columns of `table`, by column name. */
|
|
172
176
|
export function sealedColumns(table: Table): ReadonlySet<string> {
|
|
173
|
-
|
|
177
|
+
const cached = SEALED_COLUMNS_CACHE.get(table);
|
|
178
|
+
if (cached) return cached;
|
|
179
|
+
const names = new Set(Object.keys(SEALED_COLUMNS[getTableName(table)] ?? {}));
|
|
180
|
+
SEALED_COLUMNS_CACHE.set(table, names);
|
|
181
|
+
return names;
|
|
174
182
|
}
|
|
175
183
|
|
|
176
184
|
/**
|
|
@@ -203,7 +211,6 @@ export function sealedAliases(table: Table): ReadonlyMap<string, string> {
|
|
|
203
211
|
return out;
|
|
204
212
|
}
|
|
205
213
|
|
|
206
|
-
|
|
207
214
|
/**
|
|
208
215
|
* Which rows of a table belong to one tenant, and how a restore replaces them.
|
|
209
216
|
*
|
|
@@ -234,7 +241,7 @@ export function sealedAliases(table: Table): ReadonlyMap<string, string> {
|
|
|
234
241
|
* - `self` — the tenant row itself, matched on its own id, upserted and never
|
|
235
242
|
* deleted.
|
|
236
243
|
*/
|
|
237
|
-
|
|
244
|
+
type TableOwnership =
|
|
238
245
|
| { readonly kind: "direct"; readonly column: Column; readonly replace: "delete" | "upsert" }
|
|
239
246
|
| {
|
|
240
247
|
readonly kind: "viaParent";
|
|
@@ -491,7 +498,7 @@ export function findTable(section: BackupSection, name: string): Table | undefin
|
|
|
491
498
|
return tablesForSection(section).find((table) => tableName(table) === name);
|
|
492
499
|
}
|
|
493
500
|
|
|
494
|
-
export interface
|
|
501
|
+
export interface BackupPayloadExport {
|
|
495
502
|
readonly payload: BackupPayload;
|
|
496
503
|
readonly counts: Record<string, number>;
|
|
497
504
|
}
|
|
@@ -509,11 +516,6 @@ export interface ValidatedTable {
|
|
|
509
516
|
readonly rows: readonly BackupRow[];
|
|
510
517
|
}
|
|
511
518
|
|
|
512
|
-
export interface RestoreResult {
|
|
513
|
-
readonly restored: Record<string, number>;
|
|
514
|
-
readonly skipped: Record<string, number>;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
519
|
/**
|
|
518
520
|
* One part of a backup that could not be restored, and why.
|
|
519
521
|
*
|
|
@@ -23,11 +23,11 @@ import { homedir } from "node:os";
|
|
|
23
23
|
import { join } from "node:path";
|
|
24
24
|
import { GatewayError } from "../../protocol/gateway-error";
|
|
25
25
|
import { isLoopbackPeer, tokenMatches } from "../../transport/drain-endpoint";
|
|
26
|
-
import {
|
|
26
|
+
import { tablesForSection } from "./contracts";
|
|
27
27
|
import { compressBackupStream, exportBackupStream } from "./store";
|
|
28
28
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
interface BackupExportDeps {
|
|
31
31
|
readonly db: CartethyiaDatabase;
|
|
32
32
|
/** Shared secret; the route is registered only when this is set. */
|
|
33
33
|
readonly token: string;
|
|
@@ -37,7 +37,8 @@ export interface BackupExportDeps {
|
|
|
37
37
|
readonly resolvePeerAddress: (request: Request) => string | null;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export
|
|
40
|
+
/** The file-write result of an operator export, distinct from the payload shape `BackupPayloadExport` in `contracts.ts`. */
|
|
41
|
+
export interface BackupExportFileResult {
|
|
41
42
|
readonly path: string;
|
|
42
43
|
readonly bytes: number;
|
|
43
44
|
readonly counts: Record<string, number>;
|
|
@@ -52,20 +53,20 @@ export function createBackupExportHandler(
|
|
|
52
53
|
// came from, the second is the credential it carried — and `invalid_request`
|
|
53
54
|
// told an operator to fix a body that was never the problem.
|
|
54
55
|
if (!isLoopbackPeer(deps.resolvePeerAddress(request)))
|
|
55
|
-
throw new GatewayError("policy_rejected", 403, "backup export is only available from loopback");
|
|
56
|
+
throw new GatewayError("policy_rejected", 403, "backup export is only available from loopback (request {requestId}). This operation is only allowed from a trusted loopback connection — no hard feelings 🫂");
|
|
56
57
|
if (!tokenMatches(request.headers.get("x-drain-token"), deps.token))
|
|
57
|
-
throw new GatewayError("authentication_failed", 401, "invalid export token");
|
|
58
|
+
throw new GatewayError("authentication_failed", 401, "invalid export token (request {requestId}). Wrong key, right door — check the token 🔑");
|
|
58
59
|
|
|
59
60
|
const directory =
|
|
60
61
|
body !== null && typeof body === "object" && "directory" in body
|
|
61
62
|
? body.directory
|
|
62
63
|
: undefined;
|
|
63
64
|
if (directory !== undefined && typeof directory !== "string")
|
|
64
|
-
throw new GatewayError("invalid_request", 400, "directory must be a string");
|
|
65
|
+
throw new GatewayError("invalid_request", 400, "directory must be a string (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
65
66
|
|
|
66
67
|
const tenantId = await deps.resolveTenantId();
|
|
67
68
|
if (tenantId === null)
|
|
68
|
-
throw new GatewayError("invalid_request", 409, "no tenant exists; run setup first");
|
|
69
|
+
throw new GatewayError("invalid_request", 409, "no tenant exists; run setup first (request {requestId}). Nothing here but dust — run setup first 🌙");
|
|
69
70
|
|
|
70
71
|
// One gzip pass over the streamed document: a tenant with hundreds of
|
|
71
72
|
// thousands of rows is read page by page and only the compressed bytes
|
|
@@ -83,13 +84,13 @@ export function createBackupExportHandler(
|
|
|
83
84
|
writeFileSync(path, gzipped);
|
|
84
85
|
} catch (error) {
|
|
85
86
|
throw new GatewayError(
|
|
86
|
-
"
|
|
87
|
+
"internal_error",
|
|
87
88
|
500,
|
|
88
|
-
`could not write ${path}: ${error instanceof Error ? error.message : "unknown error"}
|
|
89
|
+
`could not write ${path}: ${error instanceof Error ? error.message : "unknown error"} (request {requestId}). Even we have bad days — retry, and quote the request id if it persists 🐻`,
|
|
89
90
|
);
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
const result:
|
|
93
|
+
const result: BackupExportFileResult = { path, bytes: gzipped.byteLength, counts };
|
|
93
94
|
return new Response(JSON.stringify(result), {
|
|
94
95
|
status: 200,
|
|
95
96
|
headers: { "content-type": "application/json", "cache-control": "no-store" },
|
|
@@ -108,5 +109,3 @@ function downloadDirectory(): string {
|
|
|
108
109
|
: join(process.env.XDG_DOWNLOAD_DIR ?? join(home, "Downloads"));
|
|
109
110
|
return candidate;
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
-
export { BACKUP_SECTIONS };
|
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
isNodeProviderId,
|
|
30
30
|
NODE_WIRE_FAMILIES,
|
|
31
31
|
PROXY_KINDS,
|
|
32
|
-
REFUSED_PROXY_KINDS,
|
|
33
32
|
UNSUPPORTED_PROVIDERS,
|
|
34
33
|
UPSTREAM_PROVIDERS,
|
|
35
34
|
} from "./9router-map";
|
|
@@ -50,7 +49,7 @@ export interface ImportReport {
|
|
|
50
49
|
readonly remapped: readonly string[];
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
interface ConversionResult {
|
|
54
53
|
readonly payload: BackupPayload;
|
|
55
54
|
readonly report: ImportReport;
|
|
56
55
|
}
|
|
@@ -479,11 +478,6 @@ export function convert9RouterBackup(input: unknown, tenantId: string): Conversi
|
|
|
479
478
|
return;
|
|
480
479
|
}
|
|
481
480
|
const rawKind = text(entry.type) ?? "http";
|
|
482
|
-
const refused = REFUSED_PROXY_KINDS[rawKind];
|
|
483
|
-
if (refused !== undefined) {
|
|
484
|
-
skipped.push(`proxy pool "${label}": type "${rawKind}" (${refused})`);
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
481
|
const kind = PROXY_KINDS[rawKind];
|
|
488
482
|
if (kind === undefined) {
|
|
489
483
|
skipped.push(`proxy pool "${label}": unknown type "${rawKind}"`);
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* The body bound is enforced here, before the JSON parse, because the whole
|
|
15
15
|
* point of the limit is to not buffer an unbounded file.
|
|
16
16
|
*/
|
|
17
|
-
import { Elysia, t } from "elysia";
|
|
17
|
+
import { Elysia, t, type AnyElysia } from "elysia";
|
|
18
18
|
import type { ConsoleAccessResolver } from "../auth/access";
|
|
19
19
|
import { invalidateApiKeyCache } from "../../security/api-key-auth";
|
|
20
20
|
import { invalidatePublicModels } from "../providers/catalog/public-model-store";
|
|
@@ -26,7 +26,7 @@ import { DELETE_ALL_SCOPES, compressBackupStream } from "./store";
|
|
|
26
26
|
import type { BackupService } from "./service";
|
|
27
27
|
import type { AuditRecorder } from "../auth/audit-recorder";
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
interface BackupRoutesConfig {
|
|
30
30
|
readonly accessResolver: ConsoleAccessResolver;
|
|
31
31
|
/**
|
|
32
32
|
* Builds the service for one request. A factory rather than an instance
|
|
@@ -118,6 +118,30 @@ function parseSections(raw: string | undefined): readonly BackupSection[] | unde
|
|
|
118
118
|
.filter((part) => part.length > 0) as readonly BackupSection[];
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Enforces the byte bound against actual received bytes, not just the declared
|
|
123
|
+
* header: a missing or spoofed content-length would otherwise bypass the 413
|
|
124
|
+
* and buffer an unbounded file into JSON.parse. Elysia has already parsed the
|
|
125
|
+
* body here, so measure the re-serialized form — an over-limit file still
|
|
126
|
+
* fails closed before any restore/inspection touches the DB. Returns the 413
|
|
127
|
+
* response when over the limit, or `null` when the body is accepted.
|
|
128
|
+
*/
|
|
129
|
+
function enforceBodyLimit(request: Request, body: unknown, set: { status?: number | string }): { error: string; code: string } | null {
|
|
130
|
+
const declared = Number(request.headers.get("content-length") ?? "0");
|
|
131
|
+
const actualBytes = JSON.stringify(body ?? null).length;
|
|
132
|
+
if (
|
|
133
|
+
(Number.isFinite(declared) && declared > MAX_BACKUP_BYTES) ||
|
|
134
|
+
actualBytes > MAX_BACKUP_BYTES
|
|
135
|
+
) {
|
|
136
|
+
set.status = 413;
|
|
137
|
+
return {
|
|
138
|
+
error: `backup exceeds the ${MAX_BACKUP_BYTES} byte limit`,
|
|
139
|
+
code: "request_too_large",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
121
145
|
/**
|
|
122
146
|
* Streams the export document into the response body as it is written; one
|
|
123
147
|
* row-page of the tenant's data is in memory at a time.
|
|
@@ -139,7 +163,7 @@ function payloadDocument(chunks: AsyncGenerator<string, void, void>): ReadableSt
|
|
|
139
163
|
});
|
|
140
164
|
}
|
|
141
165
|
|
|
142
|
-
export function createBackupRoutes(config: BackupRoutesConfig):
|
|
166
|
+
export function createBackupRoutes(config: BackupRoutesConfig): AnyElysia {
|
|
143
167
|
return new Elysia({ prefix: "/backup" })
|
|
144
168
|
.get("/export", { query: exportQuery }, async ({ request, query, set }) => {
|
|
145
169
|
try {
|
|
@@ -194,23 +218,8 @@ export function createBackupRoutes(config: BackupRoutesConfig): Elysia {
|
|
|
194
218
|
if (access.tenantId === null) {
|
|
195
219
|
throw new ConsoleDomainError("tenant_required", 403, "Tenant isolation required");
|
|
196
220
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// and buffer an unbounded file into JSON.parse. Elysia has already
|
|
200
|
-
// parsed the body here, so measure the re-serialized form — an
|
|
201
|
-
// over-limit file still fails closed before restore touches the DB.
|
|
202
|
-
const declared = Number(request.headers.get("content-length") ?? "0");
|
|
203
|
-
const actualBytes = JSON.stringify(body ?? null).length;
|
|
204
|
-
if (
|
|
205
|
-
(Number.isFinite(declared) && declared > MAX_BACKUP_BYTES) ||
|
|
206
|
-
actualBytes > MAX_BACKUP_BYTES
|
|
207
|
-
) {
|
|
208
|
-
set.status = 413;
|
|
209
|
-
return {
|
|
210
|
-
error: `backup exceeds the ${MAX_BACKUP_BYTES} byte limit`,
|
|
211
|
-
code: "request_too_large",
|
|
212
|
-
};
|
|
213
|
-
}
|
|
221
|
+
const tooLarge = enforceBodyLimit(request, body, set);
|
|
222
|
+
if (tooLarge !== null) return tooLarge;
|
|
214
223
|
// The restoring tenant comes from the caller's own access decision and
|
|
215
224
|
// from nowhere else. A request body may not name the tenant it writes
|
|
216
225
|
// into: that would let any holder of the scope import into, and
|
|
@@ -283,26 +292,13 @@ export function createBackupRoutes(config: BackupRoutesConfig): Elysia {
|
|
|
283
292
|
if (access.tenantId === null) {
|
|
284
293
|
throw new ConsoleDomainError("tenant_required", 403, "Tenant isolation required");
|
|
285
294
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
// validation could refuse it.
|
|
289
|
-
const declared = Number(request.headers.get("content-length") ?? "0");
|
|
290
|
-
const actualBytes = JSON.stringify(body ?? null).length;
|
|
291
|
-
if (
|
|
292
|
-
(Number.isFinite(declared) && declared > MAX_BACKUP_BYTES) ||
|
|
293
|
-
actualBytes > MAX_BACKUP_BYTES
|
|
294
|
-
) {
|
|
295
|
-
set.status = 413;
|
|
296
|
-
return {
|
|
297
|
-
error: `backup exceeds the ${MAX_BACKUP_BYTES} byte limit`,
|
|
298
|
-
code: "request_too_large",
|
|
299
|
-
};
|
|
300
|
-
}
|
|
295
|
+
const tooLarge = enforceBodyLimit(request, body, set);
|
|
296
|
+
if (tooLarge !== null) return tooLarge;
|
|
301
297
|
// Reads nothing and writes nothing: the checklist the restore wizard
|
|
302
298
|
// shows, so an operator can pick sections before typing a password.
|
|
303
299
|
return config.backupFor(request).inspect(body.backup);
|
|
304
300
|
} catch (error) {
|
|
305
301
|
return errorResponse(error, set, "Backup inspection failed");
|
|
306
302
|
}
|
|
307
|
-
})
|
|
303
|
+
});
|
|
308
304
|
}
|