cartethyia 2.5.10 → 2.6.0
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 +8 -0
- package/dist/dashboard/assets/{BannedPage-Cd30Z0b9.js → BannedPage-l6XMnfr9.js} +1 -1
- package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-wf5u-ww6.js → CliToolDetailPage-DwOzAW45.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-DykPvuck.js → CliToolsPage-BT3Up3QA.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-DwvSD3vs.js → CombosPage-6kYIUpzR.js} +1 -1
- package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CIr7KDex.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-82X0DDdH.js → CustomizationPage-C7m-H1Rz.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-CS5mPuVF.js → LoginPage-CbphLvjg.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C0UCUsVJ.js → ModelPicker-DVny4S5_.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-vdja5ZPK.js → ProvidersPage-Dfb2896L.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-BB4CNm4-.js → ProxyPage-BaCIdAUt.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-FadUOl9S.js → SetupPage-D4g6vOGc.js} +1 -1
- package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-Cfy6_Khv.js → SortableList-C93i14BD.js} +1 -1
- package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-CM92fgJf.js → StudioPage-SgQpymjm.js} +1 -1
- package/dist/dashboard/assets/{StudioPage-CM92fgJf.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
- package/dist/dashboard/assets/{UsagePage-BT4MyQeT.js → UsagePage-BFME-eba.js} +2 -2
- package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
- package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
- package/dist/dashboard/assets/{app-B7pVRhJ1.js → app-2KhtHCv9.js} +1 -1
- package/dist/dashboard/assets/{app-B7pVRhJ1.js.gz → app-2KhtHCv9.js.gz} +0 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
- package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
- package/dist/dashboard/assets/{badge-Bvyr7D0u.js → badge-CKsF123d.js} +1 -1
- package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DIvKAZih.js → banner-8j7uChA9.js} +1 -1
- package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
- package/dist/dashboard/assets/{card-D5_gYIS_.js → card-CdsSlWA3.js} +1 -1
- package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
- package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-B6vDBtbm.js → clipboard-button-uL5aYLfW.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-CpjrgdwJ.js → dialog-C07hr5_G.js} +1 -1
- package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BWmt3b5y.js → drawer-BPO1TcKH.js} +1 -1
- package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
- package/dist/dashboard/assets/{entry-C_oakV3z.js → entry-Bz_CtDfC.js} +1 -1
- package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-RRHHWGxh.js → github-badge-DmQreSDd.js} +1 -1
- package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
- package/dist/dashboard/assets/{index-Bhey_rg_.js → index-Bblu6erR.js} +2 -2
- package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
- package/dist/dashboard/assets/{inline-C2_EQ5Uf.js → inline-C9dCYaBZ.js} +1 -1
- package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
- package/dist/dashboard/assets/{input-BMRlQ450.js → input-CjCnXweJ.js} +1 -1
- package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
- package/dist/dashboard/assets/{layout-XR5xP8ew.js → layout-V6y8DAkl.js} +1 -1
- package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Cw07dioc.js → list-row-CM1AaG_y.js} +1 -1
- package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
- package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
- package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
- package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
- package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-DrdA9oSM.js → numbers-BOevJzdz.js} +1 -1
- package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
- package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
- package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
- package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
- package/dist/dashboard/assets/{select-CxVVB__N.js → select-C980c4cz.js} +1 -1
- package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
- package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
- package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
- package/dist/dashboard/assets/{stack-uPOwM9Hs.js → stack-_gfTdLVB.js} +1 -1
- package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
- package/dist/dashboard/assets/{state-D1Yz79iL.js → state-2c5V4zo7.js} +1 -1
- package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
- package/dist/dashboard/assets/{switch-sevsRrRo.js → switch-Dkke7Naf.js} +1 -1
- package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
- package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -51
- package/migrations/README.md +52 -52
- package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
- package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
- package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/package.json +96 -95
- package/scripts/cli/admin.ts +126 -128
- package/scripts/cli/autostart.ts +237 -237
- package/scripts/cli/background.ts +120 -110
- package/scripts/cli/bin/carte.cjs +8 -8
- package/scripts/cli/bin/cartethyia.cjs +167 -167
- package/scripts/cli/console-menu.ts +39 -30
- package/scripts/cli/controller.ts +145 -0
- package/scripts/cli/env.ts +99 -99
- package/scripts/cli/gateway.ts +264 -260
- package/scripts/cli/instance.ts +234 -224
- package/scripts/cli/main.ts +188 -167
- package/scripts/cli/platform.ts +64 -64
- package/scripts/cli/prompt.ts +217 -79
- package/scripts/cli/services.ts +88 -88
- package/scripts/cli/setup.ts +445 -445
- package/scripts/cli/start.ts +85 -85
- package/scripts/cli/startup-signal.ts +23 -23
- package/scripts/cli/telemetry.ts +70 -0
- package/scripts/cli/tray/index.ts +263 -263
- package/scripts/cli/tray/windows.ps1 +107 -107
- package/scripts/cli/tray.ts +29 -4
- package/scripts/cli/update.ts +39 -39
- package/scripts/cli/version.ts +67 -67
- package/scripts/cli/wordmark.ts +58 -58
- package/src/boot/app.ts +16 -1
- package/src/boot/dependencies.ts +128 -25
- package/src/config/resources.ts +137 -0
- package/src/config/runtime.ts +133 -76
- package/src/config/spec.ts +30 -29
- package/src/config/telemetry.ts +0 -25
- package/src/console/api-keys/bucket-spend.ts +14 -17
- package/src/console/api-keys/contracts.ts +410 -410
- package/src/console/api-keys/routes.ts +12 -0
- package/src/console/audit/store.ts +2 -3
- package/src/console/auth/setup-service.ts +8 -1
- package/src/console/backup/routes.ts +7 -0
- package/src/console/backup/service.ts +463 -463
- package/src/console/backup/store.ts +16 -11
- package/src/console/backup/validate.ts +1 -4
- package/src/console/cli-tools/fs-ops.ts +18 -2
- package/src/console/data-management/contracts.ts +1 -8
- package/src/console/data-management/operations.ts +61 -48
- package/src/console/data-management/routes.ts +4 -2
- package/src/console/domain-registration.ts +25 -3
- package/src/console/inferhub/client.ts +139 -0
- package/src/console/inferhub/key-store.ts +129 -0
- package/src/console/inferhub/normalize.ts +347 -0
- package/src/console/inferhub/routes.ts +411 -0
- package/src/console/inferhub/store.ts +109 -0
- package/src/console/inferhub/types.ts +183 -0
- package/src/console/observability/contracts.ts +2 -41
- package/src/console/observability/store.ts +176 -371
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/store.ts +23 -36
- package/src/console/quota/account-quota-shared.ts +52 -52
- package/src/console/quota/account-quota-tenant.ts +16 -24
- package/src/console/quota/refresh.ts +441 -441
- package/src/console/routing/pools/import-parse.ts +206 -206
- package/src/console/routing/pools/operations.ts +543 -543
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/routes.ts +181 -181
- package/src/console/settings/contracts.ts +42 -40
- package/src/console/settings/store.ts +25 -25
- package/src/console/share/share-stats.ts +22 -4
- package/src/console/share/share-usage.ts +30 -4
- package/src/console/shared/cursor.ts +15 -0
- package/src/console/updates/routes.ts +12 -0
- package/src/main.ts +13 -5
- package/src/network/pool/resolver.ts +1104 -1104
- package/src/observability/client-name.ts +22 -0
- package/src/observability/event-log.ts +5 -29
- package/src/observability/event-query.ts +690 -0
- package/src/observability/log-ring.ts +26 -2
- package/src/observability/log-sanitize.ts +118 -0
- package/src/observability/logger.ts +17 -13
- package/src/observability/metrics.ts +35 -0
- package/src/observability/payload-capture.ts +114 -41
- package/src/observability/payload-store.ts +249 -24
- package/src/observability/raw-event-writer.ts +118 -0
- package/src/observability/runtime-metrics.ts +71 -1
- package/src/observability/telemetry-buffer.ts +100 -17
- package/src/observability/telemetry-reconciliation.ts +417 -0
- package/src/persistence/boot.ts +20 -9
- package/src/persistence/db-pg.ts +13 -0
- package/src/persistence/db-sqlite.ts +569 -497
- package/src/persistence/dialect.ts +3 -2
- package/src/persistence/migrate.ts +181 -181
- package/src/persistence/schema.ts +1496 -1406
- package/src/persistence/shared-cache.ts +5 -1
- package/src/persistence/stores/api-key-store.ts +581 -581
- package/src/persistence/stores/telemetry-store.ts +124 -63
- package/src/protocol/reasoning-text.ts +30 -0
- package/src/protocol/response/chat.ts +33 -7
- package/src/protocol/response/codex.ts +14 -2
- package/src/protocol/response/messages.ts +5 -2
- package/src/providers/authentication/oauth-flow-store.ts +3 -10
- package/src/providers/bundled-discovery-providers.ts +122 -122
- package/src/providers/default-registry.ts +10 -0
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +43 -7
- package/src/providers/discovery/probing-service.ts +70 -32
- package/src/providers/integrations/antigravity/antigravity.ts +9 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
- package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
- package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
- package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
- package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
- package/src/providers/integrations/claude/claude-quota.ts +2 -2
- package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
- package/src/providers/integrations/claude/claude.ts +3 -4
- package/src/providers/integrations/cline/cline.ts +94 -8
- package/src/providers/integrations/codex/codex.ts +61 -37
- package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
- package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
- package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
- package/src/providers/integrations/cursor/cursor-request.ts +11 -0
- package/src/providers/integrations/cursor/cursor.ts +29 -17
- package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
- package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
- package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
- package/src/providers/integrations/grok/grok-quota.ts +279 -279
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
- package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
- package/src/providers/integrations/hermes.ts +7 -1
- package/src/providers/integrations/inferhub.ts +201 -73
- package/src/providers/integrations/kilo/kilo.ts +152 -152
- package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
- package/src/providers/integrations/openrouter/provider.ts +129 -129
- package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
- package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
- package/src/providers/integrations/trae/trae-oauth.ts +4 -26
- package/src/providers/integrations/trae/trae-quota.ts +389 -384
- package/src/providers/integrations/trae/trae-realm.ts +15 -2
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/operations/account-health-service.ts +605 -279
- package/src/providers/provider-metadata.ts +12 -1
- package/src/providers/provider-model-definition.ts +373 -373
- package/src/providers/quota/quota-contracts.ts +3 -7
- package/src/providers/search/search-provider.ts +2 -6
- package/src/security/admission/contracts.ts +10 -0
- package/src/security/admission/in-memory-store.ts +89 -14
- package/src/security/admission/lua.ts +191 -182
- package/src/security/admission/redis-store.ts +30 -0
- package/src/security/admission/service.ts +18 -0
- package/src/security/api-key-auth-cache.ts +96 -0
- package/src/security/api-key-auth.ts +11 -100
- package/src/security/client-router-fingerprint.ts +3 -7
- package/src/security/ip-boundary.ts +7 -3
- package/src/shared/coerce.ts +67 -0
- package/src/shared/hash.ts +6 -0
- package/src/shared/time.ts +7 -0
- package/src/transport/dispatch/attempt-finalize.ts +26 -138
- package/src/transport/dispatch/attempt-loop.ts +103 -81
- package/src/transport/dispatch/capture-policy.ts +78 -0
- package/src/transport/dispatch/proxy-request.ts +4 -2
- package/src/transport/dispatch/streaming/encoder.ts +111 -5
- package/src/transport/dispatch/streaming/stream.ts +19 -2
- package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
- package/src/transport/failure-policy.ts +27 -0
- package/src/transport/guards/abuse-guard.ts +2 -2
- package/src/transport/guards/auth-guard.ts +17 -0
- package/src/transport/guards/body-policy.ts +3 -2
- package/src/transport/guards/concurrency-guard.ts +89 -13
- package/src/transport/guards/early-rejection-capture.ts +43 -3
- package/src/transport/guards/error-lifecycle.ts +10 -2
- package/src/transport/guards/guard-errors.ts +23 -0
- package/src/transport/guards/headroom-guard.ts +41 -7
- package/src/transport/guards/pipeline.ts +24 -0
- package/src/transport/guards/request-context.ts +2 -1
- package/src/transport/routing/admission.ts +51 -9
- package/src/transport/routing/bounded-lru.ts +49 -0
- package/src/transport/routing/candidate-index.ts +88 -0
- package/src/transport/routing/reservations.ts +121 -7
- package/src/transport/routing/round-robin.ts +10 -22
- package/src/transport/routing/route-catalog.ts +26 -13
- package/src/transport/routing/route-model.ts +22 -2
- package/src/transport/routing/router.ts +53 -31
- package/src/transport/surface/chat/encode.ts +2 -3
- package/src/transport/surface/chat/parse.ts +2 -1
- package/src/transport/surface/dialects.ts +11 -3
- package/src/transport/surface/messages/encode.ts +2 -1
- package/src/transport/surface/messages/parse.ts +3 -1
- package/src/transport/surface/messages/stream.ts +2 -6
- package/src/workers/maintenance-lease.ts +200 -0
- package/src/workers/scheduler.ts +101 -1
- package/dist/dashboard/assets/BannedPage-Cd30Z0b9.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-wf5u-ww6.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DykPvuck.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DwvSD3vs.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CIr7KDex.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-82X0DDdH.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-CS5mPuVF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C0UCUsVJ.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-vdja5ZPK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BB4CNm4-.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js +0 -1
- package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js +0 -1
- package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-FadUOl9S.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-Cfy6_Khv.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BT4MyQeT.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/badge-Bvyr7D0u.js.gz +0 -0
- package/dist/dashboard/assets/banner-DIvKAZih.js.gz +0 -0
- package/dist/dashboard/assets/card-D5_gYIS_.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-B6vDBtbm.js.gz +0 -0
- package/dist/dashboard/assets/dialog-CpjrgdwJ.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BWmt3b5y.js.gz +0 -0
- package/dist/dashboard/assets/entry-B-gvimrg.js +0 -1
- package/dist/dashboard/assets/entry-B-gvimrg.js.gz +0 -0
- package/dist/dashboard/assets/entry-C_oakV3z.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dj0cHfEc.js +0 -4
- package/dist/dashboard/assets/entry-Dj0cHfEc.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-RRHHWGxh.js.gz +0 -0
- package/dist/dashboard/assets/index-Bhey_rg_.js.gz +0 -0
- package/dist/dashboard/assets/inline-C2_EQ5Uf.js.gz +0 -0
- package/dist/dashboard/assets/input-BMRlQ450.js.gz +0 -0
- package/dist/dashboard/assets/layout-XR5xP8ew.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Cw07dioc.js.gz +0 -0
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DrdA9oSM.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/select-CxVVB__N.js.gz +0 -0
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/stack-uPOwM9Hs.js.gz +0 -0
- package/dist/dashboard/assets/state-D1Yz79iL.js.gz +0 -0
- package/dist/dashboard/assets/switch-sevsRrRo.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/src/observability/event-compaction.ts +0 -317
- package/src/observability/event-day-index.ts +0 -439
|
@@ -1,192 +1,193 @@
|
|
|
1
|
-
// Qoder quota.
|
|
2
|
-
//
|
|
3
|
-
// `GET /api/v2/quota/usage` on the **openapi** host, with a plain bearer token
|
|
4
|
-
// — no COSY signature on this surface, which is the one place the business API
|
|
5
|
-
// differs from inference.
|
|
6
|
-
//
|
|
7
|
-
// The response carries two quota objects and they are not interchangeable:
|
|
8
|
-
//
|
|
9
|
-
// - `userQuota` is the plan allowance — what the account bought.
|
|
10
|
-
// - `addOnQuota` is granted credits (daily check-in, promotions) — what it was
|
|
11
|
-
// given on top.
|
|
12
|
-
//
|
|
13
|
-
// The donor sums them into one balance, and that is right for a credit balance:
|
|
14
|
-
// both are credits in the same unit and either can be spent. It would be wrong
|
|
15
|
-
// for two *rate limits*, which is why these windows are marked `pooled` here
|
|
16
|
-
// and the Trae request buckets are not.
|
|
17
|
-
//
|
|
18
|
-
// Teams add a third object (`orgResourcePackage`), a shared pool that sits
|
|
19
|
-
// outside the account's own allowance. It is only real when it is available,
|
|
20
|
-
// and an account whose plan is exhausted but whose shared pool still has
|
|
21
|
-
// credit is *not* exhausted.
|
|
22
|
-
|
|
23
|
-
import type {
|
|
24
|
-
FetchLike,
|
|
25
|
-
ProviderQuotaResult,
|
|
26
|
-
ProviderQuotaWindow,
|
|
27
|
-
} from "../../quota/quota-contracts";
|
|
28
|
-
import {
|
|
29
|
-
cleanError,
|
|
30
|
-
isoDate,
|
|
31
|
-
number,
|
|
32
|
-
percentWindow,
|
|
33
|
-
quotaRecord,
|
|
34
|
-
record,
|
|
35
|
-
} from "../../quota/quota-contracts";
|
|
36
|
-
import type { QuotaCollectionContext } from "../../quota/quota-support";
|
|
37
|
-
import {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
if (typeof value === "
|
|
64
|
-
if (typeof value === "
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* zero
|
|
86
|
-
*
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
// `
|
|
132
|
-
// "
|
|
133
|
-
//
|
|
134
|
-
// unreadable
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (!
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
"
|
|
164
|
-
|
|
165
|
-
number(shared["
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
1
|
+
// Qoder quota.
|
|
2
|
+
//
|
|
3
|
+
// `GET /api/v2/quota/usage` on the **openapi** host, with a plain bearer token
|
|
4
|
+
// — no COSY signature on this surface, which is the one place the business API
|
|
5
|
+
// differs from inference.
|
|
6
|
+
//
|
|
7
|
+
// The response carries two quota objects and they are not interchangeable:
|
|
8
|
+
//
|
|
9
|
+
// - `userQuota` is the plan allowance — what the account bought.
|
|
10
|
+
// - `addOnQuota` is granted credits (daily check-in, promotions) — what it was
|
|
11
|
+
// given on top.
|
|
12
|
+
//
|
|
13
|
+
// The donor sums them into one balance, and that is right for a credit balance:
|
|
14
|
+
// both are credits in the same unit and either can be spent. It would be wrong
|
|
15
|
+
// for two *rate limits*, which is why these windows are marked `pooled` here
|
|
16
|
+
// and the Trae request buckets are not.
|
|
17
|
+
//
|
|
18
|
+
// Teams add a third object (`orgResourcePackage`), a shared pool that sits
|
|
19
|
+
// outside the account's own allowance. It is only real when it is available,
|
|
20
|
+
// and an account whose plan is exhausted but whose shared pool still has
|
|
21
|
+
// credit is *not* exhausted.
|
|
22
|
+
|
|
23
|
+
import type {
|
|
24
|
+
FetchLike,
|
|
25
|
+
ProviderQuotaResult,
|
|
26
|
+
ProviderQuotaWindow,
|
|
27
|
+
} from "../../quota/quota-contracts";
|
|
28
|
+
import {
|
|
29
|
+
cleanError,
|
|
30
|
+
isoDate,
|
|
31
|
+
number,
|
|
32
|
+
percentWindow,
|
|
33
|
+
quotaRecord,
|
|
34
|
+
record,
|
|
35
|
+
} from "../../quota/quota-contracts";
|
|
36
|
+
import type { QuotaCollectionContext } from "../../quota/quota-support";
|
|
37
|
+
import { log } from "../../../observability/logger";
|
|
38
|
+
import { QODER_REALMS, type QoderRealm } from "./qoder-realm";
|
|
39
|
+
|
|
40
|
+
/** Paths on the realm's openapi host. */
|
|
41
|
+
const QUOTA_PATH = "/api/v2/quota/usage";
|
|
42
|
+
|
|
43
|
+
/** Shared-pool keys, in the order the donor tries them. */
|
|
44
|
+
const SHARED_KEYS: readonly string[] = [
|
|
45
|
+
"orgResourcePackage",
|
|
46
|
+
"sharedQuota",
|
|
47
|
+
"teamQuota",
|
|
48
|
+
"poolQuota",
|
|
49
|
+
"sharedCredits",
|
|
50
|
+
"sharedCredit",
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether a shared pool is usable.
|
|
55
|
+
*
|
|
56
|
+
* Absent means "not declared unavailable", which is usable. Only an explicit
|
|
57
|
+
* false/null/0 rules it out — reading absence as unavailable is what made a
|
|
58
|
+
* team account with a live pool look exhausted.
|
|
59
|
+
*/
|
|
60
|
+
function sharedAvailable(source: Record<string, unknown>): boolean {
|
|
61
|
+
const value = source["available"];
|
|
62
|
+
if (value === undefined || value === null) return true;
|
|
63
|
+
if (typeof value === "boolean") return value;
|
|
64
|
+
if (typeof value === "number") return value !== 0;
|
|
65
|
+
if (typeof value === "string") return value.trim().toLowerCase() !== "false";
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function creditWindow(
|
|
70
|
+
kind: string,
|
|
71
|
+
label: string,
|
|
72
|
+
remaining: number | null,
|
|
73
|
+
limit: number | null,
|
|
74
|
+
resetsAt: string | null,
|
|
75
|
+
): ProviderQuotaWindow | null {
|
|
76
|
+
if (remaining === null && limit === null) return null;
|
|
77
|
+
const used = remaining !== null && limit !== null ? limit - remaining : null;
|
|
78
|
+
return percentWindow(kind, label, null, resetsAt, used, limit, "pooled");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Reads one Qoder account's quota.
|
|
83
|
+
*
|
|
84
|
+
* A degenerate body — both quota objects missing, or `isQuotaExceeded` with a
|
|
85
|
+
* zero remaining *and* a zero total — is reported as an error rather than as a
|
|
86
|
+
* zero balance. "No quota" parks the account; "could not read the quota" does
|
|
87
|
+
* not, and those two are not the same claim.
|
|
88
|
+
*/
|
|
89
|
+
/** Records an unreadable body where an operator can act on it, not as an error. */
|
|
90
|
+
function logQoderQuotaMiss(detail: string): void {
|
|
91
|
+
log.warn(`[quota] qoder quota unreadable: ${detail}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function fetchQoderQuota(
|
|
95
|
+
credential: string,
|
|
96
|
+
fetcher: FetchLike,
|
|
97
|
+
context?: QuotaCollectionContext,
|
|
98
|
+
): Promise<ProviderQuotaResult> {
|
|
99
|
+
const raw = context?.auth_state ?? {};
|
|
100
|
+
const realm: QoderRealm = raw["realm"] === "cn" || raw["region"] === "CN" ? "cn" : "intl";
|
|
101
|
+
const config = QODER_REALMS[realm];
|
|
102
|
+
const url = `${config.openapi}${QUOTA_PATH}`;
|
|
103
|
+
|
|
104
|
+
let payload: unknown;
|
|
105
|
+
try {
|
|
106
|
+
const response = await fetcher(url, {
|
|
107
|
+
method: "GET",
|
|
108
|
+
headers: {
|
|
109
|
+
accept: "application/json",
|
|
110
|
+
authorization: `Bearer ${credential}`,
|
|
111
|
+
"user-agent": config.userAgent,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
115
|
+
payload = await response.json();
|
|
116
|
+
} catch (error) {
|
|
117
|
+
// Unreachable is not a balance. See the note on the degenerate branch.
|
|
118
|
+
logQoderQuotaMiss(cleanError(error));
|
|
119
|
+
return { source: "qoder", plan: null, windows: [], error: null };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const root = quotaRecord(payload);
|
|
123
|
+
const userQuota = record(root["userQuota"]);
|
|
124
|
+
const addOnQuota = record(root["addOnQuota"]);
|
|
125
|
+
const owned = [userQuota, addOnQuota].filter(
|
|
126
|
+
(value): value is Record<string, unknown> => value !== null,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const bothMissing = owned.length === 0;
|
|
130
|
+
// A live account on the free tier really does answer `userQuota` all zeroes
|
|
131
|
+
// with `isQuotaExceeded: true` — verified against a real credential:
|
|
132
|
+
// `{"isQuotaExceeded":true,"userQuota":{"total":0,"used":0,"remaining":0},
|
|
133
|
+
// "totalUsagePercentage":0.0,...}`. That is a balance of zero, not an
|
|
134
|
+
// unreadable body. Only a response with *both* quota objects absent is
|
|
135
|
+
// unreadable, and it is logged rather than returned — never as an error,
|
|
136
|
+
// which would cool the account.
|
|
137
|
+
const degenerate = bothMissing;
|
|
138
|
+
if (degenerate) {
|
|
139
|
+
// A body we cannot trust is not a zero balance, and it is not a failed
|
|
140
|
+
// read either: `error` is what flags the account `quota_fetch_failed` and
|
|
141
|
+
// cools it, so a shape we simply do not recognise would park a working
|
|
142
|
+
// account. Logged instead of returned.
|
|
143
|
+
logQoderQuotaMiss(
|
|
144
|
+
bothMissing
|
|
145
|
+
? "Qoder quota response carried no quota objects."
|
|
146
|
+
: "Qoder reported quota exceeded with a zero total; not read as a balance.",
|
|
147
|
+
);
|
|
148
|
+
return { source: "qoder", plan: null, windows: [], error: null };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const resetsAt = isoDate(root["expiresAt"] ?? root["expireAt"] ?? root["expires_at"]);
|
|
152
|
+
const windows: ProviderQuotaWindow[] = [];
|
|
153
|
+
const plan = creditWindow("qoder:plan", "Plan credits", number(userQuota?.["remaining"]), number(userQuota?.["total"]), resetsAt);
|
|
154
|
+
if (plan) windows.push(plan);
|
|
155
|
+
const addOn = creditWindow("qoder:addon", "Granted credits", number(addOnQuota?.["remaining"]), number(addOnQuota?.["total"]), resetsAt);
|
|
156
|
+
if (addOn) windows.push(addOn);
|
|
157
|
+
|
|
158
|
+
for (const key of SHARED_KEYS) {
|
|
159
|
+
const shared = record(root[key]);
|
|
160
|
+
if (!shared) continue;
|
|
161
|
+
if (!sharedAvailable(shared)) continue;
|
|
162
|
+
const window = creditWindow(
|
|
163
|
+
"qoder:shared",
|
|
164
|
+
"Shared pool",
|
|
165
|
+
number(shared["remaining"]),
|
|
166
|
+
number(shared["cap"] ?? shared["total"]),
|
|
167
|
+
null,
|
|
168
|
+
);
|
|
169
|
+
if (window) windows.push(window);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (windows.length === 0) {
|
|
174
|
+
logQoderQuotaMiss("Qoder quota response carried no readable balance.");
|
|
175
|
+
return { source: "qoder", plan: null, windows: [], error: null };
|
|
176
|
+
}
|
|
177
|
+
return { source: "qoder", plan: null, windows, error: null };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The `qodercn` provider's quota.
|
|
182
|
+
*
|
|
183
|
+
* Same collector, realm forced to `cn`: a CN account is refused by
|
|
184
|
+
* `openapi.qoder.sh`, and the collector cannot see the provider id — the
|
|
185
|
+
* context carries auth state only.
|
|
186
|
+
*/
|
|
187
|
+
export function fetchQoderCnQuota(
|
|
188
|
+
credential: string,
|
|
189
|
+
fetcher: FetchLike,
|
|
190
|
+
context?: QuotaCollectionContext,
|
|
191
|
+
): Promise<ProviderQuotaResult> {
|
|
192
|
+
return fetchQoderQuota(credential, fetcher, { ...context, auth_state: { ...(context?.auth_state ?? {}), realm: "cn" } });
|
|
193
|
+
}
|
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
// Qoder realms: one vendor, two unrelated deployments.
|
|
2
|
-
//
|
|
3
|
-
// `qoder` (global / intl) and `qodercn` (China) share the wire protocol — COSY
|
|
4
|
-
// signing, the scrambled body, the enveloped SSE stream — but nothing else
|
|
5
|
-
// about the address: different gateway host, different openapi host, different
|
|
6
|
-
// OAuth client id, different website, different UA, and a *different model
|
|
7
|
-
// roster*. Treating them as one provider with a region flag is what made a CN
|
|
8
|
-
// account dispatch to `*.qoder.sh` and be refused.
|
|
9
|
-
//
|
|
10
|
-
// Everything per-realm lives here so the adapter, the OAuth client, and the
|
|
11
|
-
// discovery fetcher cannot drift apart on host, client id, or roster.
|
|
12
|
-
//
|
|
13
|
-
// Sourced from the donor hub `Public/qoder2api-hub` (`REALM_CONFIGS` in
|
|
14
|
-
// `qoder_accounts.py`), which builds every upstream URL from this table.
|
|
15
|
-
|
|
16
|
-
/** One Qoder deployment. */
|
|
17
|
-
export type QoderRealm = "intl" | "cn";
|
|
18
|
-
|
|
19
|
-
/** Per-realm upstream addresses and client identity. */
|
|
20
|
-
export interface QoderRealmConfig {
|
|
21
|
-
/** Inference + model-list gateway host. */
|
|
22
|
-
readonly gateway: string;
|
|
23
|
-
/** Fallback gateway hosts, tried in order after `gateway`. */
|
|
24
|
-
readonly gatewayFallbacks: readonly string[];
|
|
25
|
-
/** Business API host (device token, userinfo, quota). */
|
|
26
|
-
readonly openapi: string;
|
|
27
|
-
/** Where the operator signs in. */
|
|
28
|
-
readonly website: string;
|
|
29
|
-
/** OAuth client id sent on the device-token exchange. */
|
|
30
|
-
readonly clientId: string;
|
|
31
|
-
/** Native redirect the desktop client registers. */
|
|
32
|
-
readonly redirectUri: string;
|
|
33
|
-
/** Upstream-facing User-Agent. */
|
|
34
|
-
readonly userAgent: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Chat + model-list paths, identical across realms. */
|
|
38
|
-
export const QODER_CHAT_PATH =
|
|
39
|
-
"/algo/api/v2/service/pro/sse/agent_chat_generation?FetchKeys=llm_model_result&AgentId=agent_common&Encode=1";
|
|
40
|
-
export const QODER_MODELS_PATH = "/algo/api/v2/model/list?Encode=1";
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Realm → deployment.
|
|
44
|
-
*
|
|
45
|
-
* `intl` carries two gateway fallbacks (`api2`, `api3`); `cn` has exactly one
|
|
46
|
-
* host and no resilience, which is upstream fact, not an oversight — the donor
|
|
47
|
-
* loops `gateway + gatewayFallbacks` and gets one candidate for CN.
|
|
48
|
-
*/
|
|
49
|
-
export const QODER_REALMS: Readonly<Record<QoderRealm, QoderRealmConfig>> = {
|
|
50
|
-
intl: {
|
|
51
|
-
gateway: "https://api1.qoder.sh",
|
|
52
|
-
gatewayFallbacks: ["https://api2.qoder.sh", "https://api3.qoder.sh"],
|
|
53
|
-
openapi: "https://openapi.qoder.sh",
|
|
54
|
-
website: "https://qoder.com",
|
|
55
|
-
clientId: "e883ade2-e6e3-4d6d-adf7-f92ceff5fdcb",
|
|
56
|
-
redirectUri: "qoder://aicoding.aicoding-agent/login-success",
|
|
57
|
-
userAgent: "Qoder/1.1.64",
|
|
58
|
-
},
|
|
59
|
-
cn: {
|
|
60
|
-
gateway: "https://gateway.qoder.com.cn",
|
|
61
|
-
gatewayFallbacks: [],
|
|
62
|
-
openapi: "https://openapi.qoder.com.cn",
|
|
63
|
-
website: "https://qoder.com.cn",
|
|
64
|
-
clientId: "1c5e33e1-364d-4ce6-b02c-acaa81274a5c",
|
|
65
|
-
redirectUri: "qoder-work-cn://",
|
|
66
|
-
userAgent: "QoderWork/1.1.64",
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/** The realm one bundled provider id belongs to. */
|
|
71
|
-
export function qoderRealmForProvider(providerId: string): QoderRealm {
|
|
72
|
-
return providerId === "qodercn" ? "cn" : "intl";
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Gateway hosts for one realm, primary first. */
|
|
76
|
-
export function qoderGatewayCandidates(realm: QoderRealm): readonly string[] {
|
|
77
|
-
const config = QODER_REALMS[realm];
|
|
78
|
-
return [config.gateway, ...config.gatewayFallbacks];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Model keys that exist in exactly one realm.
|
|
83
|
-
*
|
|
84
|
-
* From the donor's `INTL_EXCLUSIVE` / `CN_EXCLUSIVE` tables. A request for one
|
|
85
|
-
* of these on the wrong realm does not degrade — it is refused — so the roster
|
|
86
|
-
* each realm publishes is the authority, not a union.
|
|
87
|
-
*/
|
|
88
|
-
export const QODER_INTL_EXCLUSIVE: ReadonlySet<string> = new Set([
|
|
89
|
-
"fast",
|
|
90
|
-
"efficient",
|
|
91
|
-
"cmodel",
|
|
92
|
-
"smodel",
|
|
93
|
-
"ultimate",
|
|
94
|
-
"performance",
|
|
95
|
-
]);
|
|
96
|
-
|
|
97
|
-
export const QODER_CN_EXCLUSIVE: ReadonlySet<string> = new Set([
|
|
98
|
-
"q37fmodel",
|
|
99
|
-
"gm51model",
|
|
100
|
-
]);
|
|
101
|
-
|
|
102
|
-
/** True when this model key is served only by the other realm. */
|
|
103
|
-
export function isForeignQoderModel(realm: QoderRealm, modelId: string): boolean {
|
|
104
|
-
const foreign = realm === "cn" ? QODER_INTL_EXCLUSIVE : QODER_CN_EXCLUSIVE;
|
|
105
|
-
return foreign.has(modelId.trim().toLowerCase());
|
|
106
|
-
}
|
|
1
|
+
// Qoder realms: one vendor, two unrelated deployments.
|
|
2
|
+
//
|
|
3
|
+
// `qoder` (global / intl) and `qodercn` (China) share the wire protocol — COSY
|
|
4
|
+
// signing, the scrambled body, the enveloped SSE stream — but nothing else
|
|
5
|
+
// about the address: different gateway host, different openapi host, different
|
|
6
|
+
// OAuth client id, different website, different UA, and a *different model
|
|
7
|
+
// roster*. Treating them as one provider with a region flag is what made a CN
|
|
8
|
+
// account dispatch to `*.qoder.sh` and be refused.
|
|
9
|
+
//
|
|
10
|
+
// Everything per-realm lives here so the adapter, the OAuth client, and the
|
|
11
|
+
// discovery fetcher cannot drift apart on host, client id, or roster.
|
|
12
|
+
//
|
|
13
|
+
// Sourced from the donor hub `Public/qoder2api-hub` (`REALM_CONFIGS` in
|
|
14
|
+
// `qoder_accounts.py`), which builds every upstream URL from this table.
|
|
15
|
+
|
|
16
|
+
/** One Qoder deployment. */
|
|
17
|
+
export type QoderRealm = "intl" | "cn";
|
|
18
|
+
|
|
19
|
+
/** Per-realm upstream addresses and client identity. */
|
|
20
|
+
export interface QoderRealmConfig {
|
|
21
|
+
/** Inference + model-list gateway host. */
|
|
22
|
+
readonly gateway: string;
|
|
23
|
+
/** Fallback gateway hosts, tried in order after `gateway`. */
|
|
24
|
+
readonly gatewayFallbacks: readonly string[];
|
|
25
|
+
/** Business API host (device token, userinfo, quota). */
|
|
26
|
+
readonly openapi: string;
|
|
27
|
+
/** Where the operator signs in. */
|
|
28
|
+
readonly website: string;
|
|
29
|
+
/** OAuth client id sent on the device-token exchange. */
|
|
30
|
+
readonly clientId: string;
|
|
31
|
+
/** Native redirect the desktop client registers. */
|
|
32
|
+
readonly redirectUri: string;
|
|
33
|
+
/** Upstream-facing User-Agent. */
|
|
34
|
+
readonly userAgent: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Chat + model-list paths, identical across realms. */
|
|
38
|
+
export const QODER_CHAT_PATH =
|
|
39
|
+
"/algo/api/v2/service/pro/sse/agent_chat_generation?FetchKeys=llm_model_result&AgentId=agent_common&Encode=1";
|
|
40
|
+
export const QODER_MODELS_PATH = "/algo/api/v2/model/list?Encode=1";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Realm → deployment.
|
|
44
|
+
*
|
|
45
|
+
* `intl` carries two gateway fallbacks (`api2`, `api3`); `cn` has exactly one
|
|
46
|
+
* host and no resilience, which is upstream fact, not an oversight — the donor
|
|
47
|
+
* loops `gateway + gatewayFallbacks` and gets one candidate for CN.
|
|
48
|
+
*/
|
|
49
|
+
export const QODER_REALMS: Readonly<Record<QoderRealm, QoderRealmConfig>> = {
|
|
50
|
+
intl: {
|
|
51
|
+
gateway: "https://api1.qoder.sh",
|
|
52
|
+
gatewayFallbacks: ["https://api2.qoder.sh", "https://api3.qoder.sh"],
|
|
53
|
+
openapi: "https://openapi.qoder.sh",
|
|
54
|
+
website: "https://qoder.com",
|
|
55
|
+
clientId: "e883ade2-e6e3-4d6d-adf7-f92ceff5fdcb",
|
|
56
|
+
redirectUri: "qoder://aicoding.aicoding-agent/login-success",
|
|
57
|
+
userAgent: "Qoder/1.1.64",
|
|
58
|
+
},
|
|
59
|
+
cn: {
|
|
60
|
+
gateway: "https://gateway.qoder.com.cn",
|
|
61
|
+
gatewayFallbacks: [],
|
|
62
|
+
openapi: "https://openapi.qoder.com.cn",
|
|
63
|
+
website: "https://qoder.com.cn",
|
|
64
|
+
clientId: "1c5e33e1-364d-4ce6-b02c-acaa81274a5c",
|
|
65
|
+
redirectUri: "qoder-work-cn://",
|
|
66
|
+
userAgent: "QoderWork/1.1.64",
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** The realm one bundled provider id belongs to. */
|
|
71
|
+
export function qoderRealmForProvider(providerId: string): QoderRealm {
|
|
72
|
+
return providerId === "qodercn" ? "cn" : "intl";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Gateway hosts for one realm, primary first. */
|
|
76
|
+
export function qoderGatewayCandidates(realm: QoderRealm): readonly string[] {
|
|
77
|
+
const config = QODER_REALMS[realm];
|
|
78
|
+
return [config.gateway, ...config.gatewayFallbacks];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Model keys that exist in exactly one realm.
|
|
83
|
+
*
|
|
84
|
+
* From the donor's `INTL_EXCLUSIVE` / `CN_EXCLUSIVE` tables. A request for one
|
|
85
|
+
* of these on the wrong realm does not degrade — it is refused — so the roster
|
|
86
|
+
* each realm publishes is the authority, not a union.
|
|
87
|
+
*/
|
|
88
|
+
export const QODER_INTL_EXCLUSIVE: ReadonlySet<string> = new Set([
|
|
89
|
+
"fast",
|
|
90
|
+
"efficient",
|
|
91
|
+
"cmodel",
|
|
92
|
+
"smodel",
|
|
93
|
+
"ultimate",
|
|
94
|
+
"performance",
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
export const QODER_CN_EXCLUSIVE: ReadonlySet<string> = new Set([
|
|
98
|
+
"q37fmodel",
|
|
99
|
+
"gm51model",
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
/** True when this model key is served only by the other realm. */
|
|
103
|
+
export function isForeignQoderModel(realm: QoderRealm, modelId: string): boolean {
|
|
104
|
+
const foreign = realm === "cn" ? QODER_INTL_EXCLUSIVE : QODER_CN_EXCLUSIVE;
|
|
105
|
+
return foreign.has(modelId.trim().toLowerCase());
|
|
106
|
+
}
|