cartethyia 2.7.9 → 2.8.1
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 +6 -5
- package/dist/dashboard/assets/{BannedPage-BxoG1MrL.js → BannedPage-Dq530awD.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-ixwgKk53.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BmttNV3U.js → CliToolsPage-DaZZ3cqP.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BE3qwwfC.js → CombosPage-BlC_xdkX.js} +1 -1
- package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CRZdmMIX.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-Dv_FJ6Er.js → CustomizationPage-7xT5yg2E.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-BOoO8rpz.js → InjectSystemPromptPanel-6Teoq9Se.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-ChJuNNVN.js → LoginPage-CZuI0MAR.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BZAJtPg1.js → ModelPicker-C1hNTlkr.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
- package/dist/dashboard/assets/{OverviewPage-BE40s3u5.js → OverviewPage-0yfhSI1C.js} +1 -1
- package/dist/dashboard/assets/OverviewPage-0yfhSI1C.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js.gz +0 -0
- package/dist/dashboard/assets/{ProviderIcon-CQ6bPUf_.js → ProviderIcon-Ddhm1InL.js} +1 -1
- package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js +9 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-C5HPWqEv.js → SettingsPage-BzZAvYSX.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-CFNcATJJ.js → SetupPage--s-EfEIL.js} +1 -1
- package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-YoD4tY5l.js → StudioPage-BjXFGSK4.js} +1 -1
- package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-DxjaAhb_.js → UsagePage-CP8F8BN7.js} +1 -1
- package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BTlkq_Uk.js → api-keys-2hc12Uz2.js} +1 -1
- package/dist/dashboard/assets/api-keys-2hc12Uz2.js.gz +0 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css +2 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css.gz +0 -0
- package/dist/dashboard/assets/assert-BMcj1xZE.js +1 -0
- package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DdbT_7Am.js → badge-DkRUuVfz.js} +1 -1
- package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
- package/dist/dashboard/assets/{banner-CsGN-nbm.js → banner-CDjW841Q.js} +1 -1
- package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
- package/dist/dashboard/assets/{card-mgZ_TLLZ.js → card-Bto5UlL9.js} +1 -1
- package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-A1-v8-03.js → cli-tools-DaH6g9hL.js} +1 -1
- package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-CJ4Zty89.js → clipboard-button-BYKBCzjk.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-B5L2jPQ_.js → dialog-DL0Zftwv.js} +1 -1
- package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-DkjUouAj.js → drawer-Dpx_UXaS.js} +1 -1
- package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
- package/dist/dashboard/assets/{entry-B-LVLV6o.js → entry-BV7-1NoM.js} +1 -1
- package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CAhdYl3B.js → entry-CGvdNU6r.js} +1 -1
- package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
- package/dist/dashboard/assets/entry-DScKYvZj.js +4 -0
- package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-CrsdLVsn.js → formatters-BS3DZlZj.js} +1 -1
- package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
- package/dist/dashboard/assets/{index-BU64Lqvp.js → index-SQnvvTWX.js} +2 -2
- package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2aDLyyg.js → inline-Dl-cuLK0.js} +1 -1
- package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
- package/dist/dashboard/assets/{input-BCVkdR9d.js → input-Bfx0EzRc.js} +1 -1
- package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
- package/dist/dashboard/assets/{layout-DNcAdMK-.js → layout-BxhbQ_JS.js} +1 -1
- package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-DgrIH0hw.js → list-row-D49Y4ptn.js} +1 -1
- package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
- package/dist/dashboard/assets/{live-Cae01iu8.js → live-CbVTdX9V.js} +1 -1
- package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
- package/dist/dashboard/assets/{network-CRib7H_2.js → network-JGvDveof.js} +1 -1
- package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BKQE8Xpz.js → numbers-Dw6kiew-.js} +1 -1
- package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-ecPvG_8g.js → pagination-iUoI8GeE.js} +1 -1
- package/dist/dashboard/assets/pagination-iUoI8GeE.js.gz +0 -0
- package/dist/dashboard/assets/providers/sensenova-intl.png +0 -0
- package/dist/dashboard/assets/providers/sensenova.png +0 -0
- package/dist/dashboard/assets/providers/zro.svg +3 -0
- package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
- package/dist/dashboard/assets/{routing-Ckm1UXuC.js → routing-8jZr-3WE.js} +1 -1
- package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
- package/dist/dashboard/assets/{select-ChoFWyKz.js → select-oO1FxMVb.js} +2 -2
- package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
- package/dist/dashboard/assets/{settings-CKjFPlKW.js → settings-CLr3kdIx.js} +1 -1
- package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DWCwU3fh.js → stack-Y3pDteZr.js} +1 -1
- package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
- package/dist/dashboard/assets/{state-Cg3VL5J_.js → state-CArX_Iak.js} +1 -1
- package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
- package/dist/dashboard/assets/{switch-Db70BMix.js → switch-DwlwmDO-.js} +1 -1
- package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BqdE0LuM.js → useMutation-DJ9GYEgf.js} +1 -1
- package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -53
- package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
- package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
- package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
- package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
- package/package.json +1 -1
- package/src/boot/app.ts +11 -14
- package/src/boot/dependencies.ts +5 -7
- package/src/boot/flush.ts +20 -0
- package/src/boot/lifecycle.ts +7 -5
- package/src/config/providers.ts +6 -5
- package/src/config/routing.ts +26 -0
- package/src/config/spec-metadata.ts +72 -0
- package/src/config/spec.ts +28 -8
- package/src/console/api-keys/contracts.ts +12 -1
- package/src/console/api-keys/routes.ts +40 -28
- package/src/console/auth/account-reset.ts +7 -9
- package/src/console/backup/contracts.ts +4 -0
- package/src/console/backup/export-endpoint.ts +7 -5
- package/src/console/backup/nine-router.ts +71 -10
- package/src/console/backup/service.ts +8 -1
- package/src/console/cli-tools/service.ts +6 -12
- package/src/console/console-router-guards.ts +23 -16
- package/src/console/console-router.ts +1 -1
- package/src/console/domain-registration.ts +7 -4
- package/src/console/inferhub/client.ts +1 -1
- package/src/console/inferhub/normalize.ts +24 -28
- package/src/console/inferhub/types.ts +3 -0
- package/src/console/ip-bans/routes.ts +7 -13
- package/src/console/observability/live.ts +2 -2
- package/src/console/observability/store.ts +0 -53
- package/src/console/providers/catalog/contracts.ts +9 -3
- package/src/console/providers/catalog/model-operations.ts +2 -3
- package/src/console/providers/catalog/provider-operations.ts +1 -1
- package/src/console/providers/catalog/routes.ts +3 -2
- package/src/console/providers/catalog/store.ts +1914 -1902
- package/src/console/providers/detail/routes.ts +1 -0
- package/src/console/providers/detail/store.ts +4 -0
- package/src/console/providers/oauth/callback-listener.ts +30 -11
- package/src/console/providers/oauth/device-egress.ts +123 -0
- package/src/console/providers/oauth/routes.ts +123 -16
- package/src/console/quota/account-quota-global.ts +14 -24
- package/src/console/quota/account-quota-shared.ts +49 -0
- package/src/console/quota/account-quota-tenant.ts +11 -23
- package/src/console/quota/account-test-egress.ts +2 -2
- package/src/console/quota/daily-growth-job.ts +1 -1
- package/src/console/quota/quota-view.ts +19 -3
- package/src/console/routing/pools/contracts.ts +1 -1
- package/src/console/routing/pools/operations.ts +32 -47
- package/src/console/routing/pools/probe-verdict.ts +2 -2
- package/src/console/routing/pools/relay-deploy.ts +1 -1
- package/src/console/routing/pools/store.ts +4 -4
- package/src/console/shared/errors.ts +33 -6
- package/src/main.ts +2 -3
- package/src/network/client.ts +67 -0
- package/src/network/egress/bidi-session.ts +322 -0
- package/src/network/egress/dial.ts +201 -0
- package/src/network/egress/factory.ts +191 -0
- package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
- package/src/network/egress/tls-profile.ts +123 -0
- package/src/network/egress/ws.ts +175 -0
- package/src/network/pool/{byte-accounting.ts → accounting.ts} +3 -2
- package/src/network/{types.ts → pool/agent-config.ts} +1 -1
- package/src/network/pool/agent.ts +2 -2
- package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +95 -20
- package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
- package/src/network/pool/resolver.ts +10 -849
- package/src/network/pool/selector.ts +184 -41
- package/src/network/ssrf.ts +7 -7
- package/src/network/{retry.ts → util/retry.ts} +1 -1
- package/src/network/{rate-limit-verdict.ts → verdict.ts} +1 -1
- package/src/observability/event-log.ts +7 -361
- package/src/observability/performance-metrics.ts +9 -0
- package/src/observability/runtime-metrics.ts +14 -9
- package/src/observability/telemetry-buffer.ts +16 -26
- package/src/observability/telemetry-reconciliation.ts +9 -37
- package/src/observability/telemetry-row.ts +125 -0
- package/src/persistence/db-mode.ts +4 -1
- package/src/persistence/dialect.ts +18 -0
- package/src/persistence/migrate.ts +6 -3
- package/src/persistence/schema.ts +17 -0
- package/src/persistence/ttl-cache.ts +21 -3
- package/src/protocol/codecs.ts +59 -2
- package/src/protocol/dsml.ts +34 -86
- package/src/protocol/markup-stream-parser.ts +195 -0
- package/src/protocol/reasoning-text.ts +19 -0
- package/src/protocol/request/chat.ts +10 -10
- package/src/protocol/request/gemini.ts +8 -2
- package/src/protocol/request/messages.ts +34 -22
- package/src/protocol/response/chat.ts +12 -20
- package/src/protocol/response/messages.ts +34 -7
- package/src/protocol/response/omp-tool-call-markup.ts +43 -86
- package/src/protocol/response/responses.ts +18 -2
- package/src/protocol/stop-reason.ts +23 -1
- package/src/protocol/think-split.ts +112 -0
- package/src/protocol/translation/dialect/qwen.ts +26 -58
- package/src/protocol/translation/quirks.ts +15 -0
- package/src/protocol/upstream/send-claude-messages.ts +2 -2
- package/src/providers/adapters/compatible-adapter.ts +76 -12
- package/src/providers/authentication/oauth-client.ts +40 -6
- package/src/providers/authentication/oauth-device-flow.ts +105 -11
- package/src/providers/authentication/oauth-flow-store.ts +82 -1
- package/src/providers/authentication/oauth-refresh-service.ts +766 -758
- package/src/providers/configured-provider.ts +148 -0
- package/src/providers/default-registry.ts +137 -36
- package/src/providers/discovery/discovery-types.ts +7 -0
- package/src/providers/discovery/probe-phases.ts +20 -6
- package/src/providers/discovery/probing-service.ts +22 -2
- package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
- package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +16 -1
- package/src/providers/integrations/buddy/account.ts +969 -0
- package/src/providers/integrations/buddy/capabilities.ts +12 -12
- package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
- package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +176 -20
- package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
- package/src/providers/integrations/buddy/codebuddy/shared.ts +41 -26
- package/src/providers/integrations/buddy/headers.ts +311 -0
- package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
- package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
- package/src/providers/integrations/claude/claude-quota.ts +14 -17
- package/src/providers/integrations/cline/cline-oauth.ts +10 -5
- package/src/providers/integrations/cline/cline.ts +5 -6
- package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
- package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
- package/src/providers/integrations/clixad/provider.ts +145 -0
- package/src/providers/integrations/codex/codex-oauth.ts +8 -5
- package/src/providers/integrations/commandcode/quota.ts +22 -28
- package/src/providers/integrations/cursor/cursor-auth.ts +1 -9
- package/src/providers/integrations/cursor/cursor-exec.ts +9 -2
- package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
- package/src/providers/integrations/cursor/cursor-request.ts +26 -12
- package/src/providers/integrations/cursor/cursor-shared.ts +8 -5
- package/src/providers/integrations/cursor/cursor.ts +574 -110
- package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
- package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
- package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
- package/src/providers/integrations/deepai/deepai-common.ts +145 -0
- package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
- package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
- package/src/providers/integrations/deepai/provider.ts +42 -0
- package/src/providers/integrations/devin/devin-auth.ts +7 -0
- package/src/providers/integrations/devin/protocol/devin-proto.ts +1 -27
- package/src/providers/integrations/factory/factory-oauth.ts +33 -51
- package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
- package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
- package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
- package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
- package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
- package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
- package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
- package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
- package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
- package/src/providers/integrations/freebuff/freebuff.ts +682 -0
- package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
- package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
- package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
- package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
- package/src/providers/integrations/freepi/freepi-session.ts +158 -0
- package/src/providers/integrations/freepi/freepi.ts +80 -54
- package/src/providers/integrations/github/github-discovery.ts +30 -13
- package/src/providers/integrations/github/github-oauth.ts +217 -44
- package/src/providers/integrations/github/github-quota.ts +22 -5
- package/src/providers/integrations/github/github.ts +40 -32
- package/src/providers/integrations/grok/grok-oauth.ts +52 -49
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
- package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
- package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
- package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +42 -13
- package/src/providers/integrations/hermes/index.ts +2 -0
- package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
- package/src/providers/integrations/kilo/kilo.ts +1 -1
- package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
- package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
- package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
- package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
- package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
- package/src/providers/integrations/kiro/kiro.ts +15 -19
- package/src/providers/integrations/muse/muse-oauth.ts +5 -5
- package/src/providers/integrations/ollamacloud/index.ts +2 -0
- package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
- package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
- package/src/providers/integrations/openrouter/provider.ts +1 -1
- package/src/providers/integrations/perch/perch-credential.ts +25 -0
- package/src/providers/integrations/perch/perch-protocol.ts +0 -6
- package/src/providers/integrations/perch/perch-quota.ts +2 -16
- package/src/providers/integrations/perch/perch.ts +6 -16
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
- package/src/providers/integrations/qoder/qoder-adapter.ts +20 -125
- package/src/providers/integrations/qoder/qoder-auth.ts +109 -30
- package/src/providers/integrations/qoder/qoder-catalog.ts +21 -6
- package/src/providers/integrations/qoder/qoder-chat.ts +32 -29
- package/src/providers/integrations/qoder/qoder-checkin.ts +5 -5
- package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
- package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
- package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
- package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
- package/src/providers/integrations/qoder/qoder-types.ts +9 -0
- package/src/providers/integrations/qoder/qoder.ts +3 -26
- package/src/providers/integrations/sensenova/provider.ts +144 -0
- package/src/providers/integrations/trae/trae-protocol.ts +9 -5
- package/src/providers/integrations/trae/trae-quota.ts +21 -29
- package/src/providers/integrations/trae/trae-realm.ts +38 -0
- package/src/providers/integrations/trae/trae.ts +7 -24
- package/src/providers/integrations/vercel/provider.ts +117 -0
- package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
- package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
- package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
- package/src/providers/integrations/zro/zro.ts +205 -0
- package/src/providers/operations/account-error-classification.ts +199 -307
- package/src/providers/operations/account-health-service.ts +82 -1
- package/src/providers/operations/account-refresh-status.ts +36 -37
- package/src/providers/operations/account-tier.ts +15 -16
- package/src/providers/operations/client-version-resolver.ts +1 -1
- package/src/providers/operations/client-versions.ts +100 -81
- package/src/providers/operations/cooldown-rules.ts +11 -16
- package/src/providers/operations/oauth-refresh-lead.ts +46 -22
- package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
- package/src/providers/operations/provider-catalog-service.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +118 -51
- package/src/providers/operations/provider-rules/github.ts +48 -0
- package/src/providers/operations/provider-rules/grok.ts +33 -0
- package/src/providers/operations/provider-rules/inception.ts +49 -0
- package/src/providers/operations/provider-rules/index.ts +32 -0
- package/src/providers/operations/provider-rules/perch.ts +37 -0
- package/src/providers/operations/provider-rules/qoder.ts +70 -0
- package/src/providers/operations/provider-rules/trae.ts +81 -0
- package/src/providers/operations/session-resolution.ts +17 -1
- package/src/providers/provider-contracts.ts +6 -4
- package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
- package/src/providers/provider-metadata.ts +70 -10
- package/src/providers/provider-probe.ts +15 -1
- package/src/providers/provider-registry.ts +42 -33
- package/src/providers/quota/quota-contracts.ts +2 -0
- package/src/providers/quota/quota-limit-windows.ts +16 -5
- package/src/providers/quota/quota-support.ts +1 -1
- package/src/providers/quota/quota-window-parser.ts +90 -18
- package/src/providers/quota/refresh.ts +100 -21
- package/src/security/api-key-auth-cache.ts +1 -1
- package/src/security/api-key-auth.ts +6 -2
- package/src/security/ip-boundary.ts +1 -1
- package/src/transport/dispatch/attempt-finalize.ts +15 -14
- package/src/transport/dispatch/attempt-loop.ts +73 -19
- package/src/transport/dispatch/generation.ts +2 -8
- package/src/transport/dispatch/handler-deps.ts +1 -1
- package/src/transport/dispatch/leases.ts +1 -1
- package/src/transport/dispatch/native-shared.ts +25 -0
- package/src/transport/dispatch/proxy-request.ts +8 -46
- package/src/transport/dispatch/responses-compact.ts +2 -8
- package/src/transport/dispatch/retry-policy.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +10 -7
- package/src/transport/dispatch/streaming/stream.ts +9 -37
- package/src/transport/dispatch/systemone.ts +2 -8
- package/src/transport/dispatch/token-count.ts +2 -6
- package/src/transport/dispatch/upstream.ts +63 -0
- package/src/transport/drain-endpoint.ts +31 -4
- package/src/transport/failure-policy.ts +16 -30
- package/src/transport/guards/body-policy.ts +7 -1
- package/src/transport/guards/error-lifecycle.ts +13 -1
- package/src/transport/guards/request-context.ts +22 -14
- package/src/transport/request/preparer.ts +81 -71
- package/src/transport/routing/bounded-lru.ts +14 -0
- package/src/transport/routing/drain-reserve.ts +129 -0
- package/src/transport/routing/plan-gate.ts +7 -1
- package/src/transport/routing/round-robin.ts +60 -0
- package/src/transport/routing/route-catalog.ts +123 -111
- package/src/transport/routing/route-model.ts +51 -9
- package/src/transport/routing/router.ts +393 -32
- package/src/transport/shutdown-notice.ts +1 -1
- package/src/transport/surface/adapters.ts +11 -2
- package/src/transport/surface/chat/adapter.ts +2 -2
- package/src/transport/surface/chat/encode.ts +47 -4
- package/src/transport/surface/chat/parse.ts +2 -12
- package/src/transport/surface/completion.ts +44 -33
- package/src/transport/surface/content-parts.ts +21 -0
- package/src/transport/surface/messages/adapter.ts +7 -1
- package/src/transport/surface/messages/encode.ts +11 -3
- package/src/transport/surface/messages/parse.ts +2 -1
- package/src/transport/surface/messages/stream.ts +33 -17
- package/src/transport/surface/responses/encode.ts +22 -5
- package/src/transport/surface/responses/parse.ts +3 -11
- package/src/workers/buddy-growth-tasks.ts +8 -8
- package/src/workers/daily-checkin.ts +4 -4
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/zcode-gift-claim.ts +1 -1
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +0 -8
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js +0 -9
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js +0 -1
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css +0 -2
- package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js +0 -1
- package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
- package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
- package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
- package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
- package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
- package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
- package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js +0 -4
- package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
- package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
- package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
- package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
- package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
- package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
- package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
- package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
- package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
- package/dist/dashboard/assets/pagination-ecPvG_8g.js.gz +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
- package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
- package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
- package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
- package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
- package/src/network/pool-health.ts +0 -210
- package/src/providers/bundled-discovery-providers.ts +0 -98
- package/src/providers/configured-openai-providers.ts +0 -120
- package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
- package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
- package/src/providers/integrations/buddy/buddy-identity.ts +0 -300
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
- package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
- package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -185
- package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
- package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -175
- package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
- package/src/providers/integrations/qoder/chat-proto.ts +0 -328
- package/src/providers/integrations/qoder/qoder-grpc.ts +0 -871
- package/src/providers/integrations/qoder/qoder-ide.ts +0 -588
- package/src/providers/operations/model-discovery-cache.ts +0 -34
- package/src/providers/operations/probe-fetch.ts +0 -15
- package/src/providers/operations/provider-version-cache.ts +0 -30
- package/src/providers/provider-capabilities.ts +0 -33
- package/src/providers/provider-credential-envelope.ts +0 -43
- /package/dist/dashboard/assets/{app-B7kIWFm5.js → app-C0gTXEoN.js} +0 -0
- /package/dist/dashboard/assets/{app-B7kIWFm5.js.gz → app-C0gTXEoN.js.gz} +0 -0
- /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
- /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
- /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
- /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
- /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
- /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
type QuotaRefreshFailureCode,
|
|
30
30
|
} from "../../providers/quota/refresh";
|
|
31
31
|
import { selectAccountTestEgress } from "./account-test-egress";
|
|
32
|
-
import type { ValidatedFetch } from "../../network/
|
|
32
|
+
import type { ValidatedFetch } from "../../network/egress/fetch";
|
|
33
33
|
import type { ProviderId } from "../../providers/provider-contracts";
|
|
34
34
|
import {
|
|
35
35
|
DAILY_CHECKIN_PROVIDER_IDS,
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
fetchDailyCheckin,
|
|
44
44
|
type BuddyActivityReportResult,
|
|
45
45
|
type DailyCheckinResult,
|
|
46
|
-
} from "../../providers/integrations/buddy/
|
|
46
|
+
} from "../../providers/integrations/buddy/lifecycle";
|
|
47
47
|
import {
|
|
48
48
|
fetchQoderDailyCheckin,
|
|
49
49
|
isQoderCheckinProvider,
|
|
@@ -72,9 +72,8 @@ import {
|
|
|
72
72
|
toQuotaView,
|
|
73
73
|
} from "./quota-view";
|
|
74
74
|
import {
|
|
75
|
-
|
|
75
|
+
resolveNextAccountStatus,
|
|
76
76
|
type AccountQuotaRoutesDeps,
|
|
77
|
-
type AccountStatus,
|
|
78
77
|
} from "./account-quota-shared";
|
|
79
78
|
import {
|
|
80
79
|
DAILY_GROWTH_LEG_TIMEOUT_MS,
|
|
@@ -195,7 +194,7 @@ async function runDailyGrowthForAccount(
|
|
|
195
194
|
// The buddy-oauth-shared graph is lazy-loaded: the uid decoder is only
|
|
196
195
|
// needed for buddy accounts' report leg (B-02).
|
|
197
196
|
const { buddyAccountUid } = await import(
|
|
198
|
-
"../../providers/integrations/buddy/
|
|
197
|
+
"../../providers/integrations/buddy/account"
|
|
199
198
|
);
|
|
200
199
|
const userId = buddyAccountUid(credential);
|
|
201
200
|
let report: { ok: boolean; message: string };
|
|
@@ -428,7 +427,7 @@ export function registerAccountQuotaTenantRoutes(
|
|
|
428
427
|
// The buddy-usage-store graph is lazy-loaded: it is only needed when the
|
|
429
428
|
// page actually renders buddy accounts (B-02).
|
|
430
429
|
const { readBuddyUsageSnapshots } = await import(
|
|
431
|
-
"../../providers/integrations/buddy/
|
|
430
|
+
"../../providers/integrations/buddy/account"
|
|
432
431
|
);
|
|
433
432
|
const usageByAccount = await readBuddyUsageSnapshots(
|
|
434
433
|
db,
|
|
@@ -745,23 +744,12 @@ export function registerAccountQuotaTenantRoutes(
|
|
|
745
744
|
},
|
|
746
745
|
async ({ request, params, body, set }) => {
|
|
747
746
|
const access = requireTenantScope(accessResolver(request), "dashboard:write");
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
code: "invalid_status",
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
const nextStatus: AccountStatus | undefined =
|
|
756
|
-
body.active !== undefined
|
|
757
|
-
? body.active
|
|
758
|
-
? "active"
|
|
759
|
-
: "disabled"
|
|
760
|
-
: (body.status as AccountStatus | undefined);
|
|
761
|
-
if (!nextStatus) {
|
|
762
|
-
set.status = 400;
|
|
763
|
-
return { error: "active or status is required", code: "invalid_request" };
|
|
747
|
+
const resolved = resolveNextAccountStatus(body);
|
|
748
|
+
if (!resolved.ok) {
|
|
749
|
+
set.status = resolved.status;
|
|
750
|
+
return resolved.body;
|
|
764
751
|
}
|
|
752
|
+
const nextStatus = resolved.nextStatus;
|
|
765
753
|
const updated = await db
|
|
766
754
|
.update(providerAccounts)
|
|
767
755
|
.set({ status: nextStatus })
|
|
@@ -1262,7 +1250,7 @@ export function registerAccountQuotaTenantRoutes(
|
|
|
1262
1250
|
// The buddy-oauth-shared graph is lazy-loaded: the uid decoder is only
|
|
1263
1251
|
// needed on this buddy-only surface (B-02).
|
|
1264
1252
|
const { buddyAccountUid } = await import(
|
|
1265
|
-
"../../providers/integrations/buddy/
|
|
1253
|
+
"../../providers/integrations/buddy/account"
|
|
1266
1254
|
);
|
|
1267
1255
|
const userId = buddyAccountUid(credential);
|
|
1268
1256
|
if (!userId) {
|
|
@@ -11,8 +11,8 @@ import { networkPools, poolRoutingSettings, providerRoutingSettings } from "../.
|
|
|
11
11
|
import { resolveTenantOverride } from "../../persistence/tenant-scope";
|
|
12
12
|
import { GatewayError } from "../../protocol/gateway-error";
|
|
13
13
|
import type { NetworkPoolSelector, PoolRotation } from "../../network/pool/selector";
|
|
14
|
-
import type { ValidatedFetch } from "../../network/
|
|
15
|
-
import type { ValidatedNetworkBindingFactory } from "../../network/
|
|
14
|
+
import type { ValidatedFetch } from "../../network/egress/fetch";
|
|
15
|
+
import type { ValidatedNetworkBindingFactory } from "../../network/egress/factory";
|
|
16
16
|
import { DEFAULT_PROXY_SKIP_PROVIDER_IDS } from "../../providers/provider-metadata";
|
|
17
17
|
import type { FetchLike } from "../../providers/quota/quota-contracts";
|
|
18
18
|
|
|
@@ -31,7 +31,7 @@ import { randomUUID } from "node:crypto";
|
|
|
31
31
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
32
32
|
import type { RedisClient } from "../../persistence/redis";
|
|
33
33
|
import { checkinAttemptedToday } from "../../workers/daily-checkin";
|
|
34
|
-
import type { ValidatedFetch } from "../../network/
|
|
34
|
+
import type { ValidatedFetch } from "../../network/egress/fetch";
|
|
35
35
|
|
|
36
36
|
/** Lifecycle states of a daily-growth job. */
|
|
37
37
|
export type DailyGrowthJobState = "queued" | "running" | "done" | "failed";
|
|
@@ -13,7 +13,14 @@ import {
|
|
|
13
13
|
/** Shape returned for quota data on dashboard account responses. */
|
|
14
14
|
interface QuotaRefreshView {
|
|
15
15
|
readonly source: string;
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* `ready` = a usable quota reading exists (successful fetch, cached or
|
|
18
|
+
* snapshot). `error` = the collector returned an error with no usable
|
|
19
|
+
* reading. `refreshing` = a background fill is in flight. `unknown` = no
|
|
20
|
+
* quota evidence at all (no collector surface, or an empty successful read
|
|
21
|
+
* with no timestamp) — the canonical wire representation for unknown quota.
|
|
22
|
+
*/
|
|
23
|
+
readonly status: "ready" | "error" | "refreshing" | "unknown";
|
|
17
24
|
readonly plan: string | null;
|
|
18
25
|
readonly windows: ProviderQuotaResult["windows"];
|
|
19
26
|
readonly fetchedAt: string | null;
|
|
@@ -94,7 +101,7 @@ export function toQuotaView(
|
|
|
94
101
|
lastAttemptAt?: string | null;
|
|
95
102
|
lastSuccessAt?: string | null;
|
|
96
103
|
refreshError?: string | null;
|
|
97
|
-
status?: "ready" | "error" | "refreshing";
|
|
104
|
+
status?: "ready" | "error" | "refreshing" | "unknown";
|
|
98
105
|
} = {},
|
|
99
106
|
): QuotaRefreshView {
|
|
100
107
|
const fetchedAt = timestamps.fetchedAt ?? account.lastSuccessAt?.toISOString() ?? null;
|
|
@@ -112,9 +119,18 @@ export function toQuotaView(
|
|
|
112
119
|
// that the last attempt failed; only `quota.error` — no usable reading at
|
|
113
120
|
// all — makes the card an error.
|
|
114
121
|
const error = quota.error ?? null;
|
|
122
|
+
// Unknown quota: a successful read that carries no windows AND no timestamp
|
|
123
|
+
// is not evidence of anything — the canonical "no quota evidence" state.
|
|
124
|
+
// (A provider with a real zero-balance still reports windows/timestamps.)
|
|
125
|
+
const derivedStatus =
|
|
126
|
+
error !== null
|
|
127
|
+
? ("error" as const)
|
|
128
|
+
: quota.windows.length === 0 && fetchedAt === null
|
|
129
|
+
? ("unknown" as const)
|
|
130
|
+
: ("ready" as const);
|
|
115
131
|
return {
|
|
116
132
|
source: quota.source,
|
|
117
|
-
status: timestamps.status ??
|
|
133
|
+
status: timestamps.status ?? derivedStatus,
|
|
118
134
|
plan: quota.plan ?? null,
|
|
119
135
|
windows: quota.windows,
|
|
120
136
|
fetchedAt,
|
|
@@ -10,7 +10,7 @@ import { isAddressAllowed } from "../../../network/ssrf";
|
|
|
10
10
|
import { TRANSPORT_KINDS, type TransportKind } from "../../../network/pool/agent";
|
|
11
11
|
import type { SsrfPolicy } from "../../../config/network";
|
|
12
12
|
import type { NetworkPoolSelector } from "../../../network/pool/selector";
|
|
13
|
-
import type { PoolHealthEvent } from "../../../network/pool
|
|
13
|
+
import type { PoolHealthEvent } from "../../../network/pool/health";
|
|
14
14
|
|
|
15
15
|
export interface CreateNetworkPoolRequest {
|
|
16
16
|
kind: TransportKind;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { ConsoleDomainError, requireTenantScope } from "../../shared/errors";
|
|
5
5
|
import { providerRateLimitIsIpScoped } from "../../../providers/provider-metadata";
|
|
6
6
|
import { resolvePoolProviderRateLimitCooldownMs } from "../../../config/cooldowns";
|
|
7
|
-
import type { PoolHealthEvent } from "../../../network/pool
|
|
7
|
+
import type { PoolHealthEvent } from "../../../network/pool/health";
|
|
8
8
|
import type { AccessDecision } from "../../../security/access-control";
|
|
9
9
|
import {
|
|
10
10
|
deployRelay as deployRelayWorker,
|
|
@@ -45,35 +45,40 @@ function isBlockedEntry(entry: { readonly blocked?: boolean }): boolean {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export function createNetworkPoolOperations(config: NetworkPoolConfig) {
|
|
48
|
+
/**
|
|
49
|
+
* The read-only enrichment projection shared by the pool list and the pool
|
|
50
|
+
* detail page: live inflight (falling back to the stored row counter when no
|
|
51
|
+
* selector is configured), the derived available/utilization figures, and the
|
|
52
|
+
* active provider cooldowns (omitted entirely when there are none).
|
|
53
|
+
*/
|
|
54
|
+
async function enrichPoolResponse(res: NetworkPoolResponse, poolId: string): Promise<NetworkPoolResponse> {
|
|
55
|
+
const cooldowns = await config.poolSelector?.getPoolCooldowns(poolId);
|
|
56
|
+
const inflight = config.poolSelector
|
|
57
|
+
? await config.poolSelector.getInflightAuthoritative(poolId)
|
|
58
|
+
: res.inflight;
|
|
59
|
+
return {
|
|
60
|
+
...res,
|
|
61
|
+
inflight,
|
|
62
|
+
available: Math.max(0, res.maxInflight - inflight),
|
|
63
|
+
utilization: res.maxInflight > 0 ? Math.min(1, inflight / res.maxInflight) : 1,
|
|
64
|
+
...(cooldowns && cooldowns.length > 0
|
|
65
|
+
? {
|
|
66
|
+
providerCooldowns: cooldowns.map((c) => ({
|
|
67
|
+
providerId: c.providerId,
|
|
68
|
+
until: new Date(c.until).toISOString(),
|
|
69
|
+
reason: c.reason,
|
|
70
|
+
blocked: isBlockedEntry(c),
|
|
71
|
+
})),
|
|
72
|
+
}
|
|
73
|
+
: {}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
48
77
|
const operations = {
|
|
49
78
|
async listPools(access: AccessDecision | undefined): Promise<NetworkPoolResponse[]> {
|
|
50
79
|
const a = requireTenantScope(access, "dashboard:read");
|
|
51
80
|
const rawPools = await config.store.list(a.tenantId);
|
|
52
|
-
return Promise.all(
|
|
53
|
-
rawPools.map(async (r) => {
|
|
54
|
-
const res = sanitizePoolResponse(r);
|
|
55
|
-
const cooldowns = await config.poolSelector?.getPoolCooldowns(r.id);
|
|
56
|
-
const inflight = config.poolSelector
|
|
57
|
-
? await config.poolSelector.getInflightAuthoritative(r.id)
|
|
58
|
-
: res.inflight;
|
|
59
|
-
return {
|
|
60
|
-
...res,
|
|
61
|
-
inflight,
|
|
62
|
-
available: Math.max(0, res.maxInflight - inflight),
|
|
63
|
-
utilization: res.maxInflight > 0 ? Math.min(1, inflight / res.maxInflight) : 1,
|
|
64
|
-
...(cooldowns && cooldowns.length > 0
|
|
65
|
-
? {
|
|
66
|
-
providerCooldowns: cooldowns.map((c) => ({
|
|
67
|
-
providerId: c.providerId,
|
|
68
|
-
until: new Date(c.until).toISOString(),
|
|
69
|
-
reason: c.reason,
|
|
70
|
-
blocked: isBlockedEntry(c),
|
|
71
|
-
})),
|
|
72
|
-
}
|
|
73
|
-
: {}),
|
|
74
|
-
};
|
|
75
|
-
}),
|
|
76
|
-
);
|
|
81
|
+
return Promise.all(rawPools.map(async (r) => enrichPoolResponse(sanitizePoolResponse(r), r.id)));
|
|
77
82
|
},
|
|
78
83
|
async getPoolDetail(
|
|
79
84
|
access: AccessDecision | undefined,
|
|
@@ -82,27 +87,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
|
|
|
82
87
|
const a = requireTenantScope(access, "dashboard:read");
|
|
83
88
|
const rec = await config.store.get(a.tenantId, poolId);
|
|
84
89
|
if (!rec) throw new ConsoleDomainError("pool_not_found", 404, `Pool ${poolId} not found`);
|
|
85
|
-
|
|
86
|
-
const cooldowns = await config.poolSelector?.getPoolCooldowns(poolId);
|
|
87
|
-
const inflight = config.poolSelector
|
|
88
|
-
? await config.poolSelector.getInflightAuthoritative(poolId)
|
|
89
|
-
: res.inflight;
|
|
90
|
-
return {
|
|
91
|
-
...res,
|
|
92
|
-
inflight,
|
|
93
|
-
available: Math.max(0, res.maxInflight - inflight),
|
|
94
|
-
utilization: res.maxInflight > 0 ? Math.min(1, inflight / res.maxInflight) : 1,
|
|
95
|
-
...(cooldowns && cooldowns.length > 0
|
|
96
|
-
? {
|
|
97
|
-
providerCooldowns: cooldowns.map((c) => ({
|
|
98
|
-
providerId: c.providerId,
|
|
99
|
-
until: new Date(c.until).toISOString(),
|
|
100
|
-
reason: c.reason,
|
|
101
|
-
blocked: isBlockedEntry(c),
|
|
102
|
-
})),
|
|
103
|
-
}
|
|
104
|
-
: {}),
|
|
105
|
-
};
|
|
90
|
+
return enrichPoolResponse(sanitizePoolResponse(rec), poolId);
|
|
106
91
|
},
|
|
107
92
|
async clearPoolCooldown(
|
|
108
93
|
access: AccessDecision | undefined,
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
* pool for good.
|
|
18
18
|
*
|
|
19
19
|
* The 403-free-tier and 429 verdicts are the probe's reading of the same facts
|
|
20
|
-
* `classifyEgressFailure` (in `network/
|
|
20
|
+
* `classifyEgressFailure` (in `network/verdict`) reads on the
|
|
21
21
|
* dispatch path — for the probe they arrive as an HTTP status + body instead of
|
|
22
22
|
* a `GatewayError`, so the shared free-tier regex is borrowed from there to
|
|
23
23
|
* keep the two classifiers' boundaries honest.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { FREE_TIER_REFUSAL } from "../../../network/
|
|
26
|
+
import { FREE_TIER_REFUSAL } from "../../../network/verdict";
|
|
27
27
|
|
|
28
28
|
export type ProbeVerdict = "allowed" | "blocked" | "throttled" | "unreachable";
|
|
29
29
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Cartethyia already knows how to *use* a hosted relay: a pool whose endpoint
|
|
6
6
|
* host is `*.workers.dev` / `*.vercel.app` / `*.deno.dev` is classified as an
|
|
7
7
|
* application relay and dialed with `x-relay-target` / `x-relay-path` headers
|
|
8
|
-
* (see `network/pool/agent.ts` and `network/
|
|
8
|
+
* (see `network/pool/agent.ts` and `network/egress/fetch.ts`). This module
|
|
9
9
|
* supplies the other half — the operator does not have to write and deploy the
|
|
10
10
|
* worker by hand.
|
|
11
11
|
*
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./contracts";
|
|
17
17
|
import { classifyPoolConnectError, classifyPoolProbeResponse, parseEgressIp } from "./probe-result";
|
|
18
18
|
import { encryptCredential } from "../../../security/crypto";
|
|
19
|
-
import { createValidatedFetch } from "../../../network/
|
|
19
|
+
import { createValidatedFetch } from "../../../network/egress/fetch";
|
|
20
20
|
import { classifyProviderProbeAnswer } from "./probe-verdict";
|
|
21
21
|
import { buildProviderProbeRequest } from "../../../providers/provider-probe";
|
|
22
22
|
import type { StoredProviderProbe } from "./contracts";
|
|
@@ -34,9 +34,9 @@ import {
|
|
|
34
34
|
disablePoolForProxyHttpStatus,
|
|
35
35
|
listNetworkPoolHealthEvents,
|
|
36
36
|
recoverNetworkPool,
|
|
37
|
-
} from "../../../network/pool
|
|
38
|
-
import { drainPoolByteDelta, recordPoolBytes } from "../../../network/pool/
|
|
39
|
-
import * as poolCooldownStore from "../../../network/pool
|
|
37
|
+
} from "../../../network/pool/health";
|
|
38
|
+
import { drainPoolByteDelta, recordPoolBytes } from "../../../network/pool/accounting";
|
|
39
|
+
import * as poolCooldownStore from "../../../network/pool/cooldown-store";
|
|
40
40
|
// Cloudflare's trace endpoint answers with plain `key=value` lines including
|
|
41
41
|
// `ip=`, the address the request egressed from. Dialing it THROUGH the pool
|
|
42
42
|
// therefore reports the pool's public address — which is what an operator
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ValidationError } from "elysia";
|
|
2
2
|
import type { AccessDecision, AccessScope } from "../../security/access-control";
|
|
3
3
|
import { isRecord } from "../../protocol/codecs";
|
|
4
|
-
import { formatPublicErrorMessage } from "../../protocol/gateway-error";
|
|
4
|
+
import { formatPublicErrorMessage, GatewayError } from "../../protocol/gateway-error";
|
|
5
5
|
|
|
6
6
|
// Single console error/access contract. Every domain throws ConsoleDomainError
|
|
7
7
|
// (or the storage-layer BackupError, same code/status/message shape) and
|
|
@@ -24,8 +24,27 @@ export class ConsoleDomainError extends Error {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
interface ShapedDomainError {
|
|
28
|
+
readonly code: string;
|
|
29
|
+
readonly status: number;
|
|
30
|
+
readonly message: string;
|
|
31
|
+
readonly details?: unknown;
|
|
32
|
+
/** Upstream/network origin, preserved so the dashboard can tell a provider
|
|
33
|
+
* rejection from a console-session expiry (see `maybeSignalSessionTransition`). */
|
|
34
|
+
readonly origin?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function asDomainError(error: unknown): ShapedDomainError | undefined {
|
|
28
38
|
if (error instanceof ConsoleDomainError) return error;
|
|
39
|
+
if (error instanceof GatewayError) {
|
|
40
|
+
return {
|
|
41
|
+
code: error.code,
|
|
42
|
+
status: error.status,
|
|
43
|
+
message: error.message,
|
|
44
|
+
details: error.details,
|
|
45
|
+
...(error.origin === "cartethyia" ? {} : { origin: error.origin }),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
29
48
|
if (
|
|
30
49
|
typeof error === "object" &&
|
|
31
50
|
error !== null &&
|
|
@@ -33,8 +52,14 @@ function asDomainError(error: unknown): { code: string; status: number; message:
|
|
|
33
52
|
typeof (error as { status?: unknown }).status === "number" &&
|
|
34
53
|
typeof (error as { message?: unknown }).message === "string"
|
|
35
54
|
) {
|
|
36
|
-
const shaped = error as { code: string; status: number; message: string; details?: unknown };
|
|
37
|
-
return {
|
|
55
|
+
const shaped = error as { code: string; status: number; message: string; details?: unknown; origin?: unknown };
|
|
56
|
+
return {
|
|
57
|
+
code: shaped.code,
|
|
58
|
+
status: shaped.status,
|
|
59
|
+
message: shaped.message,
|
|
60
|
+
details: shaped.details,
|
|
61
|
+
...(typeof shaped.origin === "string" && shaped.origin !== "cartethyia" ? { origin: shaped.origin } : {}),
|
|
62
|
+
};
|
|
38
63
|
}
|
|
39
64
|
return undefined;
|
|
40
65
|
}
|
|
@@ -47,6 +72,7 @@ export function errorResponse(
|
|
|
47
72
|
): {
|
|
48
73
|
error: string;
|
|
49
74
|
code: string;
|
|
75
|
+
origin?: string;
|
|
50
76
|
details?: Record<string, unknown>;
|
|
51
77
|
} {
|
|
52
78
|
const shaped = asDomainError(error);
|
|
@@ -63,16 +89,17 @@ export function errorResponse(
|
|
|
63
89
|
const body: Record<string, unknown> = {
|
|
64
90
|
error: message,
|
|
65
91
|
code: shaped.code,
|
|
92
|
+
...(shaped.origin === undefined ? {} : { origin: shaped.origin }),
|
|
66
93
|
};
|
|
67
94
|
if (includeDetails) body.details = details;
|
|
68
|
-
return body as { error: string; code: string; details?: Record<string, unknown> };
|
|
95
|
+
return body as { error: string; code: string; origin?: string; details?: Record<string, unknown> };
|
|
69
96
|
}
|
|
70
97
|
set.status = 500;
|
|
71
98
|
const body: Record<string, unknown> = {
|
|
72
99
|
error: formatPublicErrorMessage("internal_error", fallbackMessage),
|
|
73
100
|
code: "internal_error",
|
|
74
101
|
};
|
|
75
|
-
return body as { error: string; code: string; details?: Record<string, unknown> };
|
|
102
|
+
return body as { error: string; code: string; origin?: string; details?: Record<string, unknown> };
|
|
76
103
|
}
|
|
77
104
|
|
|
78
105
|
export function requireScope(
|
package/src/main.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import "./boot/elysia-typebox-bridge";
|
|
4
4
|
import { createGatewayApp, createGatewayShell } from "./boot/app";
|
|
5
5
|
import { bootstrap } from "./boot/lifecycle";
|
|
6
|
+
import { flushAllBuffers } from "./boot/flush";
|
|
6
7
|
import type { CartethyiaBoot } from "./boot/lifecycle";
|
|
7
8
|
import {
|
|
8
9
|
resolveDrainToken,
|
|
@@ -90,9 +91,7 @@ const app = boot
|
|
|
90
91
|
// for the queues, minus the stop.
|
|
91
92
|
flushBuffers: {
|
|
92
93
|
flush: async () => {
|
|
93
|
-
await boot.deps
|
|
94
|
-
await boot.deps.usageFlush.flush();
|
|
95
|
-
await boot.deps.healthEventFlush.flush();
|
|
94
|
+
await flushAllBuffers(boot.deps);
|
|
96
95
|
},
|
|
97
96
|
},
|
|
98
97
|
},
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified network egress client.
|
|
3
|
+
*
|
|
4
|
+
* One entry point for every outbound transport the gateway can dial:
|
|
5
|
+
*
|
|
6
|
+
* - `fetch(poolId?, tenantId?)` — validated HTTP fetch (SSRF-guarded), with
|
|
7
|
+
* optional TLS ClientHello profile support (`options.tlsProfile`).
|
|
8
|
+
* - `bidi(poolId?, tenantId?)` — validated bidirectional HTTP/2 stream
|
|
9
|
+
* (Connect-RPC-style request/response over one long-lived stream).
|
|
10
|
+
* - `webSocket(poolId?, tenantId?)` — validated WSS session.
|
|
11
|
+
* - `resolve(hostname, signal, port?)` — one-shot DNS resolution + SSRF
|
|
12
|
+
* validation, pinned so a rebind cannot retarget the socket.
|
|
13
|
+
*
|
|
14
|
+
* All four bind to the same selected network pool (when one is given) or to
|
|
15
|
+
* validated direct egress. Provider adapters call this and never dial a
|
|
16
|
+
* socket themselves.
|
|
17
|
+
*/
|
|
18
|
+
import { ValidatedNetworkBindingFactory } from "./egress/factory";
|
|
19
|
+
import type { ValidatedOutboundHttp2Bidi } from "./bidi";
|
|
20
|
+
import type { ValidatedOutboundWebSocket } from "../providers/provider-contracts";
|
|
21
|
+
import type { ValidatedDestination } from "./ssrf";
|
|
22
|
+
import type { SsrfPolicy } from "../config/network";
|
|
23
|
+
import type { TlsProfile } from "./egress/tls-profile";
|
|
24
|
+
import type { ValidatedFetch } from "./egress/fetch";
|
|
25
|
+
import type { PoolAgentResolver } from "./pool/resolver";
|
|
26
|
+
|
|
27
|
+
export interface NetworkClientOptions {
|
|
28
|
+
/** SSRF policy applied to every dial. Defaults to the empty (block-all-loopback) policy. */
|
|
29
|
+
readonly policy?: SsrfPolicy;
|
|
30
|
+
/** Injected fetch for offline tests/recordings. */
|
|
31
|
+
readonly fetchFn?: typeof fetch;
|
|
32
|
+
/** Optional pool-agent resolver; without it every dial is direct. */
|
|
33
|
+
readonly poolResolver?: PoolAgentResolver;
|
|
34
|
+
/**
|
|
35
|
+
* Default TLS ClientHello profile for direct https dials. `"default"` keeps
|
|
36
|
+
* the runtime's own TLS; a named profile (see `tls-profile.ts`) dials with
|
|
37
|
+
* that profile's OpenSSL options so the wire fingerprint matches the
|
|
38
|
+
* impersonated client.
|
|
39
|
+
*/
|
|
40
|
+
readonly tlsProfile?: TlsProfile;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface NetworkClient {
|
|
44
|
+
/** Validated HTTP fetch bound to a pool slot (or direct when omitted). */
|
|
45
|
+
readonly fetch: (poolId?: string, tenantId?: string) => ValidatedFetch;
|
|
46
|
+
/** Validated bidirectional HTTP/2 stream. */
|
|
47
|
+
readonly bidi: (poolId?: string, tenantId?: string) => ValidatedOutboundHttp2Bidi;
|
|
48
|
+
/** Validated WSS session. */
|
|
49
|
+
readonly webSocket: (poolId?: string, tenantId?: string) => ValidatedOutboundWebSocket;
|
|
50
|
+
/** One-shot DNS resolution + SSRF validation for direct egress targets. */
|
|
51
|
+
readonly resolve: (hostname: string, signal: AbortSignal, port?: number) => Promise<ValidatedDestination>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function createNetworkClient(options: NetworkClientOptions = {}): NetworkClient {
|
|
55
|
+
const factory = new ValidatedNetworkBindingFactory(
|
|
56
|
+
options.policy,
|
|
57
|
+
options.fetchFn,
|
|
58
|
+
options.poolResolver,
|
|
59
|
+
options.tlsProfile,
|
|
60
|
+
);
|
|
61
|
+
return {
|
|
62
|
+
fetch: (poolId, tenantId) => factory.fetch(poolId, tenantId),
|
|
63
|
+
bidi: (poolId, tenantId) => factory.http2Bidi(poolId, tenantId),
|
|
64
|
+
webSocket: (poolId, tenantId) => factory.webSocket(poolId, tenantId),
|
|
65
|
+
resolve: (hostname, signal, port) => factory.resolve(hostname, signal, port),
|
|
66
|
+
};
|
|
67
|
+
}
|