cartethyia 2.7.3 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +59 -57
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/package.json +4 -5
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +67 -10
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +8 -4
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +92 -31
- package/src/console/api-keys/routes.ts +131 -6
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +5 -5
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +40 -34
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +11 -9
- package/src/console/backup/export-endpoint.ts +11 -12
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +32 -36
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +4 -4
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +36 -4
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +18 -4
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +5 -1
- package/src/console/observability/live.ts +34 -21
- package/src/console/observability/logs.ts +4 -4
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +36 -20
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +82 -22
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +110 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +30 -6
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1662 -1292
- package/src/console/providers/detail/routes.ts +4 -4
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +95 -16
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +285 -67
- package/src/console/quota/account-test-egress.ts +4 -4
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +13 -10
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +12 -12
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/routes.ts +3 -3
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +112 -44
- package/src/console/share/share-stats.ts +109 -39
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +42 -6
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +256 -98
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +76 -5
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +55 -51
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +22 -2
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +24 -75
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +286 -214
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/telemetry-status.ts +1 -1
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +10 -5
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +19 -7
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +177 -45
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +109 -4
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/stores/telemetry-store.ts +1 -1
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +12 -16
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/image-generation.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +27 -12
- package/src/protocol/response/codex.ts +2 -2
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +6 -6
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +3 -3
- package/src/protocol/sse-decoder.ts +64 -4
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +8 -8
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +44 -5
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/configured-provider.ts +1 -1
- package/src/providers/default-registry.ts +23 -35
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +50 -15
- package/src/providers/discovery/probing-service.ts +18 -20
- package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +15 -26
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
- package/src/providers/integrations/buddy/buddy-media.ts +16 -16
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +5 -16
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +76 -116
- package/src/providers/integrations/commandcode/quota.ts +132 -48
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +53 -20
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +13 -13
- package/src/providers/integrations/gemini.ts +11 -11
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +47 -20
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +88 -42
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +91 -78
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
- package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +22 -28
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
- package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
- package/src/providers/integrations/qoder/qoder.ts +521 -63
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/tokenharbor.ts +2 -2
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +59 -19
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +22 -6
- package/src/providers/operations/provider-credential-service.ts +35 -6
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +65 -10
- package/src/providers/provider-credential-resolver.ts +1 -1
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-registry.ts +4 -4
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +28 -10
- package/src/providers/quota/refresh.ts +29 -6
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +6 -8
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +8 -8
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +20 -8
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/coerce.ts +19 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +5 -4
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +28 -25
- package/src/transport/dispatch/generation.ts +33 -53
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +10 -8
- package/src/transport/dispatch/responses-compact.ts +35 -56
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +11 -10
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +27 -55
- package/src/transport/dispatch/token-count.ts +49 -46
- package/src/transport/dispatch/websearch.ts +24 -50
- package/src/transport/drain-endpoint.ts +3 -3
- package/src/transport/failure-policy.ts +19 -6
- package/src/transport/guards/auth-guard.ts +6 -11
- package/src/transport/guards/body-policy.ts +7 -7
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +2 -2
- package/src/transport/guards/pipeline.ts +7 -7
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +15 -20
- package/src/transport/request/preparer.ts +306 -27
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +5 -20
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +113 -19
- package/src/transport/routing/route-model.ts +131 -38
- package/src/transport/routing/router.ts +154 -39
- package/src/transport/shutdown-notice.ts +21 -9
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +3 -3
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/encode.ts +1 -1
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +376 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
- package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
- package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
- package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
- package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
- package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
- package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
- package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
- package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
- package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
- package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
- package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
- package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
- package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
- package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
- package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
- package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
- package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
- package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
- package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
- package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
- package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -27,6 +27,7 @@ import { CLAUDE_CODE_USER_AGENT } from "../integrations/claude/claude-fingerprin
|
|
|
27
27
|
import { authCredential, codexJwtAccountId, text } from "../quota/quota-contracts";
|
|
28
28
|
import { getCodexVersion } from "./client-versions";
|
|
29
29
|
import { buildCodexUserAgent } from "./cli-platform";
|
|
30
|
+
import { TtlCache } from "../../persistence/ttl-cache";
|
|
30
31
|
|
|
31
32
|
/** One redeemable (or already-spent) saved reset. */
|
|
32
33
|
export interface ResetCredit {
|
|
@@ -62,6 +63,51 @@ const JUNIPER_PROGRAM = "juniper_tide";
|
|
|
62
63
|
const ORG_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
63
64
|
/** Cedar grant ids are short slugs; anything else never reaches the provider. */
|
|
64
65
|
const CEDAR_GRANT_ID = /^[a-z0-9_-]{1,40}$/;
|
|
66
|
+
/** Abort budget for one upstream reset-path request, mirroring quota refresh. */
|
|
67
|
+
const RESET_FETCH_TIMEOUT_MS = 15_000;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Short per-account cache for {@link listAccountResetCredits}.
|
|
71
|
+
*
|
|
72
|
+
* The console reads the reset list each time a dialog opens; without a cache
|
|
73
|
+
* every open hits the upstream live. The TTL is short — the list changes when
|
|
74
|
+
* a credit is consumed, and `invalidateResetCreditsCache` clears the entry on
|
|
75
|
+
* a successful redemption — so staleness is bounded to a dialog-refresh
|
|
76
|
+
* window. Failures (`null`) are deliberately not cached: a discovery error
|
|
77
|
+
* stays retryable on the next open.
|
|
78
|
+
*/
|
|
79
|
+
const RESET_CREDITS_TTL_MS = 30_000;
|
|
80
|
+
const resetCreditsCache = new TtlCache<ResetCreditList | null>({
|
|
81
|
+
ttlMs: RESET_CREDITS_TTL_MS,
|
|
82
|
+
maxEntries: 512,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Reads one account's saved reset credits through the short TTL cache.
|
|
87
|
+
* Concurrent misses share one upstream discovery via the cache's load-through
|
|
88
|
+
* deduplication.
|
|
89
|
+
*/
|
|
90
|
+
export async function getCachedResetCredits(
|
|
91
|
+
providerId: string,
|
|
92
|
+
accountId: string,
|
|
93
|
+
load: () => Promise<ResetCreditList | null>,
|
|
94
|
+
): Promise<ResetCreditList | null> {
|
|
95
|
+
return resetCreditsCache.load(`${providerId}:${accountId}`, load, {
|
|
96
|
+
namespace: "reset-credits",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Drops every cached reset-credit entry for one account. Called after a
|
|
102
|
+
* successful redemption so the dialog does not keep showing a spent credit for
|
|
103
|
+
* the rest of the TTL.
|
|
104
|
+
*/
|
|
105
|
+
export function invalidateResetCreditsCache(accountId: string): void {
|
|
106
|
+
const suffix = `:${accountId}`;
|
|
107
|
+
for (const key of resetCreditsCache.keys()) {
|
|
108
|
+
if (key.endsWith(suffix)) resetCreditsCache.delete(key);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
65
111
|
|
|
66
112
|
export function supportsAccountReset(providerId: string): boolean {
|
|
67
113
|
const norm = providerId.trim().toLowerCase();
|
|
@@ -172,7 +218,10 @@ export async function listAccountResetCredits(
|
|
|
172
218
|
try {
|
|
173
219
|
const res = await fetcher(
|
|
174
220
|
"https://chatgpt.com/backend-api/wham/rate-limit-reset-credits",
|
|
175
|
-
{
|
|
221
|
+
{
|
|
222
|
+
headers: codexHeaders(access, accountId, version, false),
|
|
223
|
+
signal: AbortSignal.timeout(RESET_FETCH_TIMEOUT_MS),
|
|
224
|
+
},
|
|
176
225
|
);
|
|
177
226
|
if (!res.ok) return null;
|
|
178
227
|
const payload = (await res.json()) as unknown;
|
|
@@ -237,6 +286,7 @@ async function discoverCedar(
|
|
|
237
286
|
try {
|
|
238
287
|
res = await fetcher("https://api.anthropic.com/api/oauth/usage?cedar_ember=1&skip_spend=1", {
|
|
239
288
|
headers: claudeHeaders(access, false),
|
|
289
|
+
signal: AbortSignal.timeout(RESET_FETCH_TIMEOUT_MS),
|
|
240
290
|
});
|
|
241
291
|
} catch {
|
|
242
292
|
return "failed";
|
|
@@ -343,6 +393,7 @@ async function discoverJuniper(
|
|
|
343
393
|
try {
|
|
344
394
|
res = await fetcher("https://api.anthropic.com/api/oauth/usage?at_wall=1&skip_spend=1", {
|
|
345
395
|
headers: claudeHeaders(access, false),
|
|
396
|
+
signal: AbortSignal.timeout(RESET_FETCH_TIMEOUT_MS),
|
|
346
397
|
});
|
|
347
398
|
} catch {
|
|
348
399
|
return null;
|
|
@@ -385,6 +436,7 @@ async function resolveClaudeOrg(access: string, fetcher: typeof fetch): Promise<
|
|
|
385
436
|
try {
|
|
386
437
|
const res = await fetcher("https://api.anthropic.com/api/oauth/profile", {
|
|
387
438
|
headers: claudeHeaders(access, false),
|
|
439
|
+
signal: AbortSignal.timeout(RESET_FETCH_TIMEOUT_MS),
|
|
388
440
|
});
|
|
389
441
|
if (!res.ok) return null;
|
|
390
442
|
const payload = (await res.json()) as unknown;
|
|
@@ -449,6 +501,7 @@ export async function consumeAccountResetCredit(args: {
|
|
|
449
501
|
redeem_request_id: randomUUID(),
|
|
450
502
|
...(codexAccountId ? { account_id: codexAccountId } : {}),
|
|
451
503
|
}),
|
|
504
|
+
signal: AbortSignal.timeout(RESET_FETCH_TIMEOUT_MS),
|
|
452
505
|
},
|
|
453
506
|
);
|
|
454
507
|
const body = (await res.json().catch(() => undefined)) as Record<string, unknown> | undefined;
|
|
@@ -528,6 +581,7 @@ export async function consumeAccountResetCredit(args: {
|
|
|
528
581
|
method: "POST",
|
|
529
582
|
headers: claudeHeaders(access, true),
|
|
530
583
|
body: JSON.stringify(body),
|
|
584
|
+
signal: AbortSignal.timeout(RESET_FETCH_TIMEOUT_MS),
|
|
531
585
|
},
|
|
532
586
|
);
|
|
533
587
|
const payload = (await res.json().catch(() => undefined)) as
|
|
@@ -651,6 +705,9 @@ async function recordResetActivity(
|
|
|
651
705
|
errorCategory: null,
|
|
652
706
|
createdAt: now,
|
|
653
707
|
});
|
|
708
|
+
// The redeemed credit is gone; the next dialog open must not serve the
|
|
709
|
+
// pre-redeem list for the rest of the TTL.
|
|
710
|
+
invalidateResetCreditsCache(accountId);
|
|
654
711
|
await snapshotInvalidator?.invalidate();
|
|
655
712
|
return;
|
|
656
713
|
}
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* Every value below is what the real client would report, taken from each
|
|
17
17
|
* client's own source rather than guessed.
|
|
18
18
|
*/
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
import { hostname as osHostname } from "node:os";
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* `os_info` — the crate Codex reads this from — returns the marketing name
|
|
@@ -52,9 +54,6 @@ const KIMI_OS_VERSION = "macOS 15.6";
|
|
|
52
54
|
*/
|
|
53
55
|
const KIMI_DEVICE_NAME = "MacBook-Pro.local";
|
|
54
56
|
|
|
55
|
-
/** Qoder's `cosy-machineos`: "<arch>_<os>". */
|
|
56
|
-
const QODER_MACHINE_OS = "aarch64_macos";
|
|
57
|
-
|
|
58
57
|
|
|
59
58
|
/**
|
|
60
59
|
* Cline's `x-platform` / `x-platform-version`.
|
|
@@ -128,9 +127,36 @@ export function kimiDeviceName(): string {
|
|
|
128
127
|
return KIMI_DEVICE_NAME;
|
|
129
128
|
}
|
|
130
129
|
|
|
131
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Qoder's `cosy-machineos`: "<arch>_<os>".
|
|
132
|
+
*
|
|
133
|
+
* The desktop IDE builds this from its own runtime
|
|
134
|
+
* (`V8e = ${process.arch==="arm64"?"aarch64":process.arch==="x64"?"x86_64":process.arch}_${process.platform}`),
|
|
135
|
+
* so the gateway mirrors that exact mapping instead of a fixed macOS value.
|
|
136
|
+
*/
|
|
132
137
|
export function qoderMachineOs(): string {
|
|
133
|
-
|
|
138
|
+
const arch = process.arch === "arm64" ? "aarch64" : process.arch === "x64" ? "x86_64" : process.arch;
|
|
139
|
+
return `${arch}_${process.platform}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Qoder's `cosy-machinehostname`.
|
|
144
|
+
*
|
|
145
|
+
* The desktop IDE sends the real machine hostname, normalized to a header-safe
|
|
146
|
+
* form (`B9i` in the unpacked worker: lowercase, non-ASCII and runs of dashes
|
|
147
|
+
* collapsed, truncated to 96 chars with a short hash suffix). Mirror the exact
|
|
148
|
+
* mapping so the value is indistinguishable from the IDE's.
|
|
149
|
+
*/
|
|
150
|
+
export function qoderMachineHostname(): string {
|
|
151
|
+
const raw = (osHostname() ?? "").trim().toLowerCase();
|
|
152
|
+
if (!raw) return "";
|
|
153
|
+
const safe = raw
|
|
154
|
+
.replace(/[^\x21-\x7e]+/gu, "-")
|
|
155
|
+
.replace(/-{2,}/gu, "-")
|
|
156
|
+
.replace(/^-+|-+$/gu, "");
|
|
157
|
+
if (safe.length <= 96) return safe;
|
|
158
|
+
const hash = createHash("sha256").update(safe, "utf8").digest("hex").slice(0, 8);
|
|
159
|
+
return `${safe.slice(0, 96 - 1 - 8)}-${hash}`;
|
|
134
160
|
}
|
|
135
161
|
|
|
136
162
|
/** Cline's `x-platform`. */
|
|
@@ -12,12 +12,11 @@
|
|
|
12
12
|
* only call the synchronous `get`/`snapshot` accessors and never fetch.
|
|
13
13
|
*/
|
|
14
14
|
import { getCachedVersion, resetVersionCacheForTesting } from "./provider-version-cache";
|
|
15
|
+
import type { OutboundFetch } from "../provider-contracts";
|
|
16
|
+
import { composeAbortSignal } from "../authentication/oauth-flow-store";
|
|
15
17
|
|
|
16
18
|
/** Structural fetch contract for providers that inject lightweight transports. */
|
|
17
|
-
export type ClientVersionFetcher =
|
|
18
|
-
input: RequestInfo | URL,
|
|
19
|
-
init?: RequestInit,
|
|
20
|
-
) => Promise<Response>;
|
|
19
|
+
export type ClientVersionFetcher = OutboundFetch;
|
|
21
20
|
|
|
22
21
|
/** One ordered upstream probe. Earlier entries win. */
|
|
23
22
|
export interface ClientVersionSource {
|
|
@@ -156,10 +155,9 @@ export function createClientVersionResolver(
|
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
async function probe(source: ClientVersionSource, fetcher: ClientVersionFetcher, signal?: AbortSignal): Promise<string | null> {
|
|
159
|
-
const timeoutSignal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
160
158
|
const response = await fetcher(source.url, {
|
|
161
159
|
headers: { accept: "application/json, text/plain, */*" },
|
|
162
|
-
signal:
|
|
160
|
+
signal: composeAbortSignal(signal, FETCH_TIMEOUT_MS),
|
|
163
161
|
});
|
|
164
162
|
if (!response.ok) return null;
|
|
165
163
|
return (source.extract ?? defaultExtract)(response);
|
|
@@ -200,7 +200,7 @@ async function githubReleaseVersion(response: Response): Promise<string | null>
|
|
|
200
200
|
* The discovery source below only supplies the *IDE* version that follows the
|
|
201
201
|
* `KiroIDE` device marker.
|
|
202
202
|
*/
|
|
203
|
-
const KIRO_RUNTIME_SDK_VERSION = "1.0.
|
|
203
|
+
const KIRO_RUNTIME_SDK_VERSION = "1.0.44";
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
206
|
* AWS SSO OIDC SDK version stamped into login and token-refresh User-Agents.
|
|
@@ -242,7 +242,12 @@ export const VERSION_SOURCES = {
|
|
|
242
242
|
},
|
|
243
243
|
qoder: {
|
|
244
244
|
key: "qoder",
|
|
245
|
-
|
|
245
|
+
// The desktop IDE hardcodes its COSY_VERSION as "1.1.80" (`S9` in the
|
|
246
|
+
// unpacked worker bundle); the model-server surface sees this value, so
|
|
247
|
+
// the gateway presents the same build the IDE ships. Discovery (npm
|
|
248
|
+
// `@qoder-ai/qodercli/latest`) may report a newer CLI, but the CLI and
|
|
249
|
+
// IDE are different products — the chat surface identity is the IDE's.
|
|
250
|
+
fallback: "1.1.80",
|
|
246
251
|
sources: [
|
|
247
252
|
{
|
|
248
253
|
url: "https://registry.npmjs.org/@qoder-ai/qodercli/latest",
|
|
@@ -254,7 +259,9 @@ export const VERSION_SOURCES = {
|
|
|
254
259
|
key: "opencode",
|
|
255
260
|
// The v1 package (`opencode-ai`) stopped at 1.18. The v2 line publishes
|
|
256
261
|
// under `@opencode/cli`, and this feed is what its own installer reads.
|
|
257
|
-
|
|
262
|
+
// Fallback = the current latest release; raise it whenever the package
|
|
263
|
+
// moves so a cold process does not present a stale client build.
|
|
264
|
+
fallback: "2.2.5",
|
|
258
265
|
minVersion: "2.0.0",
|
|
259
266
|
sources: [{ url: "https://opencode.ai/update/api/latest/cli/npm" }],
|
|
260
267
|
},
|
|
@@ -628,11 +635,8 @@ export function buildGrokAuthUserAgent(version = getGrokVersion()): string {
|
|
|
628
635
|
}
|
|
629
636
|
|
|
630
637
|
|
|
631
|
-
export const getClineClientVersion = accessor("clineClient").get;
|
|
632
638
|
export const getClineSdkVersion = accessor("clineSdk").get;
|
|
633
639
|
export const getClineCliVersion = accessor("clineCli").get;
|
|
634
|
-
export const _resetClineClientVersionCache = accessor("clineClient").reset;
|
|
635
|
-
export const _resetClineSdkVersionCache = accessor("clineSdk").reset;
|
|
636
640
|
export const _resetClineCliVersionCache = accessor("clineCli").reset;
|
|
637
641
|
|
|
638
642
|
export const getCodexVersion = accessor("codex").get;
|
|
@@ -696,7 +700,6 @@ export const _resetCodeBuddyCnVersionCache = accessor("codebuddyCn").reset;
|
|
|
696
700
|
|
|
697
701
|
export const getClaudeCliVersion = accessor("claudeCli").get;
|
|
698
702
|
export const adoptClaudeCliVersion = accessor("claudeCli").adopt;
|
|
699
|
-
export const _resetClaudeCliVersionCache = accessor("claudeCli").reset;
|
|
700
703
|
export const getClaudeSdkVersion = accessor("claudeSdk").get;
|
|
701
704
|
|
|
702
705
|
export const getKiroVersion = accessor("kiro").get;
|
|
@@ -59,6 +59,7 @@ export async function seedBundledModels(
|
|
|
59
59
|
// Same three-valued rule for the inverse: the provider's directory
|
|
60
60
|
// states the tier, so the flag is carried rather than derived.
|
|
61
61
|
freeTier: definition.freeTier ?? null,
|
|
62
|
+
dialect: definition.dialect ?? null,
|
|
62
63
|
source: "builtin",
|
|
63
64
|
sourceUpdatedAt: now,
|
|
64
65
|
enabled: true,
|
|
@@ -88,6 +89,7 @@ export async function seedBundledModels(
|
|
|
88
89
|
cost: sql`excluded.cost`,
|
|
89
90
|
proOnly: sql`excluded.pro_only`,
|
|
90
91
|
freeTier: sql`excluded.free_tier`,
|
|
92
|
+
dialect: sql`excluded.dialect`,
|
|
91
93
|
source: sql`'builtin'`,
|
|
92
94
|
sourceUpdatedAt: sql`excluded.source_updated_at`,
|
|
93
95
|
},
|
|
@@ -20,6 +20,7 @@ import { GATEWAY_USER_AGENT } from "./gateway-user-agent";
|
|
|
20
20
|
import { isIP } from "node:net";
|
|
21
21
|
import { isAddressAllowed } from "../../network/ssrf";
|
|
22
22
|
import type { SsrfPolicy } from "../../config/network";
|
|
23
|
+
import { BoundedLruMap } from "../../transport/routing/bounded-lru";
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Revision of the compiled provider catalog, and the last one materialized.
|
|
@@ -234,14 +235,20 @@ export async function bundledModelCatalog(
|
|
|
234
235
|
* 30 s timer, so an ungated re-seed could be pushed onto the next proxy
|
|
235
236
|
* request for a catalog that had not changed. The paths that do change the
|
|
236
237
|
* catalog bump the revision; everything else is a no-op here.
|
|
238
|
+
*
|
|
239
|
+
* `precomputedCatalog` supplies the compiled catalog when the caller already
|
|
240
|
+
* materialized it (the boot path's deferred B-01 resolution), so the seed
|
|
241
|
+
* shares that walk instead of running a second one. Absent, the walk runs
|
|
242
|
+
* here as before.
|
|
237
243
|
*/
|
|
238
244
|
export async function refreshActiveProviderCatalogs(
|
|
239
245
|
db: CartethyiaDatabase,
|
|
240
246
|
registry: ProviderRegistry,
|
|
247
|
+
precomputedCatalog?: BundledProviderCatalog,
|
|
241
248
|
): Promise<void> {
|
|
242
249
|
const revision = currentCatalogRevision();
|
|
243
250
|
if (revision === lastSeededCatalogRevision) return;
|
|
244
|
-
const catalog = await bundledModelCatalog(registry);
|
|
251
|
+
const catalog = precomputedCatalog ?? (await bundledModelCatalog(registry));
|
|
245
252
|
await seedBundledModels(db, catalog.modelsByProvider);
|
|
246
253
|
lastSeededCatalogRevision = revision;
|
|
247
254
|
}
|
|
@@ -251,8 +258,17 @@ export async function refreshActiveProviderCatalogs(
|
|
|
251
258
|
* Last successfully applied BYOK config per normalized provider ID.
|
|
252
259
|
* Makes repeated `syncByokProviders` runs cheap: an unchanged row is skipped,
|
|
253
260
|
* while a changed one is re-registered instead of being rejected.
|
|
261
|
+
*
|
|
262
|
+
* Bounded (G-04 / B-04): provider IDs are operator-created strings, so an
|
|
263
|
+
* unbounded map would pin an entry for every retired or deleted custom
|
|
264
|
+
* provider forever. The LRU bound keeps only the most recently touched
|
|
265
|
+
* registrations resident; a dropped entry simply re-registers on the next
|
|
266
|
+
* pass (the fingerprint is a skip-ahead optimization, not correctness).
|
|
254
267
|
*/
|
|
255
|
-
const
|
|
268
|
+
const BYOK_FINGERPRINT_CACHE_MAX_ENTRIES = 1024;
|
|
269
|
+
const byokRegistrationFingerprints = new BoundedLruMap<string, string>(
|
|
270
|
+
BYOK_FINGERPRINT_CACHE_MAX_ENTRIES,
|
|
271
|
+
);
|
|
256
272
|
|
|
257
273
|
function byokFingerprint(
|
|
258
274
|
baseUrl: string,
|
|
@@ -290,10 +306,10 @@ export async function registerByokProviders(
|
|
|
290
306
|
try {
|
|
291
307
|
providerId = parseProviderId(row.id);
|
|
292
308
|
} catch {
|
|
293
|
-
throw new GatewayError("invalid_request", 400, `invalid BYOK provider ID: ${row.id}
|
|
309
|
+
throw new GatewayError("invalid_request", 400, `invalid BYOK provider ID: ${row.id} (request {requestId}). Check the shape of what you sent — then resend 🪞`);
|
|
294
310
|
}
|
|
295
311
|
if (isBundledProviderId(providerId)) {
|
|
296
|
-
throw new GatewayError("invalid_request", 409, `BYOK provider ID collides with a built-in: ${row.id}
|
|
312
|
+
throw new GatewayError("invalid_request", 409, `BYOK provider ID collides with a built-in: ${row.id} (request {requestId}). This name's taken — pick another alias ✨`, {
|
|
297
313
|
provider_id: providerId,
|
|
298
314
|
});
|
|
299
315
|
}
|
|
@@ -309,14 +325,14 @@ export async function registerByokProviders(
|
|
|
309
325
|
}
|
|
310
326
|
} catch (error) {
|
|
311
327
|
if (error instanceof GatewayError) throw error;
|
|
312
|
-
throw new GatewayError("invalid_request", 400, `invalid BYOK base URL for provider ${row.id}
|
|
328
|
+
throw new GatewayError("invalid_request", 400, `invalid BYOK base URL for provider ${row.id} (request {requestId}). Check the shape of what you sent — then resend 🪞`);
|
|
313
329
|
}
|
|
314
330
|
// IP literals are rejected at registration; DNS names are revalidated at dispatch.
|
|
315
331
|
if (isIP(parsedUrl.hostname) !== 0 && !isAddressAllowed(parsedUrl.hostname, ssrfPolicy)) {
|
|
316
332
|
throw new GatewayError(
|
|
317
333
|
"invalid_request",
|
|
318
334
|
400,
|
|
319
|
-
`BYOK provider ${row.id} host is blocked (private/unsafe address)
|
|
335
|
+
`BYOK provider ${row.id} host is blocked (private/unsafe address) (request {requestId}). All roads blocked — time to find a new route 🗺️`,
|
|
320
336
|
);
|
|
321
337
|
}
|
|
322
338
|
const defaultPort = parsedUrl.protocol === "http:" ? 80 : 443;
|
|
@@ -9,8 +9,20 @@ import type { OAuthRefreshService, OAuthTokenRefresher } from "../authentication
|
|
|
9
9
|
import { CredentialResolver, type CredentialAlternative } from "../provider-credential-resolver";
|
|
10
10
|
import { parseProviderId, type CredentialKind } from "../provider-contracts";
|
|
11
11
|
import type { ResolvedCredential } from "../provider-contracts";
|
|
12
|
-
import { record } from "../authentication/oauth-flow-store";
|
|
13
12
|
import { refreshLeadMs } from "./oauth-refresh-lead";
|
|
13
|
+
import { asJsonbRecord } from "../../shared/coerce";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The `auth_state` JSONB column arrives as a POJO from Postgres (drizzle
|
|
17
|
+
* jsonb) but as a JSON STRING from SQLite (the native column stores text).
|
|
18
|
+
* The resolver's `record()` helper rejects strings, so a SQLite read dropped
|
|
19
|
+
* the auth state entirely — Qoder dispatch then saw no userId/machineId and
|
|
20
|
+
* failed with 401 "missing its user identity". Parse the string form once
|
|
21
|
+
* here so every backend hands the resolver the same POJO shape.
|
|
22
|
+
*/
|
|
23
|
+
function jsonbRecord(value: unknown): Record<string, unknown> | undefined {
|
|
24
|
+
return asJsonbRecord(value);
|
|
25
|
+
}
|
|
14
26
|
|
|
15
27
|
// ── Credential cache ─────────────────────────────────────────────────────────
|
|
16
28
|
/**
|
|
@@ -93,6 +105,23 @@ export function invalidateCredentialCache(accountId?: string): void {
|
|
|
93
105
|
}
|
|
94
106
|
}
|
|
95
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Invalidates many accounts' cached credentials in one pass over the cache.
|
|
110
|
+
*
|
|
111
|
+
* The batch-delete route deletes several accounts at once; calling the single
|
|
112
|
+
* variant per id would scan the whole cache once per account. This is one
|
|
113
|
+
* scan with a suffix set, so the cost is one pass regardless of batch size.
|
|
114
|
+
*/
|
|
115
|
+
export function invalidateCredentialCaches(accountIds: readonly string[]): void {
|
|
116
|
+
if (accountIds.length === 0) return;
|
|
117
|
+
const suffixes = new Set(accountIds.map((id) => `:${id}`));
|
|
118
|
+
for (const key of credCache.keys()) {
|
|
119
|
+
// Account ids are globally unique and contain no colon, so the trailing
|
|
120
|
+
// `:${accountId}` segment uniquely identifies the owning account.
|
|
121
|
+
if (suffixes.has(key.slice(key.lastIndexOf(":")))) credCache.delete(key);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
96
125
|
const resolver = new CredentialResolver();
|
|
97
126
|
|
|
98
127
|
/** Stored account fields needed by credential resolution and token refresh. */
|
|
@@ -229,7 +258,7 @@ export async function resolveCredentialForAccount(
|
|
|
229
258
|
throw new GatewayError(
|
|
230
259
|
"authentication_failed",
|
|
231
260
|
401,
|
|
232
|
-
"provider credential cannot be decrypted; re-save the account credential",
|
|
261
|
+
"provider credential cannot be decrypted (request {requestId}); re-save the account credential. Wrong key, right door — check the credential 🔑",
|
|
233
262
|
{
|
|
234
263
|
providerId: row.providerId,
|
|
235
264
|
accountId: row.id,
|
|
@@ -253,7 +282,7 @@ export async function resolveCredentialForAccount(
|
|
|
253
282
|
}
|
|
254
283
|
}
|
|
255
284
|
|
|
256
|
-
const authState =
|
|
285
|
+
const authState = jsonbRecord(row.authState);
|
|
257
286
|
const alternative: CredentialAlternative = {
|
|
258
287
|
provider_id: parseProviderId(row.providerId),
|
|
259
288
|
account_id: row.id,
|
|
@@ -283,7 +312,7 @@ export async function resolveCredentialForAccount(
|
|
|
283
312
|
return value;
|
|
284
313
|
}
|
|
285
314
|
|
|
286
|
-
|
|
315
|
+
async function resolveAccountSecretString(
|
|
287
316
|
db: CartethyiaDatabase,
|
|
288
317
|
providerId: string,
|
|
289
318
|
accountId: string,
|
|
@@ -294,7 +323,7 @@ export async function resolveAccountSecretString(
|
|
|
294
323
|
}
|
|
295
324
|
|
|
296
325
|
/** Decrypts the stored provider tokens after refreshing an expired OAuth access token. */
|
|
297
|
-
|
|
326
|
+
async function resolveAccountCredentialsForExport(
|
|
298
327
|
db: CartethyiaDatabase,
|
|
299
328
|
providerId: string,
|
|
300
329
|
accountId: string,
|
|
@@ -319,7 +348,7 @@ export async function resolveAccountCredentialsForExport(
|
|
|
319
348
|
await oauth.refreshService.ensureFreshAccessToken(accountId, refresher);
|
|
320
349
|
account = await loadAccountWithFreshness(db, accountId);
|
|
321
350
|
if (!account) {
|
|
322
|
-
throw new GatewayError("admission_unavailable", 503, "This provider account no longer exists, so its credential cannot be read. Re-select the account, or point the route at a live one.", {
|
|
351
|
+
throw new GatewayError("admission_unavailable", 503, "This provider account no longer exists, so its credential cannot be read (request {requestId}). Re-select the account, or point the route at a live one. The account went cold — reconnect or resend 🔌", {
|
|
323
352
|
provider_id: providerId,
|
|
324
353
|
account_id: accountId,
|
|
325
354
|
});
|
|
@@ -17,7 +17,7 @@ export function createUpstreamDeadlineLifecycle(
|
|
|
17
17
|
new GatewayError(
|
|
18
18
|
"deadline_exceeded",
|
|
19
19
|
504,
|
|
20
|
-
"upstream request deadline exceeded",
|
|
20
|
+
"upstream request deadline exceeded (request {requestId}). Some things just take longer 🐢",
|
|
21
21
|
{},
|
|
22
22
|
"upstream",
|
|
23
23
|
),
|
|
@@ -52,11 +52,11 @@ export function abortGatewayError(
|
|
|
52
52
|
const reason = lifecycle.signal.reason ?? requestSignal?.reason;
|
|
53
53
|
if (reason instanceof GatewayError) return reason;
|
|
54
54
|
if (reason instanceof DOMException && reason.name === "TimeoutError")
|
|
55
|
-
return new GatewayError("deadline_exceeded", 504, "request deadline exceeded", {}, "cartethyia");
|
|
55
|
+
return new GatewayError("deadline_exceeded", 504, "request deadline exceeded (request {requestId}). Some things just take longer 🐢", {}, "cartethyia");
|
|
56
56
|
const abortLike =
|
|
57
57
|
(error instanceof DOMException && error.name === "AbortError") ||
|
|
58
58
|
(error instanceof Error && error.name === "AbortError");
|
|
59
59
|
if (lifecycle.signal.aborted || requestSignal?.aborted || abortLike)
|
|
60
|
-
return new GatewayError("transport_closed", 499, "request was cancelled");
|
|
60
|
+
return new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
|
|
61
61
|
return undefined;
|
|
62
62
|
}
|
|
@@ -42,18 +42,69 @@ export function readCredentialSecret(credential: ResolvedCredential, errorMessag
|
|
|
42
42
|
throw new GatewayError(
|
|
43
43
|
"authentication_failed",
|
|
44
44
|
401,
|
|
45
|
-
errorMessage ?? "Provider credential secret is missing or empty",
|
|
45
|
+
`${errorMessage ?? "Provider credential secret is missing or empty"} (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
48
|
return raw;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Asserts a credential is of the required kind, throwing a typed rejection
|
|
53
|
+
* otherwise. Providers disagree on the kind they accept (`oauth` vs
|
|
54
|
+
* `api_key` vs `scoped_access_token`), and each hand-rolled its own check +
|
|
55
|
+
* message. This centralizes the common pattern so a mis-bound credential reads
|
|
56
|
+
* the same everywhere, preserving the existing `invalid_request`/400 wire
|
|
57
|
+
* contract.
|
|
58
|
+
*/
|
|
59
|
+
export function assertCredentialKind(
|
|
60
|
+
credential: ResolvedCredential,
|
|
61
|
+
expected: CredentialKind | readonly CredentialKind[],
|
|
62
|
+
providerLabel: string,
|
|
63
|
+
): void {
|
|
64
|
+
const expectedKinds = Array.isArray(expected) ? expected : [expected];
|
|
65
|
+
if (expectedKinds.includes(credential.credential_kind)) return;
|
|
66
|
+
throw new GatewayError(
|
|
67
|
+
"invalid_request",
|
|
68
|
+
400,
|
|
69
|
+
`${providerLabel}: ${expectedKinds.join(" or ")} credential required (got credential_kind=${credential.credential_kind}) (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
70
|
+
{ expected_credential_kind: expectedKinds },
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Asserts a candidate's wire family is one of the ones the provider serves,
|
|
76
|
+
* throwing `capability_unsupported` otherwise. Every provider hand-rolled this
|
|
77
|
+
* guard with the same shape; this centralizes the pattern so a mis-routed
|
|
78
|
+
* candidate reads the same everywhere.
|
|
79
|
+
*/
|
|
80
|
+
export function assertWireFamily(
|
|
81
|
+
candidate: ProviderDispatchTarget,
|
|
82
|
+
allowed: WireFamily | readonly WireFamily[],
|
|
83
|
+
providerLabel: string,
|
|
84
|
+
): void {
|
|
85
|
+
const allowedFamilies = Array.isArray(allowed) ? allowed : [allowed];
|
|
86
|
+
if (allowedFamilies.includes(candidate.wire_family)) return;
|
|
87
|
+
throw new GatewayError(
|
|
88
|
+
"capability_unsupported",
|
|
89
|
+
400,
|
|
90
|
+
`${providerLabel} supports ${allowedFamilies.join("/")} only, got ${candidate.wire_family} (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
|
|
91
|
+
{ expected_wire_families: allowedFamilies, got_wire_family: candidate.wire_family },
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Minimal fetch surface accepted by provider adapters, collectors, and
|
|
97
|
+
* discovery paths. One canonical shape shared across modules that plug a
|
|
98
|
+
* transport into a provider boundary.
|
|
99
|
+
*/
|
|
100
|
+
export type OutboundFetch = (
|
|
53
101
|
input: RequestInfo | URL,
|
|
54
102
|
init?: RequestInit,
|
|
55
103
|
) => Promise<Response>;
|
|
56
104
|
|
|
105
|
+
/** Fetch capability that validates DNS answers and redirects before each request. */
|
|
106
|
+
export type ValidatedOutboundFetch = OutboundFetch;
|
|
107
|
+
|
|
57
108
|
/** Provider WebSocket session over validated WSS egress. */
|
|
58
109
|
export interface ProviderWebSocketSession {
|
|
59
110
|
send(message: string): void;
|
|
@@ -217,6 +268,8 @@ export interface ProviderDispatchTarget {
|
|
|
217
268
|
readonly endpoint_path: string;
|
|
218
269
|
readonly output_limit?: number | null;
|
|
219
270
|
readonly capabilities: Readonly<Record<string, boolean>>;
|
|
271
|
+
/** Explicit wire dialect (from `models.dialect`), when set. Absent = resolver fallback. */
|
|
272
|
+
readonly dialect?: string;
|
|
220
273
|
}
|
|
221
274
|
|
|
222
275
|
/** Provider adapter boundary: canonical request/events in, no public HTTP routes. */
|
|
@@ -228,11 +281,13 @@ export interface ProviderAdapter {
|
|
|
228
281
|
context: ProviderDispatchContext,
|
|
229
282
|
): AsyncIterable<CanonicalEvent>;
|
|
230
283
|
/**
|
|
231
|
-
* Optional native-protocol capability
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
284
|
+
* Optional native-protocol capability for the public `POST /v1/systemone`
|
|
285
|
+
* route (the dispatch leg of the System One service, alongside the probing
|
|
286
|
+
* leg served by the discovery/probing-service). A provider whose catalog
|
|
287
|
+
* serves a non-`llm` service kind (System One) implements the matching
|
|
288
|
+
* method; the native route handler looks it up and dispatches the caller's
|
|
289
|
+
* opaque body through it. The body is forwarded untouched — the method only
|
|
290
|
+
* adds the provider's auth/identity headers and posts it to `endpoint_path`.
|
|
236
291
|
*
|
|
237
292
|
* An adapter without this method is simply not eligible for the route, which
|
|
238
293
|
* is why it is optional rather than a required no-op.
|
|
@@ -352,7 +407,7 @@ export interface WebSearchOutcome {
|
|
|
352
407
|
function normalizeProviderSlug(id: string): string {
|
|
353
408
|
const trimmed = id.trim().toLowerCase();
|
|
354
409
|
if (!/^[a-z][a-z0-9-]{0,63}$/.test(trimmed)) {
|
|
355
|
-
throw new GatewayError("invalid_request", 400, "Provider ID must be a lowercase slug");
|
|
410
|
+
throw new GatewayError("invalid_request", 400, "Provider ID must be a lowercase slug (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
356
411
|
}
|
|
357
412
|
return trimmed;
|
|
358
413
|
}
|
|
@@ -379,7 +434,7 @@ export function parseCustomProviderId(value: string): CustomProviderId {
|
|
|
379
434
|
throw new GatewayError(
|
|
380
435
|
"slug_reserved",
|
|
381
436
|
409,
|
|
382
|
-
"The provider ID is reserved for a builtin provider",
|
|
437
|
+
"The provider ID is reserved for a builtin provider (request {requestId}). This name's taken — pick another alias ✨",
|
|
383
438
|
{ provider_id: normalized },
|
|
384
439
|
);
|
|
385
440
|
}
|
|
@@ -68,7 +68,7 @@ export class CredentialResolver {
|
|
|
68
68
|
throw new GatewayError(
|
|
69
69
|
"invalid_request",
|
|
70
70
|
401,
|
|
71
|
-
"No usable upstream credential is configured for the selected provider",
|
|
71
|
+
"No usable upstream credential is configured for the selected provider (request {requestId}). Wrong key, right door — check the credential 🔑",
|
|
72
72
|
{ provider_id: providerId },
|
|
73
73
|
);
|
|
74
74
|
}
|
|
@@ -115,10 +115,12 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
|
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
117
|
// Kiro frames its own conversation ledger over an AWS EventStream wire and
|
|
118
|
-
// has no chat-shaped endpoint to route through.
|
|
118
|
+
// has no chat-shaped endpoint to route through. Generation is the Kiro
|
|
119
|
+
// runtime service (`https://runtime.{region}.kiro.dev`), not the legacy
|
|
120
|
+
// `q.{region}.amazonaws.com` AWS surface.
|
|
119
121
|
id: "kiro",
|
|
120
122
|
displayName: "Kiro",
|
|
121
|
-
baseUrl: "https://
|
|
123
|
+
baseUrl: "https://runtime.us-east-1.kiro.dev",
|
|
122
124
|
bespokeWire: true,
|
|
123
125
|
hasAdapterUserAgent: true,
|
|
124
126
|
defaultSkipProxy: true,
|
|
@@ -276,6 +278,19 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
|
|
|
276
278
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
|
277
279
|
credentialUrl: "https://chat.z.ai",
|
|
278
280
|
},
|
|
281
|
+
{
|
|
282
|
+
// The ZCode Start Plan is the free plan minted by the referral flow. The
|
|
283
|
+
// endpoint refuses bare Anthropic traffic ("3012 request has been blocked
|
|
284
|
+
// due to unusual activity"); the integration adds the ZCode identity
|
|
285
|
+
// headers and the system/date/cache-control dress in-process.
|
|
286
|
+
id: "zcode-start",
|
|
287
|
+
displayName: "ZCode Start",
|
|
288
|
+
baseUrl: "https://zcode.z.ai/api/v1/zcode-plan/anthropic",
|
|
289
|
+
wireFamilyDefault: "messages",
|
|
290
|
+
hasAdapterUserAgent: true,
|
|
291
|
+
credentialUrl: "https://zcode.z.ai",
|
|
292
|
+
credentialHint: "Use the ZCode login JWT, not a Z.AI access token or Coding Plan API key.",
|
|
293
|
+
},
|
|
279
294
|
{
|
|
280
295
|
// Factory Droid's LLM proxy. The base URL is the global residency host; an
|
|
281
296
|
// EU account resolves to `api.eu.factory.ai` from the region recorded in
|