cartethyia 2.7.2 → 2.7.4
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/dist/dashboard/assets/{BannedPage-XaUmiBrC.js → BannedPage-ChG5svFb.js} +1 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-CXGS_Ct-.js → CombosPage-DCgm5g6g.js} +1 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-BKtAPYVF.js → HealthEventsModal-DzSWzE_j.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-BdmRLnq2.js → LoginPage-U6AKaJcF.js} +1 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BH8Tehdi.js → ModelPicker-BMS2t8BA.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +1 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js +9 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +1 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +1 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-Dqf4zpS-.js → SortableList-ClzpwyET.js} +1 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Byj2lAEm.js → StudioPage-Cq5tW7hW.js} +3 -3
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +56 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +1 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +2 -0
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/arrow-up-59ms1iiT.js +1 -0
- package/dist/dashboard/assets/arrow-up-59ms1iiT.js.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +15 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DC2L43Mj.js → badge-D9CS6dZk.js} +1 -1
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BBVcb0J3.js → banner-DympBNq4.js} +1 -1
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/{card-Cca1XC6G.js → card-ibRWU_uT.js} +1 -1
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +1 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DUzEbynq.js → clipboard-button-DJm-9pz7.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-DFvWhwTa.js → dialog-8fiE6tec.js} +1 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js +1 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/{entry-Bqy8RFvM.js → entry-BSAsYsmV.js} +1 -1
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +4 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +1 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/{concurrency-BDF0ok7y.js → fingerprint-pattern-BWuHtbW4.js} +1 -1
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-CWWZ-uCZ.js → formatters-D-MTJxMr.js} +1 -1
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-C9e8Ts7e.js → github-badge-CPK47xQx.js} +1 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/{index-DFVFrnWW.js → index-DBNPOfhV.js} +2 -2
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BFUcvllj.js → inline-BT0zntEr.js} +1 -1
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/{input-C_ILAvBb.js → input-Da2ZViWg.js} +1 -1
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/{layout-CBIyT6Rt.js → layout-BXNeLw6M.js} +1 -1
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-ebJ_-Nsb.js → list-row-BycOrrIV.js} +1 -1
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +1 -0
- package/dist/dashboard/assets/live-DCQNZpF7.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-sMD965oO.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +1 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BO9dKaFr.js → numbers-CEagj6ZZ.js} +2 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CfL_Zdoh.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +1 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +1 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/{select-DBNVLdUu.js → select-BIYIBkMU.js} +2 -2
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-2-Ds9DQp0t.js +1 -0
- package/dist/dashboard/assets/settings-2-Ds9DQp0t.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +1 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/{stack-EMrOMt4I.js → stack-DfgnFDWY.js} +1 -1
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/{state-CWoo7ty5.js → state-CkfdYWel.js} +1 -1
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/{switch-CCjT6IkF.js → switch-SYm237HG.js} +1 -1
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-DjkLHUcE.js → useMutation-BQqCRNIi.js} +1 -1
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/dist/dashboard/index.html +3 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +118 -115
- package/migrations/full-postgres/0026_add_qoder_native_identity.sql +20 -0
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +21 -0
- package/migrations/full-postgres/0028_add_family_template_key_telemetry.sql +40 -0
- package/migrations/full-postgres/0029_null_share_child_policy_columns.sql +38 -0
- package/migrations/full-postgres/0030_add_api_key_max_children.sql +10 -0
- package/migrations/full-postgres/0031_tenant_system_prompts.sql +16 -0
- 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/lite-sqlite3/0026_add_qoder_native_identity.sql +15 -0
- package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +21 -0
- package/migrations/lite-sqlite3/0028_add_family_template_key_telemetry.sql +40 -0
- package/migrations/lite-sqlite3/0029_null_share_child_policy_columns.sql +38 -0
- package/migrations/lite-sqlite3/0030_add_api_key_max_children.sql +10 -0
- package/migrations/lite-sqlite3/0031_tenant_system_prompts.sql +10 -0
- 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/package.json +2 -1
- package/src/boot/app.ts +9 -8
- package/src/config/network.ts +7 -3
- package/src/console/api-keys/bucket-spend.ts +7 -7
- package/src/console/api-keys/contracts.ts +475 -410
- package/src/console/api-keys/routes.ts +211 -23
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/account-reset.ts +4 -4
- package/src/console/auth/session.ts +3 -3
- package/src/console/backup/contracts.ts +2 -0
- package/src/console/backup/export-endpoint.ts +14 -17
- package/src/console/backup/routes.ts +43 -11
- package/src/console/backup/service.ts +11 -6
- package/src/console/backup/store.ts +384 -88
- package/src/console/cli-tools/routes.ts +3 -3
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +4 -4
- package/src/console/dashboard-assets.ts +16 -1
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +4 -2
- package/src/console/observability/contracts.ts +4 -0
- package/src/console/observability/live.ts +3 -3
- package/src/console/observability/logs.ts +3 -3
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/store.ts +16 -16
- package/src/console/providers/catalog/contracts.ts +43 -22
- package/src/console/providers/catalog/provider-operations.ts +17 -2
- package/src/console/providers/catalog/routes.ts +3 -3
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1523 -1292
- package/src/console/providers/detail/routes.ts +3 -3
- package/src/console/providers/oauth/routes.ts +3 -3
- package/src/console/quota/account-quota-tenant.ts +34 -2
- package/src/console/quota/account-quota.ts +4 -1
- package/src/console/quota/account-test-egress.ts +4 -4
- package/src/console/quota/native-identity.ts +189 -0
- package/src/console/remote-support/contracts.ts +12 -6
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +11 -8
- 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 +9 -9
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/routes.ts +3 -3
- package/src/console/share/share-router.ts +51 -18
- package/src/console/share/share-stats.ts +158 -49
- package/src/console/share/share-usage.ts +24 -7
- package/src/console/shared/read-through-cache.ts +38 -2
- package/src/console/system-prompt/contracts.ts +102 -0
- package/src/console/system-prompt/routes.ts +165 -0
- package/src/console/updates/routes.ts +7 -4
- package/src/main.ts +11 -0
- package/src/network/outbound-fetch.ts +75 -4
- package/src/network/pool/resolver.ts +39 -39
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +21 -1
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/ssrf.ts +4 -4
- package/src/observability/event-query.ts +437 -22
- package/src/observability/telemetry-status.ts +1 -1
- package/src/persistence/db-pg.ts +42 -0
- package/src/persistence/db-sqlite.ts +7 -2
- package/src/persistence/readiness.ts +18 -6
- package/src/persistence/schema.ts +162 -1
- package/src/persistence/stores/api-key-store.ts +849 -581
- package/src/persistence/stores/share-store.ts +65 -13
- package/src/persistence/stores/telemetry-store.ts +58 -2
- package/src/persistence/system-prompt-store.ts +104 -0
- package/src/protocol/codecs.ts +6 -6
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-generation.ts +2 -2
- package/src/protocol/reasoning.ts +65 -1
- package/src/protocol/request/chat.ts +1 -0
- package/src/protocol/response/chat.ts +2 -2
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +5 -5
- package/src/protocol/response/responses.ts +2 -2
- package/src/protocol/sse-decoder.ts +14 -3
- package/src/protocol/translation/capabilities.ts +2 -2
- 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 +2 -2
- package/src/providers/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/configured-provider.ts +1 -1
- package/src/providers/discovery/probe-phases.ts +2 -2
- package/src/providers/discovery/probing-service.ts +12 -12
- package/src/providers/integrations/agentrouter/agentrouter.ts +2 -3
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- package/src/providers/integrations/antigravity/antigravity.ts +33 -47
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +30 -2
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +22 -1
- package/src/providers/integrations/buddy/buddy-media.ts +21 -17
- package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -2
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- 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/cline/cline.ts +8 -1
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +4 -4
- package/src/providers/integrations/commandcode/quota.ts +5 -1
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -11
- package/src/providers/integrations/cursor/cursor.ts +15 -15
- package/src/providers/integrations/factory/factory.ts +4 -4
- package/src/providers/integrations/gemini.ts +11 -11
- package/src/providers/integrations/github/github.ts +26 -7
- package/src/providers/integrations/grok/grok.ts +2 -6
- package/src/providers/integrations/hermes.ts +5 -1
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo.ts +4 -0
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- package/src/providers/integrations/kiro/kiro.ts +10 -10
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/openrouter/provider.ts +4 -0
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +47 -9
- package/src/providers/integrations/qoder/qoder-checkin.ts +242 -51
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +157 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +12 -3
- package/src/providers/integrations/qoder/qoder-realm.ts +15 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +287 -0
- package/src/providers/integrations/qoder/qoder-virtual.ts +2 -2
- package/src/providers/integrations/qoder/qoder.ts +192 -24
- package/src/providers/integrations/tokenharbor.ts +2 -2
- package/src/providers/integrations/trae/trae.ts +6 -9
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/operations/account-error-classification.ts +6 -8
- package/src/providers/operations/account-health-service.ts +14 -16
- package/src/providers/operations/client-versions.ts +37 -22
- package/src/providers/operations/provider-catalog-service.ts +4 -4
- package/src/providers/operations/provider-credential-service.ts +16 -4
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +55 -3
- package/src/providers/provider-credential-resolver.ts +1 -1
- package/src/providers/provider-registry.ts +4 -4
- package/src/providers/quota/quota-contracts.ts +24 -10
- package/src/providers/quota/refresh.ts +2 -2
- package/src/providers/search/search-provider.ts +5 -5
- package/src/security/abuse.ts +4 -4
- package/src/security/access-control.ts +19 -0
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/api-key-auth.ts +18 -5
- package/src/shared/coerce.ts +19 -0
- package/src/transport/dispatch/attempt-finalize.ts +4 -3
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/empty-stream.ts +6 -6
- package/src/transport/dispatch/generation.ts +9 -9
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/proxy-request.ts +38 -9
- package/src/transport/dispatch/responses-compact.ts +11 -11
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +9 -8
- package/src/transport/dispatch/streaming/stream.ts +16 -1
- package/src/transport/dispatch/systemone.ts +8 -8
- package/src/transport/dispatch/token-count.ts +9 -9
- package/src/transport/dispatch/upstream.ts +10 -0
- package/src/transport/dispatch/websearch.ts +6 -6
- package/src/transport/drain-endpoint.ts +2 -2
- package/src/transport/failure-policy.ts +13 -6
- package/src/transport/guards/auth-guard.ts +6 -5
- package/src/transport/guards/body-policy.ts +4 -4
- 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 +1 -1
- package/src/transport/guards/pipeline.ts +6 -6
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +3 -3
- package/src/transport/request/preparer.ts +37 -21
- package/src/transport/request/state.ts +4 -7
- package/src/transport/request/system-prompt-injection.ts +89 -0
- package/src/transport/request/tenant-preferences.ts +43 -0
- package/src/transport/routing/admission.ts +13 -3
- package/src/transport/routing/route-catalog.ts +104 -2
- package/src/transport/routing/route-model.ts +60 -7
- package/src/transport/routing/router.ts +306 -256
- package/src/transport/shutdown-notice.ts +19 -7
- package/src/transport/surface/completion.ts +1 -1
- package/src/transport/surface/messages/encode.ts +1 -1
- package/src/workers/daily-checkin.ts +2 -0
- package/dist/dashboard/assets/BannedPage-XaUmiBrC.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-CqD7QiLM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-0XUo4UMn.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CXGS_Ct-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-DGnGNYnM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-DAG5J9EY.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BKtAPYVF.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BdmRLnq2.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BH8Tehdi.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CqMxibJB.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CqMxibJB.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-CqMh2NRX.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-B9uWu4Nz.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js +0 -9
- package/dist/dashboard/assets/ProxyPage-BoWOhpDO.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js +0 -1
- package/dist/dashboard/assets/QuotaPage-7P2n--Fv.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CxZ1fTkN.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-Co12Zzaw.js +0 -1
- package/dist/dashboard/assets/SetupPage-Co12Zzaw.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-Dqf4zpS-.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Byj2lAEm.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-CTkLAuRc.js +0 -56
- package/dist/dashboard/assets/UsagePage-CTkLAuRc.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-CUi6UXsR.js +0 -1
- package/dist/dashboard/assets/api-keys-CUi6UXsR.js.gz +0 -0
- package/dist/dashboard/assets/app-KvsUJUMF.css +0 -2
- package/dist/dashboard/assets/app-KvsUJUMF.css.gz +0 -0
- package/dist/dashboard/assets/assert-67xXH5rf.js +0 -1
- package/dist/dashboard/assets/assert-67xXH5rf.js.gz +0 -0
- package/dist/dashboard/assets/badge-DC2L43Mj.js.gz +0 -0
- package/dist/dashboard/assets/banner-BBVcb0J3.js.gz +0 -0
- package/dist/dashboard/assets/card-Cca1XC6G.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js +0 -1
- package/dist/dashboard/assets/cli-tools-KvsWa1Jl.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DUzEbynq.js.gz +0 -0
- package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
- package/dist/dashboard/assets/dialog-DFvWhwTa.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CFM2sMX1.js +0 -1
- package/dist/dashboard/assets/drawer-CFM2sMX1.js.gz +0 -0
- package/dist/dashboard/assets/entry-Bqy8RFvM.js.gz +0 -0
- package/dist/dashboard/assets/entry-ClCMCWWQ.js +0 -4
- package/dist/dashboard/assets/entry-ClCMCWWQ.js.gz +0 -0
- package/dist/dashboard/assets/entry-_xHo5x6r.js +0 -1
- package/dist/dashboard/assets/entry-_xHo5x6r.js.gz +0 -0
- package/dist/dashboard/assets/formatters-CWWZ-uCZ.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-C9e8Ts7e.js.gz +0 -0
- package/dist/dashboard/assets/index-DFVFrnWW.js.gz +0 -0
- package/dist/dashboard/assets/inline-BFUcvllj.js.gz +0 -0
- package/dist/dashboard/assets/input-C_ILAvBb.js.gz +0 -0
- package/dist/dashboard/assets/layout-CBIyT6Rt.js.gz +0 -0
- package/dist/dashboard/assets/list-row-ebJ_-Nsb.js.gz +0 -0
- package/dist/dashboard/assets/live-D0mm--ME.js +0 -1
- package/dist/dashboard/assets/live-D0mm--ME.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-sMD965oO.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BO9dKaFr.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CfL_Zdoh.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-BnIpi1IJ.js +0 -1
- package/dist/dashboard/assets/routing-BnIpi1IJ.js.gz +0 -0
- package/dist/dashboard/assets/select-DBNVLdUu.js.gz +0 -0
- package/dist/dashboard/assets/settings-BAFhDgX8.js +0 -1
- package/dist/dashboard/assets/settings-BAFhDgX8.js.gz +0 -0
- package/dist/dashboard/assets/stack-EMrOMt4I.js.gz +0 -0
- package/dist/dashboard/assets/state-CWoo7ty5.js.gz +0 -0
- package/dist/dashboard/assets/switch-CCjT6IkF.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-DjkLHUcE.js.gz +0 -0
- /package/dist/dashboard/assets/{app-BaWd0puM.js → app-BayTTy01.js} +0 -0
- /package/dist/dashboard/assets/{app-BaWd0puM.js.gz → app-BayTTy01.js.gz} +0 -0
|
@@ -55,7 +55,7 @@ export async function* sendClaudeMessagesRequest(
|
|
|
55
55
|
throw new GatewayError(
|
|
56
56
|
"platform_unavailable",
|
|
57
57
|
502,
|
|
58
|
-
"Claude response has no stream body",
|
|
58
|
+
"Claude response has no stream body (request {requestId}). The upstream came back empty-handed — retry 🌙",
|
|
59
59
|
{},
|
|
60
60
|
"upstream",
|
|
61
61
|
);
|
|
@@ -70,7 +70,7 @@ export async function* sendClaudeMessagesRequest(
|
|
|
70
70
|
throw new GatewayError(
|
|
71
71
|
"platform_unavailable",
|
|
72
72
|
502,
|
|
73
|
-
"Claude response is not valid JSON",
|
|
73
|
+
"Claude response is not valid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙",
|
|
74
74
|
{},
|
|
75
75
|
"upstream",
|
|
76
76
|
);
|
|
@@ -79,7 +79,7 @@ export async function* sendClaudeMessagesRequest(
|
|
|
79
79
|
throw new GatewayError(
|
|
80
80
|
"platform_unavailable",
|
|
81
81
|
502,
|
|
82
|
-
"Claude response must be an object",
|
|
82
|
+
"Claude response must be an object (request {requestId}). The upstream came back empty-handed — retry 🌙",
|
|
83
83
|
{},
|
|
84
84
|
"upstream",
|
|
85
85
|
);
|
|
@@ -25,7 +25,7 @@ export abstract class BaseProviderAdapter implements ProviderAdapter {
|
|
|
25
25
|
context: ProviderDispatchContext,
|
|
26
26
|
): AsyncIterable<CanonicalEvent> {
|
|
27
27
|
if (request.model.includes("?")) {
|
|
28
|
-
throw new GatewayError("invalid_request", 400, "model must not contain query string");
|
|
28
|
+
throw new GatewayError("invalid_request", 400, "model must not contain query string (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
29
29
|
}
|
|
30
30
|
const headers = await this.prepareHeaders(context, request, target);
|
|
31
31
|
const payload = await this.preparePayload(request, target, context);
|
|
@@ -351,7 +351,7 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
|
|
|
351
351
|
throw new GatewayError(
|
|
352
352
|
"platform_unavailable",
|
|
353
353
|
502,
|
|
354
|
-
wireFamily === "responses" ? "Responses returned empty stream" : "Chat returned empty stream"
|
|
354
|
+
`${wireFamily === "responses" ? "Responses returned empty stream" : "Chat returned empty stream"} (request {requestId}). The upstream came back empty-handed — retry 🌙`,
|
|
355
355
|
{},
|
|
356
356
|
"upstream",
|
|
357
357
|
);
|
|
@@ -379,7 +379,7 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
|
|
|
379
379
|
throw new GatewayError(
|
|
380
380
|
"transport_unavailable",
|
|
381
381
|
502,
|
|
382
|
-
`Upstream returned a non-JSON response: ${summary}
|
|
382
|
+
`Upstream returned a non-JSON response: ${summary} (request {requestId}). The connection ghosted us — come back in a moment 👻`.slice(0, 320),
|
|
383
383
|
{ upstreamStatus: res.status, contentType },
|
|
384
384
|
"upstream",
|
|
385
385
|
);
|
|
@@ -28,7 +28,7 @@ import { providerAccounts, providerOauthStates } from "../../persistence/schema"
|
|
|
28
28
|
import { decryptCredentialToString, encryptCredential } from "../../security/crypto";
|
|
29
29
|
import { invalidateCredentialCache } from "../operations/provider-credential-service";
|
|
30
30
|
import { resolveAccountDisabledRecoveryMs } from "../../config/cooldowns";
|
|
31
|
-
import {
|
|
31
|
+
import { asJsonbRecord } from "../../shared/coerce";
|
|
32
32
|
import {
|
|
33
33
|
loadAccountWithFreshness,
|
|
34
34
|
type AccountWithFreshnessRow,
|
|
@@ -224,7 +224,7 @@ async function persistRefreshed(
|
|
|
224
224
|
* never reach into the database.
|
|
225
225
|
*/
|
|
226
226
|
function buildRefreshContext(row: AccountRow, includeAccessToken: boolean): OAuthRefreshContext {
|
|
227
|
-
const authState =
|
|
227
|
+
const authState = asJsonbRecord(row.authState);
|
|
228
228
|
return {
|
|
229
229
|
account_id: row.id,
|
|
230
230
|
...(authState === undefined ? {} : { auth_state: authState }),
|
|
@@ -11,7 +11,7 @@ export function ensurePayloadModel(
|
|
|
11
11
|
): string {
|
|
12
12
|
const value = payload["model"];
|
|
13
13
|
const model = typeof value === "string" && value.trim() ? value.trim() : candidate.model_id.trim();
|
|
14
|
-
if (!model) throw new GatewayError("invalid_request", 400, `${providerName} model is required
|
|
14
|
+
if (!model) throw new GatewayError("invalid_request", 400, `${providerName} model is required (request {requestId}). Check the shape of what you sent — then resend 🪞`);
|
|
15
15
|
payload["model"] = model;
|
|
16
16
|
return model;
|
|
17
17
|
}
|
|
@@ -109,7 +109,7 @@ export function grok407ProbeFailure(args: {
|
|
|
109
109
|
return new GatewayError(
|
|
110
110
|
"quota_exceeded",
|
|
111
111
|
429,
|
|
112
|
-
detail
|
|
112
|
+
`${detail} (request {requestId}). You've maxed this window — pace yourself ⏳`,
|
|
113
113
|
{
|
|
114
114
|
...priorDetails,
|
|
115
115
|
providerCode: "subscription:free-usage-exhausted",
|
|
@@ -289,7 +289,7 @@ export async function resolveProbeTarget(args: {
|
|
|
289
289
|
throw new GatewayError(
|
|
290
290
|
"model_not_found",
|
|
291
291
|
404,
|
|
292
|
-
`No ${requestedServiceKind} model ${modelId} is registered for provider ${providerId}
|
|
292
|
+
`No ${requestedServiceKind} model ${modelId} is registered for provider ${providerId} (request {requestId}). Try another model — this one's off the menu 📚`,
|
|
293
293
|
);
|
|
294
294
|
}
|
|
295
295
|
const ctx = wireContextFrom(providerWireRow);
|
|
@@ -304,7 +304,7 @@ export class ProviderProbingService {
|
|
|
304
304
|
throw new GatewayError(
|
|
305
305
|
"capability_unsupported",
|
|
306
306
|
400,
|
|
307
|
-
`Provider ${providerId} has no web search transport
|
|
307
|
+
`Provider ${providerId} has no web search transport (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
|
|
308
308
|
);
|
|
309
309
|
}
|
|
310
310
|
const searchProbeQuery = request.prompt?.trim() || "Cartethyia gateway probe";
|
|
@@ -325,14 +325,14 @@ export class ProviderProbingService {
|
|
|
325
325
|
snippet: hit.snippet,
|
|
326
326
|
}));
|
|
327
327
|
if (!response.results || response.results.length === 0) {
|
|
328
|
-
dispatchError = new GatewayError("platform_unavailable", 502, "web search returned no results");
|
|
328
|
+
dispatchError = new GatewayError("platform_unavailable", 502, "web search returned no results (request {requestId}). The upstream came back empty-handed — retry 🌙");
|
|
329
329
|
}
|
|
330
330
|
} else if (serviceKind === "systemone") {
|
|
331
331
|
if (typeof adapter.systemone !== "function") {
|
|
332
332
|
throw new GatewayError(
|
|
333
333
|
"capability_unsupported",
|
|
334
334
|
400,
|
|
335
|
-
`Provider ${providerId} has no System One transport
|
|
335
|
+
`Provider ${providerId} has no System One transport (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
|
|
336
336
|
);
|
|
337
337
|
}
|
|
338
338
|
const response = await adapter.systemone(
|
|
@@ -360,14 +360,14 @@ export class ProviderProbingService {
|
|
|
360
360
|
dispatchError = new GatewayError(
|
|
361
361
|
response.status >= 500 ? "platform_unavailable" : "invalid_request",
|
|
362
362
|
response.status,
|
|
363
|
-
`System One upstream error: ${raw.slice(0, 240)}`,
|
|
363
|
+
`System One upstream error (request {requestId}): ${raw.slice(0, 240)}`,
|
|
364
364
|
);
|
|
365
365
|
} else {
|
|
366
366
|
let parsed: unknown;
|
|
367
367
|
try {
|
|
368
368
|
parsed = raw ? JSON.parse(raw) : null;
|
|
369
369
|
} catch {
|
|
370
|
-
dispatchError = new GatewayError("platform_unavailable", 502, "System One returned invalid JSON");
|
|
370
|
+
dispatchError = new GatewayError("platform_unavailable", 502, "System One returned invalid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙");
|
|
371
371
|
}
|
|
372
372
|
if (parsed !== null && parsed !== undefined) {
|
|
373
373
|
const answers =
|
|
@@ -381,7 +381,7 @@ export class ProviderProbingService {
|
|
|
381
381
|
dispatchError = new GatewayError(
|
|
382
382
|
"platform_unavailable",
|
|
383
383
|
502,
|
|
384
|
-
"System One returned no answers",
|
|
384
|
+
"System One returned no answers (request {requestId}). The upstream came back empty-handed — retry 🌙",
|
|
385
385
|
);
|
|
386
386
|
}
|
|
387
387
|
}
|
|
@@ -709,7 +709,7 @@ export class ProviderProbingService {
|
|
|
709
709
|
throw new GatewayError(
|
|
710
710
|
"transport_unavailable",
|
|
711
711
|
502,
|
|
712
|
-
`Model discovery failed for ${providerId}: ${error instanceof Error ? error.message : "unknown error"}
|
|
712
|
+
`Model discovery failed for ${providerId}: ${error instanceof Error ? error.message : "unknown error"} (request {requestId}). The connection ghosted us — come back in a moment 👻`,
|
|
713
713
|
{},
|
|
714
714
|
"upstream",
|
|
715
715
|
);
|
|
@@ -718,7 +718,7 @@ export class ProviderProbingService {
|
|
|
718
718
|
throw new GatewayError(
|
|
719
719
|
"transport_unavailable",
|
|
720
720
|
502,
|
|
721
|
-
`Model discovery failed for ${providerId}: endpoint returned no usable models
|
|
721
|
+
`Model discovery failed for ${providerId}: endpoint returned no usable models (request {requestId}). The upstream came back empty-handed — retry 🌙`,
|
|
722
722
|
{},
|
|
723
723
|
"upstream",
|
|
724
724
|
);
|
|
@@ -764,7 +764,7 @@ export class ProviderProbingService {
|
|
|
764
764
|
throw new GatewayError(
|
|
765
765
|
"transport_unavailable",
|
|
766
766
|
502,
|
|
767
|
-
`Model discovery failed for ${providerId}: ${error instanceof Error ? error.message : "unknown error"}
|
|
767
|
+
`Model discovery failed for ${providerId}: ${error instanceof Error ? error.message : "unknown error"} (request {requestId}). The connection ghosted us — come back in a moment 👻`,
|
|
768
768
|
{},
|
|
769
769
|
"upstream",
|
|
770
770
|
);
|
|
@@ -799,7 +799,7 @@ export class ProviderProbingService {
|
|
|
799
799
|
throw new GatewayError(
|
|
800
800
|
"invalid_request",
|
|
801
801
|
400,
|
|
802
|
-
`Add an account for ${providerId} first — model listing needs a credential
|
|
802
|
+
`Add an account for ${providerId} first — model listing needs a credential (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
803
803
|
{},
|
|
804
804
|
"cartethyia",
|
|
805
805
|
);
|
|
@@ -811,7 +811,7 @@ export class ProviderProbingService {
|
|
|
811
811
|
throw new GatewayError(
|
|
812
812
|
"invalid_request",
|
|
813
813
|
400,
|
|
814
|
-
`Add an account for ${providerId} before fetching its models
|
|
814
|
+
`Add an account for ${providerId} before fetching its models (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
815
815
|
{},
|
|
816
816
|
"cartethyia",
|
|
817
817
|
);
|
|
@@ -821,7 +821,7 @@ export class ProviderProbingService {
|
|
|
821
821
|
throw new GatewayError(
|
|
822
822
|
"capability_unsupported",
|
|
823
823
|
400,
|
|
824
|
-
`${providerId} publishes no model-listing endpoint. Its catalog ships with Cartethyia; new models appear with an update
|
|
824
|
+
`${providerId} publishes no model-listing endpoint (request {requestId}). Its catalog ships with Cartethyia; new models appear with an update. Know your tools — this one just isn't in the box 🧰`,
|
|
825
825
|
{},
|
|
826
826
|
"cartethyia",
|
|
827
827
|
);
|
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
// `x-stainless-*` family, `x-claude-code-session-id`, and `anthropic-version`
|
|
12
12
|
// for operator-supplied headers, so they are layered onto the built headers
|
|
13
13
|
// at this call site instead.
|
|
14
|
-
import { GatewayError } from "../../../protocol/gateway-error";
|
|
15
14
|
import type { CanonicalEvent, CanonicalRequest } from "../../../protocol/canonical-model";
|
|
16
|
-
import { readCredentialSecret, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext } from "../../provider-contracts";
|
|
15
|
+
import { readCredentialSecret, assertWireFamily, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext } from "../../provider-contracts";
|
|
17
16
|
import { providerBaseUrl } from "../../provider-metadata";
|
|
18
17
|
import { buildClaudeMessagesRequest } from "../shared/claude-messages";
|
|
19
18
|
import { sendClaudeMessagesRequest } from "../../../protocol/upstream/send-claude-messages";
|
|
@@ -87,7 +86,7 @@ class AgentRouterAdapter implements ProviderAdapter {
|
|
|
87
86
|
context: ProviderDispatchContext,
|
|
88
87
|
): AsyncIterable<CanonicalEvent> {
|
|
89
88
|
if (candidate.wire_family !== "messages") {
|
|
90
|
-
|
|
89
|
+
assertWireFamily(candidate, "messages", "agentrouter");
|
|
91
90
|
}
|
|
92
91
|
const apiKey = readCredentialSecret(context.credential, "AgentRouter API key is required");
|
|
93
92
|
|
|
@@ -61,7 +61,7 @@ function alibabaSpec(envelope: AlibabaCodingPlanEnvelope): ApiKeyProviderSpec {
|
|
|
61
61
|
function alibabaEnvelope(context: ProviderDispatchContext): AlibabaCodingPlanEnvelope {
|
|
62
62
|
const secret = context.credential.secret;
|
|
63
63
|
if (secret === undefined || secret.length === 0) {
|
|
64
|
-
throw new GatewayError("invalid_request", 400, "alibabacp: credential is missing a secret value");
|
|
64
|
+
throw new GatewayError("invalid_request", 400, "alibabacp: credential is missing a secret value (request {requestId}). Wrong key, right door — check the credential 🔑");
|
|
65
65
|
}
|
|
66
66
|
let envelope: AlibabaCodingPlanEnvelope;
|
|
67
67
|
try {
|
|
@@ -70,7 +70,7 @@ function alibabaEnvelope(context: ProviderDispatchContext): AlibabaCodingPlanEnv
|
|
|
70
70
|
throw new GatewayError(
|
|
71
71
|
"invalid_request",
|
|
72
72
|
400,
|
|
73
|
-
`alibabacp: ${error instanceof Error ? error.message : "stored credential is not an Alibaba envelope"}
|
|
73
|
+
`alibabacp: ${error instanceof Error ? error.message : "stored credential is not an Alibaba envelope"} (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
76
|
return envelope;
|
|
@@ -18,6 +18,7 @@ import { GatewayError } from "../../protocol/gateway-error";
|
|
|
18
18
|
import { buildClaudeMessagesRequest, filterClaudeCustomHeaders } from "./shared/claude-messages";
|
|
19
19
|
import { sendClaudeMessagesRequest } from "../../protocol/upstream/send-claude-messages";
|
|
20
20
|
import { applyParamQuirks } from "../../protocol/translation/quirks";
|
|
21
|
+
import { assertCredentialKind, assertWireFamily } from "../provider-contracts";
|
|
21
22
|
import type { ProviderDispatchTarget, ProviderAdapter, ProviderDispatchContext, ProviderId } from "../provider-contracts";
|
|
22
23
|
import { providerBaseUrl } from "../provider-metadata";
|
|
23
24
|
|
|
@@ -58,18 +59,10 @@ export class AnthropicApiKeyAdapter implements ProviderAdapter {
|
|
|
58
59
|
context: ProviderDispatchContext,
|
|
59
60
|
): AsyncIterable<CanonicalEvent> {
|
|
60
61
|
if (candidate.wire_family !== "messages") {
|
|
61
|
-
|
|
62
|
-
"capability_unsupported",
|
|
63
|
-
400,
|
|
64
|
-
`${this.provider_id} supports messages only`,
|
|
65
|
-
);
|
|
62
|
+
assertWireFamily(candidate, "messages", this.provider_id);
|
|
66
63
|
}
|
|
67
64
|
if (context.credential.credential_kind !== "api_key") {
|
|
68
|
-
|
|
69
|
-
"invalid_request",
|
|
70
|
-
400,
|
|
71
|
-
`${this.provider_id}: x-api-key credential required (got credential_kind=${context.credential.credential_kind})`,
|
|
72
|
-
);
|
|
65
|
+
assertCredentialKind(context.credential, "api_key", this.provider_id);
|
|
73
66
|
}
|
|
74
67
|
if (
|
|
75
68
|
context.credential.secret === undefined ||
|
|
@@ -78,7 +71,7 @@ export class AnthropicApiKeyAdapter implements ProviderAdapter {
|
|
|
78
71
|
throw new GatewayError(
|
|
79
72
|
"invalid_request",
|
|
80
73
|
400,
|
|
81
|
-
`${this.provider_id}: API key is missing a secret value
|
|
74
|
+
`${this.provider_id}: API key is missing a secret value (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
82
75
|
);
|
|
83
76
|
}
|
|
84
77
|
const apiKey = new TextDecoder().decode(context.credential.secret);
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
loadAntigravityProject,
|
|
47
47
|
normalizeAntigravityToolSchemas,
|
|
48
48
|
} from "./antigravity-protocol";
|
|
49
|
+
import { assertCredentialKind, assertWireFamily } from "../../provider-contracts";
|
|
49
50
|
import type { ImageGenerationOutcome, ProviderDispatchTarget, ProviderAdapter, ProviderDispatchContext, WebSearchOutcome, WebSearchResult } from "../../provider-contracts";
|
|
50
51
|
import { providerBaseUrl } from "../../provider-metadata";
|
|
51
52
|
import { abortGatewayError, createUpstreamDeadlineLifecycle } from "../../operations/upstream-deadline";
|
|
@@ -84,34 +85,31 @@ const antigravityModel = (
|
|
|
84
85
|
});
|
|
85
86
|
|
|
86
87
|
export const ANTIGRAVITY_MODELS: readonly ModelDefinition[] = [
|
|
87
|
-
// Claude via Antigravity (Anthropic-branded SKUs, Gemini wire)
|
|
88
|
+
// Claude via Antigravity (Anthropic-branded SKUs, Gemini wire). The 4.6
|
|
89
|
+
// rows are pinned at 64000 because Antigravity was observed rejecting
|
|
90
|
+
// anything higher on `daily-cloudcode-pa`; `out` is published metadata, not
|
|
91
|
+
// a request field (the envelope's `maxOutputTokens` comes from
|
|
92
|
+
// `ANTIGRAVITY_MODEL_WIRE_PROFILES`).
|
|
88
93
|
antigravityModel("claude-sonnet-4-6", 250_000, 64_000, true),
|
|
89
94
|
antigravityModel("claude-opus-4-6", 250_000, 64_000, true),
|
|
90
|
-
// The 5.5 generation. Anthropic's own model pages publish 1M context and
|
|
91
|
-
// 128k output for both, which is the same figure the Claude integration
|
|
92
|
-
// carries; Antigravity has no base-catalog rows of its own, so the numbers
|
|
93
|
-
// are stated here rather than resolved from a source that does not exist.
|
|
94
|
-
//
|
|
95
|
-
// `out` is published metadata, not a request field: the `maxOutputTokens` the
|
|
96
|
-
// envelope carries comes from `ANTIGRAVITY_MODEL_WIRE_PROFILES`, and an id
|
|
97
|
-
// absent from that table passes the caller's own value through. The 4.6 rows
|
|
98
|
-
// are pinned at 64000 because Antigravity was observed rejecting anything
|
|
99
|
-
// higher on `daily-cloudcode-pa`. The 5.5 cap is UNVERIFIED — no 5.5 request
|
|
100
|
-
// has been captured against that host — so if it turns out to carry the same
|
|
101
|
-
// 64000 ceiling, it needs its own profile entry; a catalog row cannot express
|
|
102
|
-
// it.
|
|
103
95
|
antigravityModel("claude-sonnet-5-5", 1_000_000, 128_000, true),
|
|
104
96
|
antigravityModel("claude-opus-5-5", 1_000_000, 128_000, true),
|
|
105
|
-
// Gemini
|
|
106
|
-
antigravityModel("gemini-2.5-flash",
|
|
107
|
-
antigravityModel("gemini-
|
|
97
|
+
// Gemini text SKUs, as the Antigravity roster publishes them.
|
|
98
|
+
antigravityModel("gemini-2.5-flash", 1_000_000, 66_000, true),
|
|
99
|
+
antigravityModel("gemini-2.5-flash-thinking", 1_000_000, 66_000, true),
|
|
100
|
+
antigravityModel("gemini-3-flash", 1_000_000, 66_000, true),
|
|
101
|
+
antigravityModel("gemini-3.1-flash-lite", 1_000_000, 66_000, true),
|
|
102
|
+
antigravityModel("gemini-2.5-flash-lite", 1_000_000, 66_000, true),
|
|
103
|
+
antigravityModel("gemini-3.5-flash-lite", 1_000_000, 66_000, true),
|
|
104
|
+
antigravityModel("gemini-3.8-flash", 1_000_000, 66_000, true),
|
|
105
|
+
antigravityModel("gemini-4-argon", 1_000_000, 65_536, true),
|
|
106
|
+
antigravityModel("gemini-4-carbon", 1_000_000, 65_536, true),
|
|
107
|
+
// Tab (laptop-class) preview SKU.
|
|
108
|
+
antigravityModel("tab_flash_lite_preview", 16_000, 4_000, true),
|
|
109
|
+
// Gemini image generation. `gemini-3-pro-image` uses the same catalog
|
|
110
|
+
// limits/capabilities as the existing `gemini-3.1-flash-image` row.
|
|
108
111
|
antigravityModel("gemini-3.1-flash-image", 200_000, 64_000, false, true),
|
|
109
|
-
antigravityModel("gemini-3
|
|
110
|
-
antigravityModel("gemini-3.6-flash", 1_048_576, 65_536, true),
|
|
111
|
-
antigravityModel("gemini-3.7-flash", 1_048_576, 65_536, true),
|
|
112
|
-
antigravityModel("gemini-3.8-flash", 1_048_576, 65_536, true),
|
|
113
|
-
// GPT-OSS
|
|
114
|
-
antigravityModel("gpt-oss-120b", 131_072, 32_768, true),
|
|
112
|
+
antigravityModel("gemini-3-pro-image", 200_000, 64_000, false, true),
|
|
115
113
|
];
|
|
116
114
|
|
|
117
115
|
function normalizeAntigravityGrounding(data: unknown, maxResults: number): readonly WebSearchResult[] {
|
|
@@ -191,18 +189,10 @@ class AntigravityAdapter implements ProviderAdapter {
|
|
|
191
189
|
candidate.wire_family !== "responses" &&
|
|
192
190
|
candidate.wire_family !== "messages"
|
|
193
191
|
) {
|
|
194
|
-
|
|
195
|
-
"capability_unsupported",
|
|
196
|
-
400,
|
|
197
|
-
`antigravity supports chat/responses/messages, got ${candidate.wire_family}`,
|
|
198
|
-
);
|
|
192
|
+
assertWireFamily(candidate, ["chat", "responses", "messages"], "antigravity");
|
|
199
193
|
}
|
|
200
194
|
if (context.credential.credential_kind !== "oauth") {
|
|
201
|
-
|
|
202
|
-
"invalid_request",
|
|
203
|
-
400,
|
|
204
|
-
`antigravity: Google OAuth credential required (got credential_kind=${context.credential.credential_kind})`,
|
|
205
|
-
);
|
|
195
|
+
assertCredentialKind(context.credential, "oauth", "antigravity");
|
|
206
196
|
}
|
|
207
197
|
const accessToken = context.credential.secret
|
|
208
198
|
? new TextDecoder().decode(context.credential.secret).trim()
|
|
@@ -211,7 +201,7 @@ class AntigravityAdapter implements ProviderAdapter {
|
|
|
211
201
|
throw new GatewayError(
|
|
212
202
|
"authentication_failed",
|
|
213
203
|
401,
|
|
214
|
-
"antigravity: OAuth access token is required",
|
|
204
|
+
"antigravity: OAuth access token is required (request {requestId}). Wrong key, right door — check the credential 🔑",
|
|
215
205
|
);
|
|
216
206
|
}
|
|
217
207
|
|
|
@@ -363,7 +353,7 @@ class AntigravityAdapter implements ProviderAdapter {
|
|
|
363
353
|
throw new GatewayError(
|
|
364
354
|
"platform_unavailable",
|
|
365
355
|
502,
|
|
366
|
-
"antigravity returned empty stream",
|
|
356
|
+
"antigravity returned empty stream (request {requestId}). The upstream came back empty-handed — retry 🌙",
|
|
367
357
|
{},
|
|
368
358
|
"upstream",
|
|
369
359
|
);
|
|
@@ -511,7 +501,7 @@ class AntigravityAdapter implements ProviderAdapter {
|
|
|
511
501
|
context: ProviderDispatchContext,
|
|
512
502
|
): Promise<WebSearchOutcome> {
|
|
513
503
|
const query = typeof body.query === "string" ? body.query.trim() : "";
|
|
514
|
-
if (!query) throw new GatewayError("invalid_request", 400, "search query is required");
|
|
504
|
+
if (!query) throw new GatewayError("invalid_request", 400, "search query is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
515
505
|
const rawMax = body.max_results;
|
|
516
506
|
const maxResults =
|
|
517
507
|
typeof rawMax === "number" && Number.isFinite(rawMax)
|
|
@@ -534,7 +524,7 @@ class AntigravityAdapter implements ProviderAdapter {
|
|
|
534
524
|
}
|
|
535
525
|
const results = normalizeAntigravityGrounding(grounded, maxResults);
|
|
536
526
|
if (results.length === 0)
|
|
537
|
-
throw new GatewayError("platform_unavailable", 502, "Antigravity search returned no grounded results");
|
|
527
|
+
throw new GatewayError("platform_unavailable", 502, "Antigravity search returned no grounded results (request {requestId}). The upstream came back empty-handed — retry 🌙");
|
|
538
528
|
return { results, total_results: results.length };
|
|
539
529
|
}
|
|
540
530
|
|
|
@@ -544,21 +534,17 @@ class AntigravityAdapter implements ProviderAdapter {
|
|
|
544
534
|
context: ProviderDispatchContext,
|
|
545
535
|
): Promise<ImageGenerationOutcome> {
|
|
546
536
|
if (context.credential.credential_kind !== "oauth") {
|
|
547
|
-
|
|
548
|
-
"invalid_request",
|
|
549
|
-
400,
|
|
550
|
-
`antigravity: Google OAuth credential required (got credential_kind=${context.credential.credential_kind})`,
|
|
551
|
-
);
|
|
537
|
+
assertCredentialKind(context.credential, "oauth", "antigravity");
|
|
552
538
|
}
|
|
553
539
|
const accessToken = context.credential.secret
|
|
554
540
|
? new TextDecoder().decode(context.credential.secret).trim()
|
|
555
541
|
: "";
|
|
556
542
|
if (!accessToken) {
|
|
557
|
-
throw new GatewayError("authentication_failed", 401, "antigravity: OAuth access token is required");
|
|
543
|
+
throw new GatewayError("authentication_failed", 401, "antigravity: OAuth access token is required (request {requestId}). Wrong key, right door — check the credential 🔑");
|
|
558
544
|
}
|
|
559
545
|
const prompt = imagePrompt(body);
|
|
560
546
|
const model = candidate.model_id.length > 0 ? candidate.model_id : typeof body.model === "string" ? body.model : "";
|
|
561
|
-
if (model.length === 0) throw new GatewayError("invalid_request", 400, "model is required");
|
|
547
|
+
if (model.length === 0) throw new GatewayError("invalid_request", 400, "model is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
562
548
|
const outboundFetch: typeof fetch = (context.outbound_fetch as unknown as typeof fetch) ?? this.fetchFn;
|
|
563
549
|
const projectId = await loadAntigravityProject(accessToken, {
|
|
564
550
|
baseUrl: this.baseUrl,
|
|
@@ -627,7 +613,7 @@ class AntigravityAdapter implements ProviderAdapter {
|
|
|
627
613
|
throw new GatewayError(
|
|
628
614
|
"platform_unavailable",
|
|
629
615
|
502,
|
|
630
|
-
`Antigravity image generation unreachable: ${error instanceof Error ? error.message : String(error)}
|
|
616
|
+
`Antigravity image generation unreachable (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The connection ghosted us — come back in a moment 👻`,
|
|
631
617
|
{},
|
|
632
618
|
"upstream",
|
|
633
619
|
);
|
|
@@ -649,12 +635,12 @@ class AntigravityAdapter implements ProviderAdapter {
|
|
|
649
635
|
try {
|
|
650
636
|
parsed = JSON.parse(event.data);
|
|
651
637
|
} catch {
|
|
652
|
-
throw new GatewayError("platform_unavailable", 502, "Antigravity image stream returned malformed JSON", {}, "upstream");
|
|
638
|
+
throw new GatewayError("platform_unavailable", 502, "Antigravity image stream returned malformed JSON (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
653
639
|
}
|
|
654
640
|
images.push(...geminiInlineImages(parsed));
|
|
655
641
|
}
|
|
656
642
|
if (images.length === 0) {
|
|
657
|
-
throw new GatewayError("platform_unavailable", 502, "Antigravity image generation returned no image", {}, "upstream");
|
|
643
|
+
throw new GatewayError("platform_unavailable", 502, "Antigravity image generation returned no image (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
658
644
|
}
|
|
659
645
|
return { created: imageCreatedSeconds(), data: images };
|
|
660
646
|
}
|
|
@@ -273,7 +273,7 @@ export function makeBuddyModel(
|
|
|
273
273
|
endpoint?: string,
|
|
274
274
|
realm: "intl" | "cn" = "intl",
|
|
275
275
|
): ModelDefinition {
|
|
276
|
-
if (!entry[0]) throw new GatewayError("invalid_request", 400, "invalid model");
|
|
276
|
+
if (!entry[0]) throw new GatewayError("invalid_request", 400, "invalid model (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
277
277
|
const image = BUDDY_IMAGE_MODEL_ID_SET.has(entry[0]);
|
|
278
278
|
const efforts = (realm === "cn" ? BUDDY_CN_EFFORTS : BUDDY_INTL_EFFORTS)[entry[0].toLowerCase()];
|
|
279
279
|
// Refused on free is only a question for the intl realm. cbcn serves every
|
|
@@ -21,7 +21,7 @@ import { createHash } from "node:crypto";
|
|
|
21
21
|
import { completeRequiredSchema } from "../../../protocol/codecs";
|
|
22
22
|
import type { CanonicalRequest } from "../../../protocol/canonical-model";
|
|
23
23
|
import { isRecord } from "../../../protocol/codecs";
|
|
24
|
-
import { applyDeepSeekReasoning, applyOpenAIReasoning, backfillDeepSeekReasoningContent, clampedEffortOutputFloor } from "../../../protocol/reasoning";
|
|
24
|
+
import { applyDeepSeekReasoning, applyOpenAIReasoning, backfillDeepSeekReasoningContent, clampedEffortOutputFloor, ensureChatReasoningReplayPresence } from "../../../protocol/reasoning";
|
|
25
25
|
import { BUDDY_CN_EFFORTS, BUDDY_INTL_EFFORTS, BUDDY_SHARED_RAW } from "./buddy-catalog-shared";
|
|
26
26
|
// Imported for the CN roster only: a CN-only id has no row in the shared
|
|
27
27
|
// table, and without one `raiseOutputForRaisedEffort` silently no-ops, so a
|
|
@@ -30,6 +30,7 @@ import { BUDDY_CN_EFFORTS, BUDDY_INTL_EFFORTS, BUDDY_SHARED_RAW } from "./buddy-
|
|
|
30
30
|
// cn-provider (which imports this module) resolves before any call.
|
|
31
31
|
import { CODEBUDDY_CN_RAW } from "./codebuddy/cn-catalog";
|
|
32
32
|
import { buddyAccountUid } from "./buddy-oauth-shared";
|
|
33
|
+
import { SYSTEM_PROMPT_WRAPPER_PREFIX, SYSTEM_PROMPT_WRAPPER_SUFFIX } from "../../../console/system-prompt/contracts";
|
|
33
34
|
import type { ProviderDispatchContext } from "../../provider-contracts";
|
|
34
35
|
|
|
35
36
|
const BUDDY_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
@@ -409,6 +410,21 @@ export function scrubBuddyChannelIdentity(messages: Array<Record<string, unknown
|
|
|
409
410
|
}
|
|
410
411
|
}
|
|
411
412
|
|
|
413
|
+
/**
|
|
414
|
+
* OpenAI-compatible adapters that need the DeepSeek reasoning-replay backfill
|
|
415
|
+
* on outbound chat payloads. The buddy family runs it inside
|
|
416
|
+
* `buddyPrePayloadCommon`; these adapters register here so the same rule
|
|
417
|
+
* reaches every reseller that serves a thinking model.
|
|
418
|
+
*/
|
|
419
|
+
export const clineReasoningReplayPrepares: readonly ((
|
|
420
|
+
payload: Record<string, unknown>,
|
|
421
|
+
) => void)[] = [
|
|
422
|
+
(payload) => {
|
|
423
|
+
backfillDeepSeekReasoningContent(payload);
|
|
424
|
+
ensureChatReasoningReplayPresence(payload);
|
|
425
|
+
},
|
|
426
|
+
];
|
|
427
|
+
|
|
412
428
|
/**
|
|
413
429
|
* Payload prefix shared by the buddy pre-payload hooks: force `stream=true`,
|
|
414
430
|
* apply the provider's DeepSeek thinking contract, opt `reasoning_summary` in
|
|
@@ -1152,12 +1168,24 @@ export function finalizeBuddyMessages(
|
|
|
1152
1168
|
export function applyBuddySystemPrompt(
|
|
1153
1169
|
messages: Array<Record<string, unknown>>,
|
|
1154
1170
|
systemPrompt: string,
|
|
1171
|
+
extraSystemText?: string,
|
|
1155
1172
|
): void {
|
|
1173
|
+
// Tenant injection (Inject System Prompt) rides AFTER the fixed prompt,
|
|
1174
|
+
// inside the same system turn, framed by the gateway's marker prefix/suffix:
|
|
1175
|
+
// the upstream validates the identity prompt as the channel opener, so the
|
|
1176
|
+
// fixed text must stay first and byte-identical; the wrapped tail carries
|
|
1177
|
+
// the tenant's directive without touching the identity. An override-mode
|
|
1178
|
+
// tenant prompt degrades to this wrapped append (the caller decides via
|
|
1179
|
+
// `isWrappedInjectionProvider` before passing the text).
|
|
1180
|
+
const effectivePrompt =
|
|
1181
|
+
extraSystemText === undefined || extraSystemText.length === 0
|
|
1182
|
+
? systemPrompt
|
|
1183
|
+
: `${systemPrompt}\n\n${SYSTEM_PROMPT_WRAPPER_PREFIX}\n${extraSystemText}\n${SYSTEM_PROMPT_WRAPPER_SUFFIX}`;
|
|
1156
1184
|
const source = messages.filter(
|
|
1157
1185
|
(message) => message["role"] !== "system" && message["role"] !== "developer",
|
|
1158
1186
|
);
|
|
1159
1187
|
messages.length = 0;
|
|
1160
|
-
messages.push({ role: "system", content:
|
|
1188
|
+
messages.push({ role: "system", content: effectivePrompt });
|
|
1161
1189
|
for (const message of source) {
|
|
1162
1190
|
if (message["role"] === "user" && typeof message["content"] === "string") {
|
|
1163
1191
|
messages.push({
|
|
@@ -100,12 +100,43 @@ export async function fetchBuddyDirectoryModels(args: {
|
|
|
100
100
|
readonly fetcher?: typeof fetch | undefined;
|
|
101
101
|
}): Promise<readonly ModelDefinition[] | null> {
|
|
102
102
|
const fetcher = args.fetcher ?? globalThis.fetch;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
|
|
103
|
+
// Fetch both catalog scopes and merge them. `/v3/config` is ranked higher and
|
|
104
|
+
// is the primary source (matches the reference gateways), but the personal
|
|
105
|
+
// models path carries models the v3 response does not always publish — so a
|
|
106
|
+
// single-source read can silently miss models that are callable. Merging with
|
|
107
|
+
// dedupe by model id gives the union, mirroring the upstream codebuddy2api
|
|
108
|
+
// catalog aggregation. Each failure falls through to the other source, so a
|
|
109
|
+
// host that answers only one behaves exactly as it does today.
|
|
110
|
+
const v3 = await fetchBuddyDirectoryOnce(args, fetcher, BUDDY_CONFIG_PATH);
|
|
111
|
+
const personal = await fetchBuddyDirectoryOnce(args, fetcher, args.modelsPath);
|
|
112
|
+
if (v3 !== null && personal !== null) return mergeBuddyDirectories(v3, personal);
|
|
113
|
+
return v3 ?? personal;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Merges two buddy directory reads into one roster, preserving each source's
|
|
118
|
+
* declaration order and de-duplicating by model id. `primary` wins when both
|
|
119
|
+
* sources declare the same id; models present only in `supplemental` are
|
|
120
|
+
* appended. Both lists are already filtered (disabled/CLI-roster/not-chat),
|
|
121
|
+
* so the merge only has to combine them.
|
|
122
|
+
*/
|
|
123
|
+
function mergeBuddyDirectories(
|
|
124
|
+
primary: readonly ModelDefinition[],
|
|
125
|
+
supplemental: readonly ModelDefinition[],
|
|
126
|
+
): readonly ModelDefinition[] {
|
|
127
|
+
const seen = new Set<string>();
|
|
128
|
+
const merged: ModelDefinition[] = [];
|
|
129
|
+
for (const model of primary) {
|
|
130
|
+
if (seen.has(model.modelId)) continue;
|
|
131
|
+
seen.add(model.modelId);
|
|
132
|
+
merged.push(model);
|
|
133
|
+
}
|
|
134
|
+
for (const model of supplemental) {
|
|
135
|
+
if (seen.has(model.modelId)) continue;
|
|
136
|
+
seen.add(model.modelId);
|
|
137
|
+
merged.push(model);
|
|
138
|
+
}
|
|
139
|
+
return merged;
|
|
109
140
|
}
|
|
110
141
|
|
|
111
142
|
/** Reads one directory path; `null` on any transport, status, or shape miss. */
|
|
@@ -108,10 +108,17 @@ export function buddyEnterprise(context?: ProviderDispatchContext): string | und
|
|
|
108
108
|
*
|
|
109
109
|
* Resolution order:
|
|
110
110
|
* 1. `auth_state.qimei36` — explicit operator-set binding (imported or edited).
|
|
111
|
-
* 2.
|
|
111
|
+
* 2. Derived deterministically from the internal account id (sha256 → 32 hex).
|
|
112
112
|
* This keeps the binding stable across restarts without a database write,
|
|
113
113
|
* and gives each freshly-added account its own id automatically.
|
|
114
114
|
*
|
|
115
|
+
* IMPORTANT — the derived fallback is **not portable across instances**:
|
|
116
|
+
* export/import mints a NEW account id, so the derived binding changes and the
|
|
117
|
+
* upstream channel check (11128) rejects the re-imported account. The export
|
|
118
|
+
* path therefore materializes the raw result of this resolver into
|
|
119
|
+
* `auth_state.qimei36` whenever an account relies on the derivation, so a
|
|
120
|
+
* re-import carries the exact same device identity.
|
|
121
|
+
*
|
|
115
122
|
* Returns `undefined` only when no account id is available (unauthenticated).
|
|
116
123
|
*/
|
|
117
124
|
export function resolveBuddyDeviceBinding(context?: ProviderDispatchContext): string | undefined {
|
|
@@ -121,3 +128,17 @@ export function resolveBuddyDeviceBinding(context?: ProviderDispatchContext): st
|
|
|
121
128
|
if (accountId === undefined || accountId.length === 0) return undefined;
|
|
122
129
|
return createHash("sha256").update(`wb2a:qimei36:${accountId}`).digest("hex").slice(0, 32);
|
|
123
130
|
}
|
|
131
|
+
|
|
132
|
+
/** Materializes the portable binding for an account into a stored auth-state
|
|
133
|
+
* map, when the account does not already pin one explicitly. Used by the
|
|
134
|
+
* export path so a re-import keeps the exact qimei36 device identity. */
|
|
135
|
+
export function materializeBuddyDeviceBinding(
|
|
136
|
+
accountId: string,
|
|
137
|
+
authState: Readonly<Record<string, unknown>> | undefined,
|
|
138
|
+
): Record<string, unknown> | undefined {
|
|
139
|
+
const existing = authState?.["qimei36"];
|
|
140
|
+
if (typeof existing === "string" && existing.trim().length > 0) return undefined;
|
|
141
|
+
if (accountId.length === 0) return undefined;
|
|
142
|
+
const derived = createHash("sha256").update(`wb2a:qimei36:${accountId}`).digest("hex").slice(0, 32);
|
|
143
|
+
return { ...(authState ?? {}), qimei36: derived };
|
|
144
|
+
}
|