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
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Shared IP parsing primitives: IPv4 → number, IPv6 → groups/bytes/BigInt,
|
|
2
|
+
// CIDR containment, and loopback detection.
|
|
3
|
+
//
|
|
4
|
+
// Consolidates the two independent copies that used to live in `ssrf.ts` and
|
|
5
|
+
// `security/ip-boundary.ts`. The two modules' semantics differ **on purpose**,
|
|
6
|
+
// so every shared entry point carries the divergence as an explicit opt-in
|
|
7
|
+
// rather than silently picking one behavior:
|
|
8
|
+
//
|
|
9
|
+
// - **Dotted-quad IPv6** (`::ffff:1.2.3.4`): the SSRF guard accepts it (the DNS
|
|
10
|
+
// layer can hand it one); the client-identity layer deliberately fail-closes
|
|
11
|
+
// its BigInt key path on a dotted tail so an unparseable spelling can never
|
|
12
|
+
// mint a database uniqueness key.
|
|
13
|
+
// - **CIDR**: the SSRF guard requires an explicit `/prefix` (a bare address
|
|
14
|
+
// matches nothing — fail-closed on an allowlist) and does not unwrap
|
|
15
|
+
// IPv4-mapped spellings; the trusted-proxy boundary treats a bare address as
|
|
16
|
+
// "this exact host" and unwraps mapped spellings, because Bun reports an
|
|
17
|
+
// IPv4 loopback peer as `::ffff:127.0.0.1` on Windows.
|
|
18
|
+
import { isIP } from "node:net";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* IPv4 literal as a 32-bit unsigned number, or `undefined` when `address` is
|
|
22
|
+
* not a plain dotted quad. Decimal octets only — the exotic spellings `Number`
|
|
23
|
+
* accepts (`0x`, leading `+`, octal) are refused, because this value feeds
|
|
24
|
+
* allowlist comparisons where a lenient parse would widen the match.
|
|
25
|
+
*/
|
|
26
|
+
export function ipv4ToNumber(address: string): number | undefined {
|
|
27
|
+
const parts = address.split(".");
|
|
28
|
+
if (parts.length !== 4 || parts.some((part) => !/^\d+$/.test(part))) return undefined;
|
|
29
|
+
const octets = parts.map(Number);
|
|
30
|
+
if (octets.some((part) => part < 0 || part > 255)) return undefined;
|
|
31
|
+
const [a, b, c, d] = octets;
|
|
32
|
+
if (a === undefined || b === undefined || c === undefined || d === undefined) return undefined;
|
|
33
|
+
return ((a * 256 + b) * 256 + c) * 256 + d;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Same parse as {@link ipv4ToNumber}, but a refusal is an error. The SSRF
|
|
38
|
+
* guard's internal call sites only ever reach the parser with an `isIP`-validated
|
|
39
|
+
* string, so a malformed octet there is an invariant breach, not input handling.
|
|
40
|
+
*/
|
|
41
|
+
export function ipv4ToNumberStrict(address: string): number {
|
|
42
|
+
const value = ipv4ToNumber(address);
|
|
43
|
+
if (value === undefined) throw new Error("invalid IPv4");
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An IPv6 literal as exactly eight lowercase 16-bit hex groups, or `undefined`
|
|
49
|
+
* when it is not one. Expands a `::` run (which must stand for at least one
|
|
50
|
+
* group) and — with `allowDottedQuad` — converts a trailing dotted quad into
|
|
51
|
+
* the two groups it represents.
|
|
52
|
+
*
|
|
53
|
+
* `allowDottedQuad` is the preserved SSRF/boundary divergence: the SSRF guard
|
|
54
|
+
* passes `true` (DNS answers can arrive as `::ffff:1.2.3.4`), the client-identity
|
|
55
|
+
* key path leaves the default (`false`) so a dotted spelling never reaches a
|
|
56
|
+
* database uniqueness key. The zone id (`%eth0`) is stripped; it identifies an
|
|
57
|
+
* interface, not an address.
|
|
58
|
+
*/
|
|
59
|
+
export function normalizeIpv6Groups(
|
|
60
|
+
value: string,
|
|
61
|
+
opts: { allowDottedQuad?: boolean } = {},
|
|
62
|
+
): string[] | undefined {
|
|
63
|
+
const allowDottedQuad = opts.allowDottedQuad ?? false;
|
|
64
|
+
const text = value.toLowerCase().trim().split("%", 1)[0] ?? "";
|
|
65
|
+
if (text.length === 0) return undefined;
|
|
66
|
+
const doubleIndex = text.indexOf("::");
|
|
67
|
+
// More than one `::` is invalid, as is a `::` inside a group.
|
|
68
|
+
if (doubleIndex !== text.lastIndexOf("::")) return undefined;
|
|
69
|
+
|
|
70
|
+
const headText = doubleIndex >= 0 ? text.slice(0, doubleIndex) : text;
|
|
71
|
+
const tailText = doubleIndex >= 0 ? text.slice(doubleIndex + 2) : "";
|
|
72
|
+
const head = headText.length > 0 ? headText.split(":") : [];
|
|
73
|
+
const tail = tailText.length > 0 ? tailText.split(":") : [];
|
|
74
|
+
|
|
75
|
+
// A dotted quad is a single token that fills two groups.
|
|
76
|
+
const expandDotted = (tokens: string[]): string[] | undefined => {
|
|
77
|
+
const last = tokens[tokens.length - 1];
|
|
78
|
+
if (last === undefined || !last.includes(".")) return tokens;
|
|
79
|
+
if (!allowDottedQuad) return undefined;
|
|
80
|
+
const quad = last.split(".");
|
|
81
|
+
if (quad.length !== 4) return undefined;
|
|
82
|
+
const octets = quad.map((part) => Number(part));
|
|
83
|
+
if (octets.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return undefined;
|
|
84
|
+
const [a, b, c, d] = octets;
|
|
85
|
+
if (a === undefined || b === undefined || c === undefined || d === undefined) return undefined;
|
|
86
|
+
const high = ((a << 8) | b).toString(16);
|
|
87
|
+
const low = ((c << 8) | d).toString(16);
|
|
88
|
+
return [...tokens.slice(0, -1), high, low];
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const expandedHead = expandDotted(head);
|
|
92
|
+
const expandedTail = expandDotted(tail);
|
|
93
|
+
if (expandedHead === undefined || expandedTail === undefined) return undefined;
|
|
94
|
+
|
|
95
|
+
let full: string[];
|
|
96
|
+
if (doubleIndex >= 0) {
|
|
97
|
+
const missing = 8 - expandedHead.length - expandedTail.length;
|
|
98
|
+
// `::` must stand for at least one group, so exactly-8 without it is invalid.
|
|
99
|
+
if (missing < 1) return undefined;
|
|
100
|
+
full = [...expandedHead, ...Array.from({ length: missing }, () => "0"), ...expandedTail];
|
|
101
|
+
} else {
|
|
102
|
+
full = expandedHead;
|
|
103
|
+
}
|
|
104
|
+
if (full.length !== 8) return undefined;
|
|
105
|
+
const normalized: string[] = [];
|
|
106
|
+
for (const group of full) {
|
|
107
|
+
if (!/^[0-9a-f]{1,4}$/.test(group)) return undefined;
|
|
108
|
+
normalized.push(Number.parseInt(group, 16).toString(16));
|
|
109
|
+
}
|
|
110
|
+
return normalized;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The 16 bytes of an IPv6 literal, or `undefined` when it is not one. */
|
|
114
|
+
export function ipv6GroupsToBytes(groups: readonly string[]): Uint8Array | undefined {
|
|
115
|
+
if (groups.length !== 8) return undefined;
|
|
116
|
+
const bytes = new Uint8Array(16);
|
|
117
|
+
for (let index = 0; index < 8; index += 1) {
|
|
118
|
+
const group = groups[index]!;
|
|
119
|
+
if (!/^[0-9a-f]{1,4}$/.test(group)) return undefined;
|
|
120
|
+
const n = Number.parseInt(group, 16);
|
|
121
|
+
bytes[index * 2] = n >>> 8;
|
|
122
|
+
bytes[index * 2 + 1] = n & 255;
|
|
123
|
+
}
|
|
124
|
+
return bytes;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The 128-bit value of an IPv6 literal, or `undefined` when it is not one. */
|
|
128
|
+
export function ipv6GroupsToBigInt(groups: readonly string[]): bigint | undefined {
|
|
129
|
+
if (groups.length !== 8) return undefined;
|
|
130
|
+
let result = 0n;
|
|
131
|
+
for (const group of groups) {
|
|
132
|
+
if (!/^[0-9a-f]{1,4}$/.test(group)) return undefined;
|
|
133
|
+
result = (result << 16n) | BigInt(Number.parseInt(group, 16));
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** 128-bit value of an IPv6 literal; `allowDottedQuad` opts into the dotted tail. */
|
|
139
|
+
export function ipv6ToBigInt(address: string, opts: { allowDottedQuad?: boolean } = {}): bigint | undefined {
|
|
140
|
+
const groups = normalizeIpv6Groups(address, opts);
|
|
141
|
+
return groups === undefined ? undefined : ipv6GroupsToBigInt(groups);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The 32-bit tail of an IPv4-mapped IPv6 address, or `undefined` when `value` is
|
|
146
|
+
* not one. Accepts every spelling of the same address: `::ffff:1.2.3.4`,
|
|
147
|
+
* `::ffff:102:304`, `0:0:0:0:0:ffff:102:304`, and `::ffff:0:102:304`.
|
|
148
|
+
*
|
|
149
|
+
* Works by normalizing to exactly eight 16-bit groups first. That is the only way
|
|
150
|
+
* to compare spellings: a dotted tail is written as ONE colon-separated token but
|
|
151
|
+
* occupies TWO group slots, so any check that counts `split(":")` entries gets the
|
|
152
|
+
* position of the `ffff` marker wrong.
|
|
153
|
+
*/
|
|
154
|
+
function mappedIpv4Tail(value: string): string | undefined {
|
|
155
|
+
const groups = normalizeIpv6Groups(value, { allowDottedQuad: true });
|
|
156
|
+
if (groups === undefined) return undefined;
|
|
157
|
+
// The first five groups (80 bits) must be zero, and the sixth must be `ffff`.
|
|
158
|
+
for (let index = 0; index < 5; index += 1) {
|
|
159
|
+
if (groups[index] !== "0") return undefined;
|
|
160
|
+
}
|
|
161
|
+
if (groups[5] !== "ffff") return undefined;
|
|
162
|
+
const high = groups[6];
|
|
163
|
+
const low = groups[7];
|
|
164
|
+
if (high === undefined || low === undefined) return undefined;
|
|
165
|
+
return `${high}:${low}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Unwraps an IPv4-mapped IPv6 address to plain IPv4.
|
|
170
|
+
*
|
|
171
|
+
* Bun reports an IPv4 loopback peer as `::ffff:127.0.0.1` on Windows, and
|
|
172
|
+
* `isIP` classifies it as IPv6 — so a parser that bails on a dotted quad never
|
|
173
|
+
* matched `127.0.0.1/32`. Accepts both the dotted and the hex (`::ffff:7f00:1`)
|
|
174
|
+
* spelling.
|
|
175
|
+
*/
|
|
176
|
+
export function unwrapMappedIpv4(address: string): string {
|
|
177
|
+
const value = address.toLowerCase().split("%", 1)[0] ?? "";
|
|
178
|
+
// Recognize the mapped range STRUCTURALLY, not by its compressed spelling. The
|
|
179
|
+
// old `startsWith("::ffff:")` test missed any form that writes the leading zero
|
|
180
|
+
// groups out — `0:0:0:0:0:ffff:7f00:1` is the same host as `::ffff:127.0.0.1`,
|
|
181
|
+
// but it failed the prefix test, fell through to the generic IPv6 parse, and
|
|
182
|
+
// was treated as an unrelated IPv6 address. The trust check then refused a peer
|
|
183
|
+
// the allowlist covered, and `canonicalClientIpKey` minted a second key for one
|
|
184
|
+
// host — which is stored on issued child keys for consistent usage reporting
|
|
185
|
+
// and audit.
|
|
186
|
+
//
|
|
187
|
+
// A mapped address is `::ffff:<32 bits>`, i.e. the first 80 bits are zero and the
|
|
188
|
+
// next 16 are `0xffff`. Matching that is the same test `isIP` uses internally.
|
|
189
|
+
const tail = mappedIpv4Tail(value);
|
|
190
|
+
if (tail === undefined) return address;
|
|
191
|
+
// `mappedIpv4Tail` always returns two hex groups, so the dotted and hex
|
|
192
|
+
// spellings have already converged by here.
|
|
193
|
+
const groups = tail.split(":");
|
|
194
|
+
if (groups.length !== 2) return address;
|
|
195
|
+
const high = Number.parseInt(groups[0] ?? "", 16);
|
|
196
|
+
const low = Number.parseInt(groups[1] ?? "", 16);
|
|
197
|
+
if (
|
|
198
|
+
!Number.isInteger(high) ||
|
|
199
|
+
!Number.isInteger(low) ||
|
|
200
|
+
high < 0 ||
|
|
201
|
+
high > 0xffff ||
|
|
202
|
+
low < 0 ||
|
|
203
|
+
low > 0xffff
|
|
204
|
+
)
|
|
205
|
+
return address;
|
|
206
|
+
return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export interface CidrContainsOptions {
|
|
210
|
+
/**
|
|
211
|
+
* `true` (SSRF guard): the CIDR must carry an explicit `/prefix` — a bare
|
|
212
|
+
* address matches nothing, so a misconfigured allowlist entry fails closed.
|
|
213
|
+
* `false` (trusted-proxy boundary): a bare address means "this exact host".
|
|
214
|
+
*/
|
|
215
|
+
requirePrefix?: boolean;
|
|
216
|
+
/**
|
|
217
|
+
* `true` (trusted-proxy boundary): unwrap IPv4-mapped spellings on both the
|
|
218
|
+
* subject and the network before comparing, so `::ffff:127.0.0.1` and
|
|
219
|
+
* `127.0.0.1/32` describe the same host. `false` (SSRF guard): compare the
|
|
220
|
+
* literal as written.
|
|
221
|
+
*/
|
|
222
|
+
unwrapMapped?: boolean;
|
|
223
|
+
/**
|
|
224
|
+
* The SSRF guard's IPv6 parse accepts the dotted-quad spelling (`::ffff:1.2.3.4`,
|
|
225
|
+
* which DNS answers can take); the boundary path refuses it. Defaults to
|
|
226
|
+
* `false`; the SSRF call site passes `true`.
|
|
227
|
+
*/
|
|
228
|
+
allowDottedQuad?: boolean;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Whether `address` falls inside `cidr`. The blank-prefix refusal
|
|
233
|
+
* (`"10.0.0.0/"` must NOT mean /0) is shared by both former implementations and
|
|
234
|
+
* is preserved unconditionally: `Number("")` is 0 and /0 would admit every
|
|
235
|
+
* address of the family — for the SSRF guard that includes the link-local
|
|
236
|
+
* metadata endpoint the guard exists to refuse, and for the trusted-proxy
|
|
237
|
+
* boundary it would believe any caller's `X-Forwarded-For`.
|
|
238
|
+
*/
|
|
239
|
+
export function cidrContainsAddress(
|
|
240
|
+
address: string,
|
|
241
|
+
cidr: string,
|
|
242
|
+
opts: CidrContainsOptions = {},
|
|
243
|
+
): boolean {
|
|
244
|
+
const requirePrefix = opts.requirePrefix ?? true;
|
|
245
|
+
const unwrapMapped = opts.unwrapMapped ?? false;
|
|
246
|
+
const allowDottedQuad = opts.allowDottedQuad ?? false;
|
|
247
|
+
const [network, rawPrefix] = cidr.split("/");
|
|
248
|
+
if (!network) return false;
|
|
249
|
+
const prefixText = rawPrefix === undefined ? undefined : rawPrefix.trim();
|
|
250
|
+
// Requiring a run of digits also refuses the exotic spellings `Number`
|
|
251
|
+
// accepts for zero (`-0`, `+0`, `0x0`, `0b0`, `0o0`, `0.0`) — typos, not a
|
|
252
|
+
// deliberate "match everything".
|
|
253
|
+
if (prefixText !== undefined && !/^\d+$/.test(prefixText)) return false;
|
|
254
|
+
if (requirePrefix && prefixText === undefined) return false;
|
|
255
|
+
const subject = unwrapMapped ? unwrapMappedIpv4(address) : address;
|
|
256
|
+
const base = unwrapMapped ? unwrapMappedIpv4(network) : network;
|
|
257
|
+
const family = isIP(subject);
|
|
258
|
+
if (family !== isIP(base)) return false;
|
|
259
|
+
const maxPrefix = family === 4 ? 32 : 128;
|
|
260
|
+
const prefix = prefixText === undefined ? maxPrefix : Number(prefixText);
|
|
261
|
+
if (!Number.isInteger(prefix) || prefix < 0 || prefix > maxPrefix) return false;
|
|
262
|
+
if (family === 4) {
|
|
263
|
+
const value = ipv4ToNumber(subject);
|
|
264
|
+
const networkValue = ipv4ToNumber(base);
|
|
265
|
+
if (value === undefined || networkValue === undefined) return false;
|
|
266
|
+
if (prefix === 0) return true;
|
|
267
|
+
const mask = (0xffffffff << (32 - prefix)) >>> 0;
|
|
268
|
+
return ((value >>> 0) & mask) === ((networkValue >>> 0) & mask);
|
|
269
|
+
}
|
|
270
|
+
if (family !== 6) return false;
|
|
271
|
+
const value = ipv6ToBigInt(subject, { allowDottedQuad });
|
|
272
|
+
const networkValue = ipv6ToBigInt(base, { allowDottedQuad });
|
|
273
|
+
if (value === undefined || networkValue === undefined) return false;
|
|
274
|
+
const shift = BigInt(128 - prefix);
|
|
275
|
+
return (value >> shift) === (networkValue >> shift);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* True when `address` is a loopback address.
|
|
280
|
+
*
|
|
281
|
+
* Structural, not a string-prefix test: `127.0.0.1`, `::1`, and the mapped
|
|
282
|
+
* `::ffff:127.0.0.1` a Windows/Bun peer arrives as are all the same host, and
|
|
283
|
+
* a prefix test would miss the mapped form — the exact shape that hid this
|
|
284
|
+
* class of bug before.
|
|
285
|
+
*
|
|
286
|
+
* `transport/drain-endpoint.ts` keeps its own weaker `isLoopbackPeer` (a
|
|
287
|
+
* `::ffff:` prefix strip that misses the `0:0:0:0:0:ffff:7f00:1` spelling);
|
|
288
|
+
* unifying it widens the admin gate's acceptance set and is deferred — switch
|
|
289
|
+
* it to this predicate as a deliberate, separately-landed behavior widening.
|
|
290
|
+
*/
|
|
291
|
+
export function isLoopbackAddress(address: string): boolean {
|
|
292
|
+
const normalized = unwrapMappedIpv4(address.trim().split("%", 1)[0] ?? "");
|
|
293
|
+
if (normalized === "::1") return true;
|
|
294
|
+
if (isIP(normalized) !== 4) return false;
|
|
295
|
+
const value = ipv4ToNumber(normalized);
|
|
296
|
+
// 127.0.0.0/8 — the whole loopback block, not just 127.0.0.1.
|
|
297
|
+
return value !== undefined && ((value >>> 0) & 0xff000000) === (127 << 24);
|
|
298
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Validated fetch: SSRF-guarded outbound fetch with optional pool-agent support.
|
|
2
2
|
import { GatewayError } from "../protocol/gateway-error";
|
|
3
|
-
import { Agent as HttpAgent } from "node:http";
|
|
4
|
-
import { Agent as HttpsAgent } from "node:https";
|
|
3
|
+
import { request as httpRequest, Agent as HttpAgent } from "node:http";
|
|
4
|
+
import { request as httpsRequest, Agent as HttpsAgent } from "node:https";
|
|
5
5
|
import { Agent as UndiciAgent, fetch as undiciFetch, type UndiciRequestInit } from "undici";
|
|
6
6
|
import { SocksProxyAgent } from "socks-proxy-agent";
|
|
7
7
|
import { resolveAllAddresses, validateResolvedAddresses } from "./ssrf";
|
|
@@ -10,7 +10,7 @@ import { isProxyAgentPair, type ProxyAgentPair } from "./pool/agent";
|
|
|
10
10
|
import { metrics } from "../observability/metrics";
|
|
11
11
|
import { trackNetworkCall } from "../observability/performance-metrics";
|
|
12
12
|
// Validated fetch — single egress with ProxyAgent support
|
|
13
|
-
|
|
13
|
+
interface ValidatedFetchOptions {
|
|
14
14
|
readonly fetchFn?: typeof fetch;
|
|
15
15
|
readonly policy?: SsrfPolicy;
|
|
16
16
|
readonly maxRedirects?: number;
|
|
@@ -167,6 +167,66 @@ async function dial(
|
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* One raw GET/HEAD dial that carries a body.
|
|
172
|
+
*
|
|
173
|
+
* The runtime-native fetch (`Bun`/undici) rejects `GET`/`HEAD` requests with a
|
|
174
|
+
* body — "request with GET/HEAD method cannot have body" — but some upstreams
|
|
175
|
+
* validate a signature over the body on a GET (Qoder's `/model/list` does).
|
|
176
|
+
* This is the Node `http`/`https` path for exactly that case. It runs only
|
|
177
|
+
* after the SSRF resolve/validate pass and only when no pool/relay agent owns
|
|
178
|
+
* the dial, so the security boundary is unchanged.
|
|
179
|
+
*/
|
|
180
|
+
function nodeDial(url: URL, init: RequestInit): Promise<Response> {
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
const mod = url.protocol === "http:" ? httpRequest : httpsRequest;
|
|
183
|
+
const headers: Record<string, string> = {};
|
|
184
|
+
{
|
|
185
|
+
const h = new Headers(init.headers);
|
|
186
|
+
h.forEach((value, key) => {
|
|
187
|
+
headers[key] = value;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
let payload: Buffer | undefined;
|
|
191
|
+
const body = init.body;
|
|
192
|
+
if (typeof body === "string") {
|
|
193
|
+
payload = Buffer.from(body, "utf8");
|
|
194
|
+
} else if (Buffer.isBuffer(body)) {
|
|
195
|
+
payload = body;
|
|
196
|
+
} else if (body instanceof Uint8Array) {
|
|
197
|
+
payload = Buffer.from(body);
|
|
198
|
+
} else if (body !== null && body !== undefined) {
|
|
199
|
+
reject(new TypeError("nodeDial only supports string/Buffer/Uint8Array bodies"));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (payload !== undefined) headers["content-length"] = String(payload.byteLength);
|
|
203
|
+
const req = mod(
|
|
204
|
+
url,
|
|
205
|
+
{
|
|
206
|
+
method: (init.method ?? "GET").toUpperCase(),
|
|
207
|
+
headers,
|
|
208
|
+
...(init.signal === undefined ? {} : { signal: init.signal as AbortSignal }),
|
|
209
|
+
},
|
|
210
|
+
(res) => {
|
|
211
|
+
const chunks: Buffer[] = [];
|
|
212
|
+
res.on("data", (c: Buffer) => chunks.push(c));
|
|
213
|
+
res.on("end", () => {
|
|
214
|
+
resolve(
|
|
215
|
+
new Response(Buffer.concat(chunks), {
|
|
216
|
+
status: res.statusCode ?? 200,
|
|
217
|
+
...(res.statusMessage === undefined ? {} : { statusText: res.statusMessage }),
|
|
218
|
+
headers: res.headers as unknown as HeadersInit,
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
req.on("error", reject);
|
|
225
|
+
if (payload !== undefined) req.write(payload);
|
|
226
|
+
req.end();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
170
230
|
export function createValidatedFetch(options: ValidatedFetchOptions = {}): ValidatedFetch {
|
|
171
231
|
const policy = options.policy ?? {};
|
|
172
232
|
const maxRedirects = options.maxRedirects ?? policy.maxRedirects ?? 3;
|
|
@@ -242,7 +302,18 @@ export function createValidatedFetch(options: ValidatedFetchOptions = {}): Valid
|
|
|
242
302
|
// serverless front door refuses (400).
|
|
243
303
|
response = await dial(relay.url, relay.init, fetchFn, undefined);
|
|
244
304
|
} else {
|
|
245
|
-
|
|
305
|
+
const method = (requestInit.method ?? "GET").toUpperCase();
|
|
306
|
+
const hasBody = requestInit.body !== undefined && requestInit.body !== null;
|
|
307
|
+
// The runtime fetch cannot carry a body on GET/HEAD, but Qoder's
|
|
308
|
+
// model-list endpoint requires a signed GET body. When no pool/relay
|
|
309
|
+
// owns the dial and no injected test fetch is present, dial through the
|
|
310
|
+
// Node http(s) client (post SSRF validation). Every other case keeps
|
|
311
|
+
// the existing fetch path.
|
|
312
|
+
if ((method === "GET" || method === "HEAD") && hasBody && !socksAgent && options.fetchFn === undefined) {
|
|
313
|
+
response = await nodeDial(url, requestInit);
|
|
314
|
+
} else {
|
|
315
|
+
response = await dial(url, requestInit, fetchFn, proxyUrl, socksAgent ? socksDispatcherFor(socksAgent) : undefined, socksFetch);
|
|
316
|
+
}
|
|
246
317
|
}
|
|
247
318
|
if (response.status < 300 || response.status >= 400) {
|
|
248
319
|
trackNetworkCall(url.hostname, performance.now() - startedAt);
|
|
@@ -264,7 +335,7 @@ export function createValidatedFetch(options: ValidatedFetchOptions = {}): Valid
|
|
|
264
335
|
if (isCrossOriginRedirect && (response.status === 307 || response.status === 308)) {
|
|
265
336
|
const method = (requestInit.method ?? "GET").toUpperCase();
|
|
266
337
|
if (method !== "GET" && method !== "HEAD" && requestInit.body !== undefined) {
|
|
267
|
-
throw new GatewayError("invalid_request", 400, "cross-origin redirect with body blocked");
|
|
338
|
+
throw new GatewayError("invalid_request", 400, "cross-origin redirect with body blocked (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
268
339
|
}
|
|
269
340
|
}
|
|
270
341
|
url = nextUrl;
|
|
@@ -44,7 +44,7 @@ export type TransportKind = (typeof TRANSPORT_KINDS)[number];
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
/** Config keys reserved for endpoint/label; every other key is opaque non-secret transport config. */
|
|
47
|
-
|
|
47
|
+
const RESERVED_ENDPOINT_CONFIG_KEYS: Record<string, true> = {
|
|
48
48
|
endpoint: true,
|
|
49
49
|
label: true,
|
|
50
50
|
};
|
|
@@ -122,9 +122,11 @@ function createSsrfLookup(policy: SsrfPolicy): NonNullable<RequestOptions["looku
|
|
|
122
122
|
family?: number,
|
|
123
123
|
) => void,
|
|
124
124
|
): void => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
// No outer AbortController/timer here: `resolveAllAddresses` already races
|
|
126
|
+
// its own 5s deadline against the OS lookup and maps the failure to a
|
|
127
|
+
// deterministic `transport_unavailable` 502. A second timer would only
|
|
128
|
+
// re-race it with a different error class (499/504 depending on timing).
|
|
129
|
+
void resolveAllAddresses(hostname)
|
|
128
130
|
.then((addrs) => {
|
|
129
131
|
try {
|
|
130
132
|
validateResolvedAddresses(addrs, policy);
|
|
@@ -145,8 +147,7 @@ function createSsrfLookup(policy: SsrfPolicy): NonNullable<RequestOptions["looku
|
|
|
145
147
|
}
|
|
146
148
|
cb(null, first, family);
|
|
147
149
|
})
|
|
148
|
-
.catch((error: unknown) => cb(error as Error))
|
|
149
|
-
.finally(() => clearTimeout(timer));
|
|
150
|
+
.catch((error: unknown) => cb(error as Error));
|
|
150
151
|
}) as unknown as NonNullable<RequestOptions["lookup"]>;
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/** Running totals for one pool. */
|
|
17
|
-
|
|
17
|
+
interface PoolByteTotals {
|
|
18
18
|
/** Bytes sent to the proxy (uploads, TLS handshake, CONNECT). */
|
|
19
19
|
readonly sent: number;
|
|
20
20
|
/** Bytes received from the proxy (downloads). */
|