cartethyia 2.7.4 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-ChG5svFb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BIcWQun7.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Bf2VelDe.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-ggeMSjte.js → CustomizationPage-BKj_r0rp.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-U6AKaJcF.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-5SnUSy3j.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-f0DE1h0-.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js +1 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js +1 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-8fiE6tec.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BSAsYsmV.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-CPK47xQx.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js +1 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BLULNqTp.js} +1 -1
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +56 -55
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +48 -12
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +37 -31
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +1 -1
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +1 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +20 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +259 -66
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +4 -4
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +4 -4
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +252 -97
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +59 -291
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +113 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +10 -1
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +6 -10
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +6 -6
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/providers/adapters/compatible-adapter.ts +42 -3
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +23 -35
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +2 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +38 -5
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +9 -9
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +41 -14
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +81 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +85 -8
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +354 -62
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +53 -10
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +16 -4
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +1 -1
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +6 -0
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +1 -13
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +152 -37
- package/src/transport/shutdown-notice.ts +2 -2
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +376 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/{app-BayTTy01.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-BayTTy01.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -36,8 +36,7 @@ import {
|
|
|
36
36
|
type ProxyCooldownEntry,
|
|
37
37
|
} from "./pool-state";
|
|
38
38
|
|
|
39
|
-
export type { PoolRotation,
|
|
40
|
-
export { parseCooldownEntry };
|
|
39
|
+
export type { PoolRotation, ProxyCooldownEntry };
|
|
41
40
|
|
|
42
41
|
// Pool selector
|
|
43
42
|
/**
|
|
@@ -51,7 +50,7 @@ export { parseCooldownEntry };
|
|
|
51
50
|
*/
|
|
52
51
|
export const PROVIDER_BLOCK_MS = 10 * 365 * 24 * 60 * 60 * 1000;
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
const DEFAULT_PROXY_CONCURRENCY = 10;
|
|
55
54
|
const DEFAULT_PROXY_WEIGHT = 100;
|
|
56
55
|
const MAX_PROXY_WEIGHT = 1000;
|
|
57
56
|
// Crash-recovery bound, not a lease duration: normal releases DECR/DEL the
|
|
@@ -63,7 +62,7 @@ const MAX_PROXY_WEIGHT = 1000;
|
|
|
63
62
|
function poolInflightTtlSeconds(): number {
|
|
64
63
|
return resolveInflightTtlSeconds();
|
|
65
64
|
}
|
|
66
|
-
|
|
65
|
+
function effectiveConcurrency(limit: number): number {
|
|
67
66
|
if (!Number.isInteger(limit) || limit < 1) return 0;
|
|
68
67
|
return limit;
|
|
69
68
|
}
|
|
@@ -100,7 +99,7 @@ function decrementLocal(counter: Map<string, number>, key: string): void {
|
|
|
100
99
|
else counter.set(key, value - 1);
|
|
101
100
|
}
|
|
102
101
|
|
|
103
|
-
|
|
102
|
+
type PoolSelectionFailureReason =
|
|
104
103
|
| "at_capacity"
|
|
105
104
|
| "provider_at_capacity"
|
|
106
105
|
| "cooldown"
|
|
@@ -109,7 +108,7 @@ export type PoolSelectionFailureReason =
|
|
|
109
108
|
| "coordination_unavailable"
|
|
110
109
|
| "no_active_pool";
|
|
111
110
|
|
|
112
|
-
|
|
111
|
+
interface PoolCapacitySnapshot {
|
|
113
112
|
readonly poolId: string;
|
|
114
113
|
readonly maxInflight: number;
|
|
115
114
|
readonly currentInflight: number;
|
|
@@ -206,67 +205,67 @@ export class NetworkPoolSelector {
|
|
|
206
205
|
}
|
|
207
206
|
|
|
208
207
|
/**
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
208
|
+
* One local slot acquisition: checks every map's ceiling, bumps each counter,
|
|
209
|
+
* and returns a release that decrements all of them exactly once. Shared by
|
|
210
|
+
* `acquire` (single pool counter) and `acquireProvider` (pool + provider
|
|
211
|
+
* share) so the release closure and the local bookkeeping live in one place.
|
|
213
212
|
*/
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
213
|
+
private acquireLocalSlot(
|
|
214
|
+
increments: ReadonlyArray<{
|
|
215
|
+
readonly map: Map<string, number>;
|
|
216
|
+
readonly key: string;
|
|
217
|
+
readonly capacity: number;
|
|
218
|
+
}>,
|
|
219
219
|
): { acquired: boolean; release: () => void } {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
this.state.inflight.set(poolId, current + 1);
|
|
229
|
-
this.state.providerInflight.set(key, providerCurrent + 1);
|
|
220
|
+
for (const increment of increments) {
|
|
221
|
+
const current = increment.map.get(increment.key) ?? 0;
|
|
222
|
+
if (current >= increment.capacity) return { acquired: false, release: () => {} };
|
|
223
|
+
}
|
|
224
|
+
for (const increment of increments) {
|
|
225
|
+
enforceInflightLimit(increment.map);
|
|
226
|
+
increment.map.set(increment.key, (increment.map.get(increment.key) ?? 0) + 1);
|
|
227
|
+
}
|
|
230
228
|
this.notifyPoolUsage();
|
|
231
229
|
|
|
232
230
|
let released = false;
|
|
233
231
|
const release = () => {
|
|
234
232
|
if (released) return;
|
|
235
233
|
released = true;
|
|
236
|
-
decrementLocal(
|
|
237
|
-
decrementLocal(this.state.providerInflight, key);
|
|
234
|
+
for (const increment of increments) decrementLocal(increment.map, increment.key);
|
|
238
235
|
this.notifyPoolUsage();
|
|
239
236
|
};
|
|
240
237
|
return { acquired: true, release };
|
|
241
238
|
}
|
|
242
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Raise this provider's share of a pool, enforcing both the pool ceiling and
|
|
242
|
+
* the provider ceiling. The pool half is still checked here so a caller that
|
|
243
|
+
* already admitted the pool through `acquire` does not double-count it — use
|
|
244
|
+
* this instead of `acquire` whenever a provider ceiling applies.
|
|
245
|
+
*/
|
|
246
|
+
acquireProvider(
|
|
247
|
+
poolId: string,
|
|
248
|
+
providerId: string,
|
|
249
|
+
poolCapacity: number,
|
|
250
|
+
providerCapacity: number,
|
|
251
|
+
): { acquired: boolean; release: () => void } {
|
|
252
|
+
return this.acquireLocalSlot([
|
|
253
|
+
{ map: this.state.inflight, key: poolId, capacity: poolCapacity },
|
|
254
|
+
{
|
|
255
|
+
map: this.state.providerInflight,
|
|
256
|
+
key: providerInflightStateKey(poolId, providerId),
|
|
257
|
+
capacity: providerCapacity,
|
|
258
|
+
},
|
|
259
|
+
]);
|
|
260
|
+
}
|
|
261
|
+
|
|
243
262
|
acquire(
|
|
244
263
|
poolId: string,
|
|
245
264
|
maxInflight = DEFAULT_PROXY_CONCURRENCY,
|
|
246
265
|
): { acquired: boolean; release: () => void } {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
enforceInflightLimit(this.state.inflight);
|
|
253
|
-
this.state.inflight.set(poolId, current + 1);
|
|
254
|
-
this.notifyPoolUsage();
|
|
255
|
-
let released = false;
|
|
256
|
-
|
|
257
|
-
const release = () => {
|
|
258
|
-
if (released) return;
|
|
259
|
-
released = true;
|
|
260
|
-
const val = this.state.inflight.get(poolId) ?? 1;
|
|
261
|
-
if (val <= 1) {
|
|
262
|
-
this.state.inflight.delete(poolId);
|
|
263
|
-
} else {
|
|
264
|
-
this.state.inflight.set(poolId, val - 1);
|
|
265
|
-
}
|
|
266
|
-
this.notifyPoolUsage();
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
return { acquired: true, release };
|
|
266
|
+
return this.acquireLocalSlot([
|
|
267
|
+
{ map: this.state.inflight, key: poolId, capacity: maxInflight },
|
|
268
|
+
]);
|
|
270
269
|
}
|
|
271
270
|
|
|
272
271
|
private cooldownKey(poolId: string, providerId: string): string {
|
|
@@ -577,6 +576,98 @@ export class NetworkPoolSelector {
|
|
|
577
576
|
await this.redis.del(indexKey);
|
|
578
577
|
}
|
|
579
578
|
|
|
579
|
+
/**
|
|
580
|
+
* Tries one local candidate: bump the local counters (pool, and provider
|
|
581
|
+
* share when a ceiling is set) and return its release. `undefined` when the
|
|
582
|
+
* candidate is full or the provider share is exhausted.
|
|
583
|
+
*/
|
|
584
|
+
private localAcquire(
|
|
585
|
+
candidate: { readonly poolId: string; readonly capacity: number },
|
|
586
|
+
providerId: string,
|
|
587
|
+
providerCapacity: number | undefined,
|
|
588
|
+
): { poolId: string; release: () => void } | undefined {
|
|
589
|
+
const slot =
|
|
590
|
+
providerCapacity === undefined
|
|
591
|
+
? this.acquire(candidate.poolId, candidate.capacity)
|
|
592
|
+
: this.acquireProvider(candidate.poolId, providerId, candidate.capacity, providerCapacity);
|
|
593
|
+
return slot.acquired ? { poolId: candidate.poolId, release: slot.release } : undefined;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* One atomic Redis admit (the compare-and-increment script) for a candidate:
|
|
598
|
+
* bumps both the remote and local counters on success and returns a release
|
|
599
|
+
* that decrements both. `undefined` when the pool or provider share is full
|
|
600
|
+
* (1 admitted; 0 pool full; 2 provider full — either full result fails over
|
|
601
|
+
* to the next pool, which is the whole point of the ceiling).
|
|
602
|
+
*/
|
|
603
|
+
private async redisAdmit(
|
|
604
|
+
redis: RedisClient,
|
|
605
|
+
candidate: { readonly poolId: string; readonly capacity: number },
|
|
606
|
+
providerId: string,
|
|
607
|
+
providerCapacity: number | undefined,
|
|
608
|
+
): Promise<{ poolId: string; release: () => void } | undefined> {
|
|
609
|
+
const providerKey =
|
|
610
|
+
providerCapacity === undefined
|
|
611
|
+
? undefined
|
|
612
|
+
: proxyProviderInflightKey(candidate.poolId, providerId);
|
|
613
|
+
const result =
|
|
614
|
+
providerKey === undefined
|
|
615
|
+
? await redisEvalNumber(
|
|
616
|
+
redis,
|
|
617
|
+
POOL_ADMIT_SCRIPT,
|
|
618
|
+
1,
|
|
619
|
+
proxyInflightKey(candidate.poolId),
|
|
620
|
+
String(candidate.capacity),
|
|
621
|
+
String(poolInflightTtlSeconds()),
|
|
622
|
+
)
|
|
623
|
+
: await redisEvalNumber(
|
|
624
|
+
redis,
|
|
625
|
+
POOL_PROVIDER_ADMIT_SCRIPT,
|
|
626
|
+
2,
|
|
627
|
+
proxyInflightKey(candidate.poolId),
|
|
628
|
+
providerKey,
|
|
629
|
+
String(candidate.capacity),
|
|
630
|
+
String(providerCapacity),
|
|
631
|
+
String(poolInflightTtlSeconds()),
|
|
632
|
+
);
|
|
633
|
+
if (result !== 1) return undefined;
|
|
634
|
+
enforceInflightLimit(this.state.inflight);
|
|
635
|
+
this.state.inflight.set(candidate.poolId, this.getInflight(candidate.poolId) + 1);
|
|
636
|
+
if (providerKey !== undefined) {
|
|
637
|
+
enforceInflightLimit(this.state.providerInflight);
|
|
638
|
+
const localProviderKey = providerInflightStateKey(candidate.poolId, providerId);
|
|
639
|
+
this.state.providerInflight.set(
|
|
640
|
+
localProviderKey,
|
|
641
|
+
this.getProviderInflight(candidate.poolId, providerId) + 1,
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
this.notifyPoolUsage();
|
|
645
|
+
let released = false;
|
|
646
|
+
return {
|
|
647
|
+
poolId: candidate.poolId,
|
|
648
|
+
release: () => {
|
|
649
|
+
if (released) return;
|
|
650
|
+
released = true;
|
|
651
|
+
decrementLocal(this.state.inflight, candidate.poolId);
|
|
652
|
+
if (providerKey !== undefined) {
|
|
653
|
+
decrementLocal(
|
|
654
|
+
this.state.providerInflight,
|
|
655
|
+
providerInflightStateKey(candidate.poolId, providerId),
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
this.notifyPoolUsage();
|
|
659
|
+
const script = providerKey === undefined ? POOL_RELEASE_SCRIPT : POOL_PROVIDER_RELEASE_SCRIPT;
|
|
660
|
+
const keys =
|
|
661
|
+
providerKey === undefined
|
|
662
|
+
? [proxyInflightKey(candidate.poolId)]
|
|
663
|
+
: [proxyInflightKey(candidate.poolId), providerKey];
|
|
664
|
+
void redisEvalNumber(redis, script, keys.length, ...keys).catch((error: unknown) => {
|
|
665
|
+
log.error("[pool-selector] failed to release distributed slot", error as Error);
|
|
666
|
+
});
|
|
667
|
+
},
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
580
671
|
/**
|
|
581
672
|
* Distributed pool admission — the selection algorithm:
|
|
582
673
|
*
|
|
@@ -652,17 +743,8 @@ export class NetworkPoolSelector {
|
|
|
652
743
|
shuffleArray(scored);
|
|
653
744
|
for (const candidate of scored) {
|
|
654
745
|
if (this.getInflight(candidate.poolId) >= candidate.capacity) continue;
|
|
655
|
-
const slot =
|
|
656
|
-
|
|
657
|
-
? this.acquire(candidate.poolId, candidate.capacity)
|
|
658
|
-
: this.acquireProvider(
|
|
659
|
-
candidate.poolId,
|
|
660
|
-
providerId,
|
|
661
|
-
candidate.capacity,
|
|
662
|
-
providerCapacity,
|
|
663
|
-
);
|
|
664
|
-
if (!slot.acquired) continue;
|
|
665
|
-
return { poolId: candidate.poolId, release: slot.release };
|
|
746
|
+
const slot = this.localAcquire(candidate, providerId, providerCapacity);
|
|
747
|
+
if (slot) return slot;
|
|
666
748
|
}
|
|
667
749
|
return undefined;
|
|
668
750
|
}
|
|
@@ -677,18 +759,10 @@ export class NetworkPoolSelector {
|
|
|
677
759
|
const candidate = scored[absolute];
|
|
678
760
|
if (!candidate) continue;
|
|
679
761
|
if (this.getInflight(candidate.poolId) >= candidate.capacity) continue;
|
|
680
|
-
const slot =
|
|
681
|
-
|
|
682
|
-
? this.acquire(candidate.poolId, candidate.capacity)
|
|
683
|
-
: this.acquireProvider(
|
|
684
|
-
candidate.poolId,
|
|
685
|
-
providerId,
|
|
686
|
-
candidate.capacity,
|
|
687
|
-
providerCapacity,
|
|
688
|
-
);
|
|
689
|
-
if (!slot.acquired) continue;
|
|
762
|
+
const slot = this.localAcquire(candidate, providerId, providerCapacity);
|
|
763
|
+
if (!slot) continue;
|
|
690
764
|
advanceRotation(this.state.rotationCursors, rotation.key, scored.length, absolute, rotation.rotateCount);
|
|
691
|
-
return
|
|
765
|
+
return slot;
|
|
692
766
|
}
|
|
693
767
|
return undefined;
|
|
694
768
|
}
|
|
@@ -716,11 +790,7 @@ export class NetworkPoolSelector {
|
|
|
716
790
|
}
|
|
717
791
|
}
|
|
718
792
|
if (!bestPool) return undefined;
|
|
719
|
-
|
|
720
|
-
providerCapacity === undefined
|
|
721
|
-
? this.acquire(bestPool.poolId, bestPool.capacity)
|
|
722
|
-
: this.acquireProvider(bestPool.poolId, providerId, bestPool.capacity, providerCapacity);
|
|
723
|
-
return slot.acquired ? { poolId: bestPool.poolId, release: slot.release } : undefined;
|
|
793
|
+
return this.localAcquire(bestPool, providerId, providerCapacity);
|
|
724
794
|
}
|
|
725
795
|
|
|
726
796
|
const redis = this.redis;
|
|
@@ -795,34 +865,8 @@ export class NetworkPoolSelector {
|
|
|
795
865
|
});
|
|
796
866
|
}
|
|
797
867
|
for (const candidate of withCounts) {
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
? undefined
|
|
801
|
-
: proxyProviderInflightKey(candidate.poolId, providerId);
|
|
802
|
-
const result =
|
|
803
|
-
providerKey === undefined
|
|
804
|
-
? await redisEvalNumber(
|
|
805
|
-
redis,
|
|
806
|
-
POOL_ADMIT_SCRIPT,
|
|
807
|
-
1,
|
|
808
|
-
proxyInflightKey(candidate.poolId),
|
|
809
|
-
String(candidate.capacity),
|
|
810
|
-
String(poolInflightTtlSeconds()),
|
|
811
|
-
)
|
|
812
|
-
: await redisEvalNumber(
|
|
813
|
-
redis,
|
|
814
|
-
POOL_PROVIDER_ADMIT_SCRIPT,
|
|
815
|
-
2,
|
|
816
|
-
proxyInflightKey(candidate.poolId),
|
|
817
|
-
providerKey,
|
|
818
|
-
String(candidate.capacity),
|
|
819
|
-
String(providerCapacity),
|
|
820
|
-
String(poolInflightTtlSeconds()),
|
|
821
|
-
);
|
|
822
|
-
// 1 admitted; 0 pool full; 2 provider full. Either full result fails over
|
|
823
|
-
// to the next pool, which is the whole point of the ceiling: the request
|
|
824
|
-
// moves to an egress that still has room for this provider.
|
|
825
|
-
if (result !== 1) continue;
|
|
868
|
+
const admitted = await this.redisAdmit(redis, candidate, providerId, providerCapacity);
|
|
869
|
+
if (admitted === undefined) continue;
|
|
826
870
|
if (rotation && rotation.strategy !== "random") {
|
|
827
871
|
advanceRotation(
|
|
828
872
|
this.state.rotationCursors,
|
|
@@ -832,37 +876,7 @@ export class NetworkPoolSelector {
|
|
|
832
876
|
rotation.rotateCount,
|
|
833
877
|
);
|
|
834
878
|
}
|
|
835
|
-
|
|
836
|
-
this.state.inflight.set(candidate.poolId, this.getInflight(candidate.poolId) + 1);
|
|
837
|
-
if (providerKey !== undefined) {
|
|
838
|
-
enforceInflightLimit(this.state.providerInflight);
|
|
839
|
-
const localProviderKey = providerInflightStateKey(candidate.poolId, providerId);
|
|
840
|
-
this.state.providerInflight.set(
|
|
841
|
-
localProviderKey,
|
|
842
|
-
this.getProviderInflight(candidate.poolId, providerId) + 1,
|
|
843
|
-
);
|
|
844
|
-
}
|
|
845
|
-
this.notifyPoolUsage();
|
|
846
|
-
let released = false;
|
|
847
|
-
return {
|
|
848
|
-
poolId: candidate.poolId,
|
|
849
|
-
release: () => {
|
|
850
|
-
if (released) return;
|
|
851
|
-
released = true;
|
|
852
|
-
decrementLocal(this.state.inflight, candidate.poolId);
|
|
853
|
-
if (providerKey !== undefined) {
|
|
854
|
-
decrementLocal(this.state.providerInflight, providerInflightStateKey(candidate.poolId, providerId));
|
|
855
|
-
}
|
|
856
|
-
this.notifyPoolUsage();
|
|
857
|
-
const script = providerKey === undefined ? POOL_RELEASE_SCRIPT : POOL_PROVIDER_RELEASE_SCRIPT;
|
|
858
|
-
const keys = providerKey === undefined ? [proxyInflightKey(candidate.poolId)] : [proxyInflightKey(candidate.poolId), providerKey];
|
|
859
|
-
void redisEvalNumber(redis, script, keys.length, ...keys).catch(
|
|
860
|
-
(error: unknown) => {
|
|
861
|
-
log.error("[pool-selector] failed to release distributed slot", error as Error);
|
|
862
|
-
},
|
|
863
|
-
);
|
|
864
|
-
},
|
|
865
|
-
};
|
|
879
|
+
return admitted;
|
|
866
880
|
}
|
|
867
881
|
return undefined;
|
|
868
882
|
}
|
|
@@ -16,7 +16,7 @@ export interface PoolHealthEvent {
|
|
|
16
16
|
readonly createdAt: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
interface PoolHealthSnapshot {
|
|
20
20
|
readonly poolId: string;
|
|
21
21
|
readonly tenantId: string;
|
|
22
22
|
readonly status: "active" | "cooldown" | "disabled";
|
|
@@ -21,7 +21,7 @@ import { GatewayError } from "../protocol/gateway-error";
|
|
|
21
21
|
import { providerRateLimitIsIpScoped } from "../providers/provider-metadata";
|
|
22
22
|
|
|
23
23
|
/** What one upstream failure means for the egress that carried it. */
|
|
24
|
-
|
|
24
|
+
type EgressVerdict =
|
|
25
25
|
| { kind: "none" }
|
|
26
26
|
| { kind: "cooldown"; retryAfterMs?: number }
|
|
27
27
|
| { kind: "blocked" };
|
package/src/network/retry.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { exponentialBackoff, type BackoffOptions } from "./backoff";
|
|
10
10
|
import { sleep as defaultSleep } from "../shared/sleep";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
interface RetryOptions {
|
|
13
13
|
readonly maxRetries?: number;
|
|
14
14
|
readonly baseDelayMs?: number;
|
|
15
15
|
readonly maxDelayMs?: number;
|
package/src/network/ssrf.ts
CHANGED
|
@@ -3,6 +3,12 @@ import { lookup } from "node:dns/promises";
|
|
|
3
3
|
import { isIP } from "node:net";
|
|
4
4
|
import { GatewayError } from "../protocol/gateway-error";
|
|
5
5
|
import type { SsrfPolicy } from "../config/network";
|
|
6
|
+
import {
|
|
7
|
+
cidrContainsAddress,
|
|
8
|
+
ipv4ToNumberStrict,
|
|
9
|
+
ipv6GroupsToBytes,
|
|
10
|
+
normalizeIpv6Groups,
|
|
11
|
+
} from "./ip-utils";
|
|
6
12
|
|
|
7
13
|
const DNS_CACHE_TTL_MS = 60_000;
|
|
8
14
|
const DNS_CACHE_MAX = 64;
|
|
@@ -31,87 +37,24 @@ type DnsLookup = (
|
|
|
31
37
|
options: { all: true },
|
|
32
38
|
) => Promise<readonly { address: string; family: number }[]>;
|
|
33
39
|
|
|
34
|
-
function ipv4Number(address: string): number {
|
|
35
|
-
const parts = address.split(".");
|
|
36
|
-
if (parts.length !== 4 || parts.some((part) => !/^\d+$/.test(part)))
|
|
37
|
-
throw new Error("invalid IPv4");
|
|
38
|
-
const octets = parts.map(Number);
|
|
39
|
-
if (octets.some((part) => part < 0 || part > 255)) throw new Error("invalid IPv4");
|
|
40
|
-
return (((octets[0]! << 24) >>> 0) | (octets[1]! << 16) | (octets[2]! << 8) | octets[3]!) >>> 0;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
40
|
function ipv6Bytes(address: string): Uint8Array {
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
46
|
-
const split = value.lastIndexOf(":");
|
|
47
|
-
if (split < 0) throw new Error("invalid IPv6");
|
|
48
|
-
const mapped = ipv4Number(value.slice(split + 1));
|
|
49
|
-
return ipv6Bytes(
|
|
50
|
-
`${value.slice(0, split)}:${(mapped >>> 16).toString(16)}:${(mapped & 0xffff).toString(16)}`,
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
const halves = value.split("::");
|
|
54
|
-
if (halves.length > 2) throw new Error("invalid IPv6");
|
|
55
|
-
const left = halves[0] ? halves[0].split(":") : [];
|
|
56
|
-
const right = halves.length === 2 && halves[1] ? halves[1].split(":") : [];
|
|
57
|
-
if (halves.length === 1 && left.length !== 8) throw new Error("invalid IPv6");
|
|
58
|
-
const missing = 8 - left.length - right.length;
|
|
59
|
-
if (missing < 0 || (halves.length === 2 && missing < 1)) throw new Error("invalid IPv6");
|
|
60
|
-
const groups = [...left, ...Array.from({ length: missing }, () => "0"), ...right];
|
|
61
|
-
if (groups.length !== 8 || groups.some((part) => !/^[0-9a-f]{1,4}$/.test(part)))
|
|
62
|
-
throw new Error("invalid IPv6");
|
|
63
|
-
const bytes = new Uint8Array(16);
|
|
64
|
-
groups.forEach((part, index) => {
|
|
65
|
-
const n = Number.parseInt(part, 16);
|
|
66
|
-
bytes[index * 2] = n >>> 8;
|
|
67
|
-
bytes[index * 2 + 1] = n & 255;
|
|
68
|
-
});
|
|
41
|
+
const bytes = ipv6GroupsToBytes(normalizeIpv6Groups(address, { allowDottedQuad: true }) ?? []);
|
|
42
|
+
if (bytes === undefined) throw new Error("invalid IPv6");
|
|
69
43
|
return bytes;
|
|
70
44
|
}
|
|
71
45
|
|
|
72
46
|
function cidrContains(address: string, cidr: string): boolean {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// An operator's typo would therefore silently widen the allowlist to the whole
|
|
79
|
-
// IPv4 space, including the link-local metadata endpoint this guard exists to
|
|
80
|
-
// refuse.
|
|
81
|
-
//
|
|
82
|
-
// Requiring a run of digits (after trimming) also refuses the exotic spellings
|
|
83
|
-
// `Number` accepts for zero — `-0`, `+0`, `0x0`, `0b0`, `0o0`, `0.0` — all of
|
|
84
|
-
// which are typos rather than a deliberate "allow everything". Only a written
|
|
85
|
-
// `0` means that, which is the next branch's business.
|
|
86
|
-
const prefixDigits = prefixText.trim();
|
|
87
|
-
if (!/^\d+$/.test(prefixDigits)) return false;
|
|
88
|
-
const prefix = Number(prefixDigits);
|
|
89
|
-
const family = isIP(address);
|
|
90
|
-
if (
|
|
91
|
-
family !== isIP(network) ||
|
|
92
|
-
!Number.isInteger(prefix) ||
|
|
93
|
-
prefix < 0 ||
|
|
94
|
-
prefix > (family === 4 ? 32 : 128)
|
|
95
|
-
)
|
|
96
|
-
return false;
|
|
97
|
-
if (family === 4) {
|
|
98
|
-
const bits = 32 - prefix;
|
|
99
|
-
return bits === 32 ? true : ipv4Number(address) >>> bits === ipv4Number(network) >>> bits;
|
|
100
|
-
}
|
|
101
|
-
const a = ipv6Bytes(address);
|
|
102
|
-
const n = ipv6Bytes(network);
|
|
103
|
-
for (let index = 0; index < prefix; index++) {
|
|
104
|
-
const byte = Math.floor(index / 8);
|
|
105
|
-
const bit = 7 - (index % 8);
|
|
106
|
-
if (((a[byte]! >> bit) & 1) !== ((n[byte]! >> bit) & 1)) return false;
|
|
107
|
-
}
|
|
108
|
-
return true;
|
|
47
|
+
return cidrContainsAddress(address, cidr, {
|
|
48
|
+
requirePrefix: true,
|
|
49
|
+
unwrapMapped: false,
|
|
50
|
+
allowDottedQuad: true,
|
|
51
|
+
});
|
|
109
52
|
}
|
|
110
53
|
|
|
111
54
|
function unsafeAddress(address: string): boolean {
|
|
112
55
|
const family = isIP(address);
|
|
113
56
|
if (family === 4) {
|
|
114
|
-
const n =
|
|
57
|
+
const n = ipv4ToNumberStrict(address);
|
|
115
58
|
return [
|
|
116
59
|
[0, 8],
|
|
117
60
|
[0x0a000000, 8],
|
|
@@ -175,9 +118,15 @@ export function validateResolvedAddresses(
|
|
|
175
118
|
return addresses[0]!;
|
|
176
119
|
}
|
|
177
120
|
|
|
121
|
+
export async function resolveAllAddresses(hostname: string): Promise<string[]>;
|
|
178
122
|
export async function resolveAllAddresses(
|
|
179
123
|
hostname: string,
|
|
180
124
|
signal: AbortSignal,
|
|
125
|
+
lookupFn?: DnsLookup,
|
|
126
|
+
): Promise<string[]>;
|
|
127
|
+
export async function resolveAllAddresses(
|
|
128
|
+
hostname: string,
|
|
129
|
+
signal: AbortSignal = new AbortController().signal,
|
|
181
130
|
lookupFn: DnsLookup = lookup,
|
|
182
131
|
): Promise<string[]> {
|
|
183
132
|
if (signal.aborted) throw abortErrorForSignal(signal);
|
|
@@ -255,7 +255,7 @@ export async function readTelemetryEvents(): Promise<TelemetryEventRecord[]> {
|
|
|
255
255
|
return events;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
interface EventQuery {
|
|
259
259
|
readonly tenantId?: string;
|
|
260
260
|
readonly since?: Date;
|
|
261
261
|
/**
|
|
@@ -372,7 +372,7 @@ export async function queryTelemetryEvents(query: EventQuery): Promise<Telemetry
|
|
|
372
372
|
return events;
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
-
|
|
375
|
+
interface EventTotals {
|
|
376
376
|
requests: number;
|
|
377
377
|
succeeded: number;
|
|
378
378
|
failed: number;
|