cartethyia 2.7.4 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-ChG5svFb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BIcWQun7.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Bf2VelDe.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-ggeMSjte.js → CustomizationPage-BKj_r0rp.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-U6AKaJcF.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-5SnUSy3j.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-f0DE1h0-.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js +1 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js +1 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-8fiE6tec.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BSAsYsmV.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-CPK47xQx.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js +1 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BLULNqTp.js} +1 -1
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +56 -55
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +48 -12
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +37 -31
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +1 -1
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +1 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +20 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +259 -66
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +4 -4
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +4 -4
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +252 -97
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +59 -291
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +113 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +10 -1
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +6 -10
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +6 -6
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/providers/adapters/compatible-adapter.ts +42 -3
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +23 -35
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +2 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +38 -5
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +9 -9
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +41 -14
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +81 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +85 -8
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +354 -62
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +53 -10
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +16 -4
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +1 -1
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +6 -0
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +1 -13
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +152 -37
- package/src/transport/shutdown-notice.ts +2 -2
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +376 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/{app-BayTTy01.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-BayTTy01.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -86,6 +86,14 @@ interface QoderModelConfig {
|
|
|
86
86
|
* `false` marks the model as billed.
|
|
87
87
|
*/
|
|
88
88
|
readonly is_free?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* The reasoning effort to send when the caller did not ask for one. The IDE
|
|
91
|
+
* surface follows the `reasoningEffort` field: a model marked `is_reasoning`
|
|
92
|
+
* may still stream no reasoning unless an effort is present (verified live:
|
|
93
|
+
* Sonus streams thinking only when effort is sent; DeepSeek thinks by
|
|
94
|
+
* default). Set only for models that need the push.
|
|
95
|
+
*/
|
|
96
|
+
readonly default_reasoning_effort?: string;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
/**
|
|
@@ -112,7 +120,7 @@ export const QODER_CN_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>>
|
|
|
112
120
|
};
|
|
113
121
|
|
|
114
122
|
/**
|
|
115
|
-
* Global/intl roster (
|
|
123
|
+
* Global/intl roster (18 keys), from the donor's `qoder_catalog_intl.json`.
|
|
116
124
|
*
|
|
117
125
|
* Note how the shared keys differ from CN — `auto` is non-reasoning here and
|
|
118
126
|
* `qmodel` carries a 1M window. That is why the rosters are not merged.
|
|
@@ -127,8 +135,11 @@ export const QODER_INTL_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>
|
|
|
127
135
|
fast: { id: "fast", display_name: "Fast", max_input_tokens: 1000000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
|
|
128
136
|
performance: { id: "performance", display_name: "Performance", max_input_tokens: 1000000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
|
|
129
137
|
efficient: { id: "efficient", display_name: "Efficient", max_input_tokens: 200000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
// Sonus/Cantus need an explicit effort on the IDE surface; without it the
|
|
139
|
+
// upstream streams text only, no reasoning (verified live). DeepSeek-family
|
|
140
|
+
// models think by default and are left untouched.
|
|
141
|
+
smodel: { id: "smodel", display_name: "Sonus", max_input_tokens: 1000000, max_output_tokens: 128_000, is_vl: true, is_free: false, is_reasoning: true, default_reasoning_effort: "high" },
|
|
142
|
+
cmodel: { id: "cmodel", display_name: "Cantus", max_input_tokens: 1000000, max_output_tokens: 128_000, is_vl: true, is_free: false, is_reasoning: true, default_reasoning_effort: "high" },
|
|
132
143
|
qmodel_38max: { id: "qmodel_38max", display_name: "Qwen3.8-Max", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_reasoning: true },
|
|
133
144
|
qfmodel: { id: "qfmodel", display_name: "Qwen3.8-Flash", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_free: true, is_reasoning: true },
|
|
134
145
|
qmodel_latest: { id: "qmodel_latest", display_name: "Qwen3.7-Max", max_input_tokens: 1000000, max_output_tokens: 131_072, is_vl: true, is_reasoning: false },
|
|
@@ -142,6 +153,9 @@ export const QODER_INTL_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>
|
|
|
142
153
|
// previously marked free. Display names are corrected from the same capture.
|
|
143
154
|
dfmodel: { id: "dfmodel", display_name: "DeepSeek-Flash", max_input_tokens: 1000000, max_output_tokens: 384_000, is_vl: true, is_free: false, is_reasoning: true },
|
|
144
155
|
mmodel: { id: "mmodel", display_name: "MiniMax-M3.1-Flash", max_input_tokens: 1000000, max_output_tokens: 512_000, is_vl: true, is_free: false, is_reasoning: false },
|
|
156
|
+
// `lite` is the free-tier plan package: same 1M window and multimodal input
|
|
157
|
+
// as `qmodel` (Qwen3.7-Plus), capped at 64k output, free tier.
|
|
158
|
+
lite: { id: "lite", display_name: "Lite-Qwen-3.7-Plus", max_input_tokens: 1000000, max_output_tokens: 65_536, is_vl: true, is_free: true, is_reasoning: false },
|
|
145
159
|
};
|
|
146
160
|
|
|
147
161
|
/**
|
|
@@ -213,13 +227,24 @@ export function qoderVirtualModels(
|
|
|
213
227
|
// substitutes the shared unfiled fallback for null — the same marker a
|
|
214
228
|
// hand-added row gets.
|
|
215
229
|
ctx: target.contextLimit ?? null,
|
|
216
|
-
|
|
217
|
-
|
|
230
|
+
// A virtual override wins; otherwise inherit the target's ceiling.
|
|
231
|
+
...(virtual.outputLimit !== undefined
|
|
232
|
+
? { out: virtual.outputLimit }
|
|
233
|
+
: target.outputLimit === null
|
|
234
|
+
? {}
|
|
235
|
+
: { out: target.outputLimit }),
|
|
236
|
+
vision: virtual.vision ?? target.modalities.input.includes("image"),
|
|
218
237
|
reasoning: target.reasoning ?? false,
|
|
219
238
|
toolCall: target.toolCall ?? false,
|
|
220
239
|
// The same model under its caller-facing name belongs to the same tier,
|
|
221
|
-
// so the pill follows the roster row rather than being restated
|
|
222
|
-
|
|
240
|
+
// so the pill follows the roster row rather than being restated —
|
|
241
|
+
// unless the virtual card explicitly overrides it (`lite` is a free
|
|
242
|
+
// plan package over a billed slot).
|
|
243
|
+
...(virtual.free !== undefined
|
|
244
|
+
? { free: virtual.free, freeTier: virtual.free }
|
|
245
|
+
: target.freeTier === undefined
|
|
246
|
+
? {}
|
|
247
|
+
: { free: target.freeTier, freeTier: target.freeTier }),
|
|
223
248
|
}),
|
|
224
249
|
);
|
|
225
250
|
}
|
|
@@ -274,11 +299,27 @@ export function mapQoderCatalogResponse(raw: unknown): QoderCatalogEntry[] {
|
|
|
274
299
|
return entries;
|
|
275
300
|
}
|
|
276
301
|
|
|
302
|
+
/**
|
|
303
|
+
* The static roster's stated output ceiling for one slot, when the roster
|
|
304
|
+
* states one.
|
|
305
|
+
*
|
|
306
|
+
* Upstream's live `/model/list` rows report no output limit, so a discovered
|
|
307
|
+
* row cannot read one from the wire. Inheriting the roster's number keeps the
|
|
308
|
+
* discovered row's `outputLimit` honest — without it `defineModel` falls back
|
|
309
|
+
* to a base-catalog/majority vote that can sit below the roster's ceiling
|
|
310
|
+
* (e.g. `qwen3.7-plus` votes 64k while the roster says 131k).
|
|
311
|
+
*/
|
|
312
|
+
function qoderRosterMaxOutputFor(providerId: string, key: string): number | undefined {
|
|
313
|
+
const configs = providerId === "qodercn" ? QODER_CN_MODEL_CONFIGS : QODER_INTL_MODEL_CONFIGS;
|
|
314
|
+
return configs[key]?.max_output_tokens;
|
|
315
|
+
}
|
|
316
|
+
|
|
277
317
|
/** Projects one live catalog row onto a Cartethyia catalog row. */
|
|
278
318
|
export function qoderCatalogModel(
|
|
279
319
|
entry: QoderCatalogEntry,
|
|
280
320
|
providerId: string,
|
|
281
321
|
): ModelDefinition {
|
|
322
|
+
const rosterOutput = qoderRosterMaxOutputFor(providerId, entry.key);
|
|
282
323
|
return defineModel({
|
|
283
324
|
id: entry.key,
|
|
284
325
|
providerId,
|
|
@@ -295,6 +336,9 @@ export function qoderCatalogModel(
|
|
|
295
336
|
// a free row bills zero per token and belongs to the provider's free tier.
|
|
296
337
|
free: entry.isFree,
|
|
297
338
|
freeTier: entry.isFree,
|
|
339
|
+
// The live row carries no output ceiling, so inherit the static roster's
|
|
340
|
+
// stated one when it exists (see `qoderRosterMaxOutputFor`).
|
|
341
|
+
...(rosterOutput === undefined ? {} : { out: rosterOutput }),
|
|
298
342
|
});
|
|
299
343
|
}
|
|
300
344
|
|
|
@@ -457,7 +501,40 @@ export async function discoverQoderModels(input: {
|
|
|
457
501
|
lastError = new Error("Qoder model list returned no usable models");
|
|
458
502
|
continue;
|
|
459
503
|
}
|
|
460
|
-
const models = enabled.
|
|
504
|
+
const models = enabled.flatMap((entry) => {
|
|
505
|
+
// Publish caller-facing virtual ids (`sonus`, `lite-qwen-3.7-plus`, …)
|
|
506
|
+
// instead of the opaque roster keys (`smodel`, `qmodel`, …). A discovery
|
|
507
|
+
// row must look like what the UI offers and what a caller can type;
|
|
508
|
+
// keeping the raw key here made live-synced rows appear under names
|
|
509
|
+
// nobody can route to by display, and hid overridden cards like `lite`.
|
|
510
|
+
// More than one virtual may share a slot (`lite-qwen-3.7-plus` and
|
|
511
|
+
// `qwen3.7-plus` both dispatch to `qmodel`), so every match on the slot
|
|
512
|
+
// is published.
|
|
513
|
+
const virtuals = QODER_VIRTUAL_MODELS.filter((virtual) => virtual.target === entry.key);
|
|
514
|
+
if (virtuals.length === 0) return [qoderCatalogModel(entry, providerId)];
|
|
515
|
+
const rosterOutput = qoderRosterMaxOutputFor(providerId, entry.key);
|
|
516
|
+
return virtuals.map((virtual) =>
|
|
517
|
+
defineModel({
|
|
518
|
+
id: virtual.id,
|
|
519
|
+
providerId,
|
|
520
|
+
wireFamily: "chat",
|
|
521
|
+
endpoint: QODER_CHAT_PATH,
|
|
522
|
+
ctx: entry.maxInputTokens ?? null,
|
|
523
|
+
vision: virtual.vision ?? entry.isVl,
|
|
524
|
+
reasoning: entry.isReasoning,
|
|
525
|
+
// A virtual override wins; otherwise inherit the static roster's
|
|
526
|
+
// ceiling for the slot instead of a base-catalog/majority estimate
|
|
527
|
+
// (see `qoderRosterMaxOutputFor`).
|
|
528
|
+
...(virtual.outputLimit !== undefined
|
|
529
|
+
? { out: virtual.outputLimit }
|
|
530
|
+
: rosterOutput === undefined
|
|
531
|
+
? {}
|
|
532
|
+
: { out: rosterOutput }),
|
|
533
|
+
free: virtual.free ?? entry.isFree,
|
|
534
|
+
freeTier: virtual.free ?? entry.isFree,
|
|
535
|
+
}),
|
|
536
|
+
);
|
|
537
|
+
});
|
|
461
538
|
// Index the widest selectable window per key (from `context_config`,
|
|
462
539
|
// already computed into `maxInputTokens` by `mapQoderCatalogResponse`) so
|
|
463
540
|
// dispatch can send the maximum rather than the currently active one.
|
|
@@ -8,14 +8,16 @@
|
|
|
8
8
|
* the same endpoints as the upstream client.
|
|
9
9
|
*
|
|
10
10
|
* The issued token is used directly at dispatch: `dt-...` tokens skip the
|
|
11
|
-
* PAT→jobToken exchange.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `auth_state`, because the token itself carries neither and every
|
|
16
|
-
* needs both for COSY signing.
|
|
11
|
+
* PAT→jobToken exchange. Refresh uses the deviceToken refresh endpoint
|
|
12
|
+
* (`POST /api/v1/deviceToken/refresh` with `{refresh_token, machine_id,
|
|
13
|
+
* machine_token}`), verified live 2026-11-02 — it returns a fresh `dt-…`
|
|
14
|
+
* and a rotated `drt-…`. The account's Qoder user id and machine id travel
|
|
15
|
+
* in `auth_state`, because the token itself carries neither and every
|
|
16
|
+
* dispatch needs both for COSY signing.
|
|
17
17
|
*/
|
|
18
18
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
19
|
+
import { execSync } from "node:child_process";
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
19
21
|
import { QODER_REALMS, type QoderRealm } from "./qoder-realm";
|
|
20
22
|
import {
|
|
21
23
|
base64UrlEncode,
|
|
@@ -28,7 +30,7 @@ import type {
|
|
|
28
30
|
OAuthDeviceStartResult,
|
|
29
31
|
OAuthExchangeResult,
|
|
30
32
|
} from "../../authentication/oauth-flow-store";
|
|
31
|
-
import type { OAuthTokenRefreshResult } from "../../authentication/oauth-refresh-service";
|
|
33
|
+
import type { OAuthTokenRefreshResult, OAuthRefreshContext } from "../../authentication/oauth-refresh-service";
|
|
32
34
|
import { OAuthDeviceFlow } from "../../authentication/oauth-device-flow";
|
|
33
35
|
|
|
34
36
|
/** One Qoder account profile, as much of it as the gateway uses. */
|
|
@@ -109,9 +111,7 @@ export function readQoderUserInfo(body: Record<string, unknown>): QoderUserInfo
|
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
/** Global/intl endpoints. Kept as the default realm's values, unchanged. */
|
|
112
|
-
export const QODER_LOGIN_URL = "https://qoder.com/device/selectAccounts" as const;
|
|
113
114
|
export const QODER_DEVICE_TOKEN_URL = "https://openapi.qoder.sh/api/v1/deviceToken/poll" as const;
|
|
114
|
-
export const QODER_USERINFO_URL = "https://openapi.qoder.sh/api/v1/userinfo" as const;
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
117
|
* Per-realm device endpoints, derived from `QODER_REALMS`.
|
|
@@ -221,6 +221,82 @@ function parseDeviceState(value: string | undefined): QoderDeviceState | undefin
|
|
|
221
221
|
return { verifier, machineId };
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Random PKCE verifier in the exact alphabet the desktop IDE uses.
|
|
226
|
+
*
|
|
227
|
+
* The IDE's `startDeviceFlow` draws the verifier from
|
|
228
|
+
* `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~`. Using
|
|
229
|
+
* the same alphabet keeps the login byte-for-byte identical to the desktop
|
|
230
|
+
* client, which the approval server may use to fingerprint the requesting
|
|
231
|
+
* client. Length is fixed at 64 — RFC 7636 §4.1 requires [43,128]; a fixed
|
|
232
|
+
* length avoids a non-cryptographic length while the content comes entirely
|
|
233
|
+
* from the CSPRNG (`randomBytes`) below.
|
|
234
|
+
*/
|
|
235
|
+
function qoderIdeVerifier(): string {
|
|
236
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
237
|
+
// RFC 7636 §4.1: verifier length must be within [43,128]; fixed here. Content
|
|
238
|
+
// comes from the CSPRNG (randomBytes) below. `67` (alphabet length) does not
|
|
239
|
+
// divide 256, so the `%` mapping has a negligible modulo bias (~21/256 per
|
|
240
|
+
// char) — irrelevant for a 64-char secret with ~388 bits of input entropy.
|
|
241
|
+
const length = 64;
|
|
242
|
+
const bytes = randomBytes(length);
|
|
243
|
+
let out = "";
|
|
244
|
+
for (let i = 0; i < length; i += 1) {
|
|
245
|
+
out += alphabet[bytes[i]! % alphabet.length];
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* The hardware machine id the desktop IDE reports for the device flow.
|
|
252
|
+
*
|
|
253
|
+
* The IDE's `getMachineId` is platform-hardware-derived: Windows reads
|
|
254
|
+
* `MachineGuid` from `HKLM\SOFTWARE\Microsoft\Cryptography`, macOS reads the
|
|
255
|
+
* `IOPlatformUUID` from IORegistry, Linux reads `/etc/machine-id`. The device
|
|
256
|
+
* login sends it as `machine_id`, and the upstream correlates the approved
|
|
257
|
+
* device to the account. A random UUID instead registers a different device,
|
|
258
|
+
* which is the observable difference between an IDE login that chats and a
|
|
259
|
+
* gateway login that is refused with quota errors.
|
|
260
|
+
*
|
|
261
|
+
* Returns the hardware id when it can be read, else `""` so callers fall back
|
|
262
|
+
* to a random UUID exactly like the IDE does on an unreadable machine.
|
|
263
|
+
*/
|
|
264
|
+
export function qoderHardwareMachineId(): string {
|
|
265
|
+
try {
|
|
266
|
+
if (process.platform === "win32") {
|
|
267
|
+
const out = execSync('reg query "HKLM\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid', {
|
|
268
|
+
encoding: "utf8",
|
|
269
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
270
|
+
timeout: 5000,
|
|
271
|
+
});
|
|
272
|
+
const m = /MachineGuid\s+REG_SZ\s+([0-9a-fA-F-]+)/.exec(out);
|
|
273
|
+
return m?.[1]?.trim() ?? "";
|
|
274
|
+
}
|
|
275
|
+
if (process.platform === "darwin") {
|
|
276
|
+
const out = execSync("ioreg -rd1 -c IOPlatformExpertDevice", {
|
|
277
|
+
encoding: "utf8",
|
|
278
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
279
|
+
timeout: 5000,
|
|
280
|
+
});
|
|
281
|
+
const m = /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out);
|
|
282
|
+
return m?.[1]?.trim() ?? "";
|
|
283
|
+
}
|
|
284
|
+
if (process.platform === "linux") {
|
|
285
|
+
for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
286
|
+
try {
|
|
287
|
+
const v = readFileSync(p, "utf8").trim();
|
|
288
|
+
if (v) return v;
|
|
289
|
+
} catch {
|
|
290
|
+
// try the next candidate
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
} catch {
|
|
295
|
+
// fall through to the caller's random fallback
|
|
296
|
+
}
|
|
297
|
+
return "";
|
|
298
|
+
}
|
|
299
|
+
|
|
224
300
|
export class QoderOAuthClient extends OAuthDeviceFlow {
|
|
225
301
|
override readonly supportsDeviceCode = true;
|
|
226
302
|
override readonly supportsBrowserCode = false;
|
|
@@ -254,25 +330,35 @@ export class QoderOAuthClient extends OAuthDeviceFlow {
|
|
|
254
330
|
override async startDeviceAuth(
|
|
255
331
|
_context?: OAuthDeviceFlowContext,
|
|
256
332
|
): Promise<OAuthDeviceStartResult> {
|
|
257
|
-
|
|
333
|
+
// 1:1 with the desktop IDE device flow (unpacked worker `startDeviceFlow`):
|
|
334
|
+
// the verifier is random from the IDE's own alphabet (`A-Za-z0-9-._~`,
|
|
335
|
+
// 43–128 chars), the challenge is PKCE S256, the nonce is a full UUID
|
|
336
|
+
// (dashes included), and the client id is ALWAYS sent — intl uses the
|
|
337
|
+
// same id the realm table carries, CN uses its own. Sending no client id
|
|
338
|
+
// (or stripping the UUID dashes) makes the approval screen classify the
|
|
339
|
+
// device differently and can produce an account the model server later
|
|
340
|
+
// refuses with quota/entitlement errors.
|
|
341
|
+
//
|
|
342
|
+
// The machine id defaults to the hardware id (same source the IDE reads),
|
|
343
|
+
// but an operator may pass `machine_id`/`machine_token` through
|
|
344
|
+
// `context.parameters` to reuse a device identity the IDE already holds
|
|
345
|
+
// (e.g. `aicoding.loginMachineIdentity.v1.qoder.local`) — the upstream
|
|
346
|
+
// correlates the approved device to the account, so matching the IDE's
|
|
347
|
+
// device is how a gateway login chats on the same account the IDE does.
|
|
348
|
+
const overrideMachineId = nonEmptyTrimmedString(_context?.parameters?.["machine_id"]);
|
|
349
|
+
const overrideMachineToken = nonEmptyTrimmedString(_context?.parameters?.["machine_token"]);
|
|
350
|
+
const verifier = qoderIdeVerifier();
|
|
258
351
|
const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// has always sent. Both shapes are upstream fact from the donor, and only
|
|
262
|
-
// the CN branch is new, so the working intl URL is untouched.
|
|
263
|
-
const nonce = this.realm === "cn" ? randomUUID() : randomUUID().replaceAll("-", "");
|
|
264
|
-
const machineId = randomUUID();
|
|
352
|
+
const nonce = randomUUID();
|
|
353
|
+
const machineId = overrideMachineId ?? (qoderHardwareMachineId() || randomUUID());
|
|
265
354
|
const params = new URLSearchParams({
|
|
266
355
|
challenge,
|
|
267
356
|
challenge_method: "S256",
|
|
268
|
-
machine_id: machineId,
|
|
269
357
|
nonce,
|
|
358
|
+
machine_id: machineId,
|
|
359
|
+
client_id: QODER_REALMS[this.realm].clientId,
|
|
270
360
|
});
|
|
271
|
-
if (
|
|
272
|
-
const config = QODER_REALMS.cn;
|
|
273
|
-
params.set("client_id", config.clientId);
|
|
274
|
-
params.set("redirect_uri", config.redirectUri);
|
|
275
|
-
}
|
|
361
|
+
if (overrideMachineToken !== undefined) params.set("machine_token", overrideMachineToken);
|
|
276
362
|
return {
|
|
277
363
|
verificationUri: `${qoderOAuthUrls(this.realm).loginUrl}?${params.toString()}`,
|
|
278
364
|
// This flow shows no user code: approval happens against the
|
|
@@ -395,10 +481,64 @@ export class QoderOAuthClient extends OAuthDeviceFlow {
|
|
|
395
481
|
}
|
|
396
482
|
|
|
397
483
|
override async refresh(
|
|
398
|
-
|
|
399
|
-
|
|
484
|
+
refreshToken: string,
|
|
485
|
+
signal?: AbortSignal,
|
|
486
|
+
context?: OAuthRefreshContext,
|
|
400
487
|
): Promise<OAuthTokenRefreshResult> {
|
|
401
|
-
|
|
488
|
+
// The deviceToken refresh endpoint is the one working refresh grant for this
|
|
489
|
+
// flow (verified live 2026-11-02: POST /api/v1/deviceToken/refresh with
|
|
490
|
+
// `{refresh_token, machine_id, machine_token}` returns 200 with a fresh
|
|
491
|
+
// `device_token` + rotated `refresh_token`). The abstract OAuth refresh
|
|
492
|
+
// endpoint answers 403 for this flow, so we post to the deviceToken one.
|
|
493
|
+
const authState = record(context?.auth_state);
|
|
494
|
+
const machineId = authState === undefined ? undefined : nonEmptyTrimmedString(authState.machineId);
|
|
495
|
+
if (machineId === undefined) {
|
|
496
|
+
throw new Error("Qoder refresh requires auth_state.machineId");
|
|
497
|
+
}
|
|
498
|
+
const response = await this.fetchFn(
|
|
499
|
+
`${qoderOAuthUrls(this.realm).deviceTokenUrl}/refresh`,
|
|
500
|
+
{
|
|
501
|
+
method: "POST",
|
|
502
|
+
headers: {
|
|
503
|
+
"content-type": "application/json",
|
|
504
|
+
accept: "application/json",
|
|
505
|
+
"user-agent": "Go-http-client/2.0",
|
|
506
|
+
},
|
|
507
|
+
body: JSON.stringify({
|
|
508
|
+
refresh_token: refreshToken,
|
|
509
|
+
machine_id: machineId,
|
|
510
|
+
machine_token: machineId,
|
|
511
|
+
}),
|
|
512
|
+
...(signal === undefined ? {} : { signal }),
|
|
513
|
+
},
|
|
514
|
+
);
|
|
515
|
+
const raw = await response.text();
|
|
516
|
+
let payload: unknown;
|
|
517
|
+
try {
|
|
518
|
+
payload = JSON.parse(raw);
|
|
519
|
+
} catch {
|
|
520
|
+
throw new Error(`Qoder token refresh returned invalid JSON (${response.status})`);
|
|
521
|
+
}
|
|
522
|
+
if (!response.ok) {
|
|
523
|
+
const message = record(payload);
|
|
524
|
+
throw new Error(
|
|
525
|
+
nonEmptyTrimmedString(message?.message) ??
|
|
526
|
+
nonEmptyTrimmedString(message?.errorMessage) ??
|
|
527
|
+
`Qoder token refresh failed (${response.status})`,
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
const body = record(payload);
|
|
531
|
+
const deviceToken = body === undefined ? undefined : nonEmptyTrimmedString(body.device_token);
|
|
532
|
+
if (deviceToken === undefined) {
|
|
533
|
+
throw new Error("Qoder token refresh response omitted device_token");
|
|
534
|
+
}
|
|
535
|
+
const nextRefresh = nonEmptyTrimmedString(body?.refresh_token);
|
|
536
|
+
const expiresAt = new Date(parseQoderExpiry(body?.expires_at, body?.expires_in));
|
|
537
|
+
return {
|
|
538
|
+
access: deviceToken,
|
|
539
|
+
...(nextRefresh === undefined ? {} : { refresh: nextRefresh }),
|
|
540
|
+
expiresAt,
|
|
541
|
+
};
|
|
402
542
|
}
|
|
403
543
|
}
|
|
404
544
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// Qoder quota.
|
|
2
2
|
//
|
|
3
|
-
// `GET /api/v2/quota/usage` on the **openapi** host, with a
|
|
4
|
-
//
|
|
5
|
-
// differs from inference.
|
|
3
|
+
// `GET /api/v2/quota/usage` on the **openapi** host, with a bearer token — no
|
|
4
|
+
// COSY signature on this surface, which is the one place the business API
|
|
5
|
+
// differs from inference. Device tokens (`dt-…`) are used directly; a PAT
|
|
6
|
+
// (`pt-…`) is exchanged for a job token (`jt-…`) first, because the business
|
|
7
|
+
// API rejects a raw PAT with 401 `TOKEN_EXPIRE`.
|
|
6
8
|
//
|
|
7
9
|
// The response carries two quota objects and they are not interchangeable:
|
|
8
10
|
//
|
|
@@ -36,6 +38,7 @@ import {
|
|
|
36
38
|
import type { QuotaCollectionContext } from "../../quota/quota-support";
|
|
37
39
|
import { log } from "../../../observability/logger";
|
|
38
40
|
import { QODER_REALMS, type QoderRealm } from "./qoder-realm";
|
|
41
|
+
import { isQoderPat, resolveQoderAuth } from "./qoder";
|
|
39
42
|
|
|
40
43
|
/** Paths on the realm's openapi host. */
|
|
41
44
|
const QUOTA_PATH = "/api/v2/quota/usage";
|
|
@@ -75,7 +78,29 @@ function creditWindow(
|
|
|
75
78
|
): ProviderQuotaWindow | null {
|
|
76
79
|
if (remaining === null && limit === null) return null;
|
|
77
80
|
const used = remaining !== null && limit !== null ? limit - remaining : null;
|
|
78
|
-
|
|
81
|
+
// Give pooled windows a percentage so the console can render a real bar and
|
|
82
|
+
// tone instead of an uncolored "quota-only" row (which is what a null
|
|
83
|
+
// percent produced: every Qoder account was grey, and "enable available
|
|
84
|
+
// accounts" could never match a window). A ceiling of 0 with nothing left is
|
|
85
|
+
// a spent balance — report 0% remaining rather than a null percent.
|
|
86
|
+
let usedPercent: number | null = null;
|
|
87
|
+
if (remaining !== null && limit !== null) {
|
|
88
|
+
if (limit > 0) usedPercent = ((limit - remaining) / limit) * 100;
|
|
89
|
+
else if (remaining <= 0) usedPercent = 100;
|
|
90
|
+
}
|
|
91
|
+
return percentWindow(kind, label, usedPercent, resetsAt, used, limit, "pooled");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The business API answers "never resets" with a far-future sentinel
|
|
96
|
+
* (`9999-12-31T00:00:00.000Z`). Keeping it on the wire makes the console show
|
|
97
|
+
* a 7978-year countdown. Any date at or beyond year 9999 means no reset.
|
|
98
|
+
*/
|
|
99
|
+
function normalizeQoderReset(resetsAt: string | null): string | null {
|
|
100
|
+
if (resetsAt === null) return null;
|
|
101
|
+
const date = new Date(resetsAt);
|
|
102
|
+
if (!Number.isFinite(date.getTime())) return null;
|
|
103
|
+
return date.getUTCFullYear() >= 9999 ? null : resetsAt;
|
|
79
104
|
}
|
|
80
105
|
|
|
81
106
|
/**
|
|
@@ -103,11 +128,28 @@ export async function fetchQoderQuota(
|
|
|
103
128
|
|
|
104
129
|
let payload: unknown;
|
|
105
130
|
try {
|
|
131
|
+
// A PAT (`pt-…`) is not an active bearer on the business API: it is a
|
|
132
|
+
// minted credential that must be exchanged for a short-lived job token
|
|
133
|
+
// (`jt-…`) first. Verified live 2026-11-02: a raw `pt-…` bearer answers
|
|
134
|
+
// 401 `TOKEN_EXPIRE` while the exchanged `jt-…` answers 200. Device
|
|
135
|
+
// tokens (`dt-…`) are used directly. A failed exchange — expired/revoked
|
|
136
|
+
// PAT — is an unreadable balance, not an error that cools the account.
|
|
137
|
+
let bearer = credential;
|
|
138
|
+
if (isQoderPat(credential)) {
|
|
139
|
+
const auth = await resolveQoderAuth({
|
|
140
|
+
secret: credential,
|
|
141
|
+
authState: raw,
|
|
142
|
+
signal: AbortSignal.timeout(15_000),
|
|
143
|
+
fetcher,
|
|
144
|
+
realm,
|
|
145
|
+
});
|
|
146
|
+
bearer = auth.securityOauthToken;
|
|
147
|
+
}
|
|
106
148
|
const response = await fetcher(url, {
|
|
107
149
|
method: "GET",
|
|
108
150
|
headers: {
|
|
109
151
|
accept: "application/json",
|
|
110
|
-
authorization: `Bearer ${
|
|
152
|
+
authorization: `Bearer ${bearer}`,
|
|
111
153
|
"user-agent": config.userAgent,
|
|
112
154
|
},
|
|
113
155
|
});
|
|
@@ -148,7 +190,7 @@ export async function fetchQoderQuota(
|
|
|
148
190
|
return { source: "qoder", plan: null, windows: [], error: null };
|
|
149
191
|
}
|
|
150
192
|
|
|
151
|
-
const resetsAt = isoDate(root["expiresAt"] ?? root["expireAt"] ?? root["expires_at"]);
|
|
193
|
+
const resetsAt = normalizeQoderReset(isoDate(root["expiresAt"] ?? root["expireAt"] ?? root["expires_at"]));
|
|
152
194
|
const windows: ProviderQuotaWindow[] = [];
|
|
153
195
|
const plan = creditWindow("qoder:plan", "Plan credits", number(userQuota?.["remaining"]), number(userQuota?.["total"]), resetsAt);
|
|
154
196
|
if (plan) windows.push(plan);
|
|
@@ -41,9 +41,13 @@ export const QODER_MODELS_PATH = "/algo/api/v2/model/list?Encode=1";
|
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* The modern model-server surface used by the IDE / QoderWake (OpenAI-shaped
|
|
44
|
-
* chunks, plain bearer auth). This surface streams immediately
|
|
45
|
-
* `/algo` chat endpoint answers
|
|
46
|
-
*
|
|
44
|
+
* chunks, plain bearer auth). This surface streams immediately and echoes the
|
|
45
|
+
* real model key — the legacy `/algo` chat endpoint answers
|
|
46
|
+
* `10605 isQueued` (parking requests in a server-side queue) and echoes a
|
|
47
|
+
* fixed `"auto"` model. Dispatch tries this surface first, and falls back to
|
|
48
|
+
* legacy COSY only when it refuses the account on entitlement grounds (HTTP
|
|
49
|
+
* 402 / `{"code":116}`) AND the dispatched model is a free-tier row — for a
|
|
50
|
+
* billed model the same code is genuine quota exhaustion and must surface.
|
|
47
51
|
*/
|
|
48
52
|
export const QODER_IDE_CHAT_PATH = "/model/v1/chat/completions";
|
|
49
53
|
|
|
@@ -76,7 +80,9 @@ export const QODER_REALMS: Readonly<Record<QoderRealm, QoderRealmConfig>> = {
|
|
|
76
80
|
gatewayFallbacks: [],
|
|
77
81
|
openapi: "https://openapi.qoder.com.cn",
|
|
78
82
|
website: "https://qoder.com.cn",
|
|
79
|
-
|
|
83
|
+
// 1:1 with the desktop IDE's device flow (`Hfl` in the unpacked worker):
|
|
84
|
+
// the client id the IDE registers for the CN deployment.
|
|
85
|
+
clientId: "e93fe488-5778-4c35-a6fc-0f54ed7b3139",
|
|
80
86
|
redirectUri: "qoder-work-cn://",
|
|
81
87
|
userAgent: "QoderWork/1.1.64",
|
|
82
88
|
},
|
|
@@ -112,30 +118,3 @@ export function qoderInferenceCandidates(
|
|
|
112
118
|
}
|
|
113
119
|
return [config.gateway, ...config.gatewayFallbacks];
|
|
114
120
|
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Model keys that exist in exactly one realm.
|
|
118
|
-
*
|
|
119
|
-
* From the donor's `INTL_EXCLUSIVE` / `CN_EXCLUSIVE` tables. A request for one
|
|
120
|
-
* of these on the wrong realm does not degrade — it is refused — so the roster
|
|
121
|
-
* each realm publishes is the authority, not a union.
|
|
122
|
-
*/
|
|
123
|
-
export const QODER_INTL_EXCLUSIVE: ReadonlySet<string> = new Set([
|
|
124
|
-
"fast",
|
|
125
|
-
"efficient",
|
|
126
|
-
"cmodel",
|
|
127
|
-
"smodel",
|
|
128
|
-
"ultimate",
|
|
129
|
-
"performance",
|
|
130
|
-
]);
|
|
131
|
-
|
|
132
|
-
export const QODER_CN_EXCLUSIVE: ReadonlySet<string> = new Set([
|
|
133
|
-
"q37fmodel",
|
|
134
|
-
"gm51model",
|
|
135
|
-
]);
|
|
136
|
-
|
|
137
|
-
/** True when this model key is served only by the other realm. */
|
|
138
|
-
export function isForeignQoderModel(realm: QoderRealm, modelId: string): boolean {
|
|
139
|
-
const foreign = realm === "cn" ? QODER_INTL_EXCLUSIVE : QODER_CN_EXCLUSIVE;
|
|
140
|
-
return foreign.has(modelId.trim().toLowerCase());
|
|
141
|
-
}
|