cartethyia 2.7.3 → 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/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-ChG5svFb.js} +1 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-BIcWQun7.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-Bf2VelDe.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-JEbYJFxw.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-DzSWzE_j.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-U6AKaJcF.js} +1 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.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-BQS0Rx_u.js → SetupPage-f0DE1h0-.js} +1 -1
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-BAL5npBC.js → SortableList-ClzpwyET.js} +1 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.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/assert-DbRA8EG2.js +15 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-D9CS6dZk.js} +1 -1
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-DympBNq4.js} +1 -1
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/{card-Be5Uzamx.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-DOfrVmsi.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-C9omdjQt.js → dialog-8fiE6tec.js} +1 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-CqGYeOrd.js} +1 -1
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/{entry-q5HwZ8nN.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/{formatters-BTScrQpe.js → formatters-D-MTJxMr.js} +1 -1
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-CPK47xQx.js} +1 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-DBNPOfhV.js} +2 -2
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-BT0zntEr.js} +1 -1
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-Da2ZViWg.js} +1 -1
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-BXNeLw6M.js} +1 -1
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.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-JJGQm8TD.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-BNDZnsUE.js → numbers-CEagj6ZZ.js} +2 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.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-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-B3iTetHN.js → select-BIYIBkMU.js} +2 -2
- package/dist/dashboard/assets/select-BIYIBkMU.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-KrOrm7JZ.js → stack-DfgnFDWY.js} +1 -1
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-CkfdYWel.js} +1 -1
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-SYm237HG.js} +1 -1
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-BQqCRNIi.js} +1 -1
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +55 -54
- 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/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/package.json +2 -1
- package/src/boot/app.ts +9 -8
- package/src/config/network.ts +7 -3
- package/src/console/api-keys/contracts.ts +52 -0
- package/src/console/api-keys/routes.ts +93 -4
- 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 +6 -6
- package/src/console/backup/routes.ts +3 -3
- 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/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +2 -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 +26 -1
- package/src/console/quota/account-test-egress.ts +4 -4
- 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 +12 -12
- package/src/console/share/share-stats.ts +105 -35
- package/src/console/shared/read-through-cache.ts +38 -2
- package/src/console/updates/routes.ts +7 -4
- 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 +320 -16
- package/src/observability/telemetry-status.ts +1 -1
- package/src/persistence/db-sqlite.ts +7 -2
- package/src/persistence/readiness.ts +18 -6
- package/src/persistence/schema.ts +66 -7
- package/src/persistence/stores/api-key-store.ts +99 -3
- package/src/persistence/stores/telemetry-store.ts +1 -1
- 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/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 +13 -24
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- 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 +16 -16
- package/src/providers/integrations/buddy/codebuddy/provider.ts +1 -1
- package/src/providers/integrations/buddy/workbuddy/provider.ts +1 -1
- 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 +1 -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 +6 -6
- package/src/providers/integrations/grok/grok.ts +2 -6
- package/src/providers/integrations/inferhub.ts +4 -11
- 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/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +47 -9
- 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-realm.ts +15 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +12 -13
- package/src/providers/integrations/qoder/qoder-virtual.ts +2 -2
- package/src/providers/integrations/qoder/qoder.ts +188 -22
- 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/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 +47 -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/admission/reasons.ts +17 -17
- package/src/security/api-key-auth.ts +4 -4
- 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 +7 -7
- 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/systemone.ts +8 -8
- package/src/transport/dispatch/token-count.ts +9 -9
- 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 +20 -20
- package/src/transport/request/state.ts +4 -7
- package/src/transport/routing/route-catalog.ts +97 -2
- package/src/transport/routing/route-model.ts +7 -7
- package/src/transport/routing/router.ts +2 -2
- 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/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.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/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.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 +0 -1
- 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 +0 -1
- 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/layout-C_7A6H55.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.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 +0 -1
- 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/switch-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-BayTTy01.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-BayTTy01.js.gz} +0 -0
|
@@ -1,326 +1,349 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure helpers for the provider catalog store: credential parsing, generated
|
|
3
|
-
* labels, and row projections. These are the parts of the store that do not
|
|
4
|
-
* touch the database, kept here so the Drizzle store stays about persistence.
|
|
5
|
-
*/
|
|
6
|
-
import { decodeJwtPayload } from "../../../providers/authentication/oauth-flow-store";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
...(
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* `
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
readonly
|
|
121
|
-
readonly
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
* account
|
|
125
|
-
*
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
...(
|
|
196
|
-
...(
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
:
|
|
216
|
-
|
|
217
|
-
?
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
function
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
): string
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
*/
|
|
250
|
-
export function
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for the provider catalog store: credential parsing, generated
|
|
3
|
+
* labels, and row projections. These are the parts of the store that do not
|
|
4
|
+
* touch the database, kept here so the Drizzle store stays about persistence.
|
|
5
|
+
*/
|
|
6
|
+
import { decodeJwtPayload } from "../../../providers/authentication/oauth-flow-store";
|
|
7
|
+
import {
|
|
8
|
+
asJsonbRecord,
|
|
9
|
+
asRecord,
|
|
10
|
+
firstNonEmptyString as firstString,
|
|
11
|
+
tryParseJson as safeParseJson,
|
|
12
|
+
} from "../../../shared/coerce";
|
|
13
|
+
import type { CredentialMode, ModelCatalogEntry } from "./contracts";
|
|
14
|
+
import {
|
|
15
|
+
FALLBACK_CONTEXT_LIMIT,
|
|
16
|
+
FALLBACK_OUTPUT_LIMIT,
|
|
17
|
+
} from "../../../providers/provider-model-definition";
|
|
18
|
+
import { modelsDevCatalog } from "../../../providers/discovery/models-dev-catalog";
|
|
19
|
+
import { models } from "../../../persistence/schema";
|
|
20
|
+
/** Field names a pasted OAuth credential may carry its access token under. */
|
|
21
|
+
const OAUTH_ACCESS_FIELDS = ["accessToken", "access_token", "access", "token"] as const;
|
|
22
|
+
/** Field names a pasted OAuth credential may carry its refresh token under. */
|
|
23
|
+
const OAUTH_REFRESH_FIELDS = ["refreshToken", "refresh_token", "refresh"] as const;
|
|
24
|
+
|
|
25
|
+
/** Parses an expiry field that may be an ISO string, an epoch ms, or epoch seconds. */
|
|
26
|
+
function parseExpiry(value: unknown): Date | undefined {
|
|
27
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
28
|
+
// Heuristic: a 10-digit value is epoch seconds, a 13-digit value is ms.
|
|
29
|
+
const ms = value < 1e12 ? value * 1000 : value;
|
|
30
|
+
const date = new Date(ms);
|
|
31
|
+
return Number.isFinite(date.getTime()) ? date : undefined;
|
|
32
|
+
}
|
|
33
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
34
|
+
const asNumber = Number(value);
|
|
35
|
+
if (Number.isFinite(asNumber)) return parseExpiry(asNumber);
|
|
36
|
+
const date = new Date(value);
|
|
37
|
+
return Number.isFinite(date.getTime()) ? date : undefined;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reads the two claims a static token's own payload can answer: when it stops
|
|
43
|
+
* working, and who issued it.
|
|
44
|
+
*
|
|
45
|
+
* The issuer is kept because a static JWT carries no other hint of its origin:
|
|
46
|
+
* an operator who pasted one from the wrong surface has nothing in the console
|
|
47
|
+
* to check it against, and several issuers accept the same token shape.
|
|
48
|
+
*/
|
|
49
|
+
export function parseJwtCredential(
|
|
50
|
+
raw: string,
|
|
51
|
+
): { readonly expiresAt?: Date; readonly issuer?: string } | undefined {
|
|
52
|
+
const payload = decodeJwtPayload(raw.trim());
|
|
53
|
+
if (payload === undefined) return undefined;
|
|
54
|
+
const expiresAt = parseExpiry(payload["exp"]);
|
|
55
|
+
const iss = payload["iss"];
|
|
56
|
+
const issuer = typeof iss === "string" && iss.trim().length > 0 ? iss.trim() : undefined;
|
|
57
|
+
return {
|
|
58
|
+
...(expiresAt === undefined ? {} : { expiresAt }),
|
|
59
|
+
...(issuer === undefined ? {} : { issuer }),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Splits a pasted OAuth credential into its access token, refresh token, and
|
|
65
|
+
* expiry.
|
|
66
|
+
*
|
|
67
|
+
* The console accepts either a bare access token or the JSON export a client
|
|
68
|
+
* produces (`{accessToken, refreshToken, expiresAt, …}`, possibly nested under
|
|
69
|
+
* `data`). A bare token has no refresh token — the account can be dispatched
|
|
70
|
+
* with it but cannot be refreshed, so the caller stores it as a static
|
|
71
|
+
* API credential instead of sending the access token to a refresh endpoint.
|
|
72
|
+
*/
|
|
73
|
+
/**
|
|
74
|
+
* Middle segment of an auto-generated account label, from how the credential
|
|
75
|
+
* was actually detected.
|
|
76
|
+
*
|
|
77
|
+
* Only the two kinds that arrive with no name of their own are named here. An
|
|
78
|
+
* OAuth paste carries its own identity in `authState` (the account it was
|
|
79
|
+
* exported from), so it keeps whatever the export or the operator supplied —
|
|
80
|
+
* generating a name for it would overwrite an identity that was already there.
|
|
81
|
+
* An unrecognized mode falls back to `key`, matching the mode it is stored as.
|
|
82
|
+
*/
|
|
83
|
+
export function accountLabelKindSegment(credentialMode: CredentialMode): string {
|
|
84
|
+
return credentialMode === "jwt" ? "jwt" : "key";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* First unused `dcint-key-N` / `dcint-jwt-N` label for one provider.
|
|
89
|
+
*
|
|
90
|
+
* Polished tokens and plain JWTs are the credentials that arrive nameless: an
|
|
91
|
+
* operator pastes several into one provider and every row came out as
|
|
92
|
+
* "<providerId> account", so five accounts were five identical labels and the
|
|
93
|
+
* list could not tell them apart — which account failed, or is cooling, was
|
|
94
|
+
* unreadable.
|
|
95
|
+
*
|
|
96
|
+
* Numbering runs per kind, so the first JWT is `jwt-1` rather than inheriting
|
|
97
|
+
* whatever number the api_key rows reached (`jwt-3` with no `jwt-1` reads as
|
|
98
|
+
* two missing accounts). Counting is case-insensitive on the whole label, so a
|
|
99
|
+
* hand-typed `DCINT-KEY-2` still reserves its number.
|
|
100
|
+
*
|
|
101
|
+
* Pure, so the numbering can be pinned without a database.
|
|
102
|
+
*/
|
|
103
|
+
export function nextGeneratedAccountLabel(
|
|
104
|
+
providerId: string,
|
|
105
|
+
credentialMode: CredentialMode,
|
|
106
|
+
existingLabels: readonly string[],
|
|
107
|
+
): string {
|
|
108
|
+
const kind = accountLabelKindSegment(credentialMode);
|
|
109
|
+
const prefix = `${providerId}-${kind}-`;
|
|
110
|
+
const taken = new Set(
|
|
111
|
+
existingLabels.map((label) => label.trim().toLowerCase()),
|
|
112
|
+
);
|
|
113
|
+
for (let n = 1; ; n += 1) {
|
|
114
|
+
const candidate = `${prefix}${n}`;
|
|
115
|
+
if (!taken.has(candidate.toLowerCase())) return candidate;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function parsePastedOAuthCredential(raw: string): {
|
|
120
|
+
readonly accessToken: string;
|
|
121
|
+
readonly refreshToken: string;
|
|
122
|
+
readonly expiresAt: Date | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* Non-secret account configuration the export carried — the profile the
|
|
125
|
+
* account is bound to, how it authenticates, the region it was minted in.
|
|
126
|
+
* Empty for a bare token, which carries none.
|
|
127
|
+
*/
|
|
128
|
+
readonly authState: Readonly<Record<string, unknown>>;
|
|
129
|
+
} {
|
|
130
|
+
let record: Record<string, unknown> | undefined;
|
|
131
|
+
try {
|
|
132
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
133
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
134
|
+
record = parsed as Record<string, unknown>;
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
record = undefined;
|
|
138
|
+
}
|
|
139
|
+
if (record === undefined) {
|
|
140
|
+
// Bare token: it is the access token, and there is no refresh token.
|
|
141
|
+
const jwt = parseJwtCredential(raw);
|
|
142
|
+
return {
|
|
143
|
+
accessToken: raw,
|
|
144
|
+
refreshToken: raw,
|
|
145
|
+
expiresAt: jwt?.expiresAt,
|
|
146
|
+
authState: {},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
// A nested `data` object is unwrapped the same way the dashboard's parser does.
|
|
150
|
+
const nested =
|
|
151
|
+
record["data"] !== null && typeof record["data"] === "object" && !Array.isArray(record["data"])
|
|
152
|
+
? (record["data"] as Record<string, unknown>)
|
|
153
|
+
: undefined;
|
|
154
|
+
const accessToken =
|
|
155
|
+
firstString(record, OAUTH_ACCESS_FIELDS) ??
|
|
156
|
+
(nested ? firstString(nested, OAUTH_ACCESS_FIELDS) : undefined) ??
|
|
157
|
+
raw;
|
|
158
|
+
const refreshToken =
|
|
159
|
+
firstString(record, OAUTH_REFRESH_FIELDS) ??
|
|
160
|
+
(nested ? firstString(nested, OAUTH_REFRESH_FIELDS) : undefined) ??
|
|
161
|
+
accessToken;
|
|
162
|
+
const expiresAt =
|
|
163
|
+
parseExpiry(
|
|
164
|
+
record["expiresAt"] ??
|
|
165
|
+
record["expires_at"] ??
|
|
166
|
+
(nested ? (nested["expiresAt"] ?? nested["expires_at"]) : undefined) ??
|
|
167
|
+
record["expires"] ??
|
|
168
|
+
(nested ? nested["expires"] : undefined),
|
|
169
|
+
) ?? parseJwtCredential(accessToken)?.expiresAt;
|
|
170
|
+
return { accessToken, refreshToken, expiresAt, authState: pastedAuthState(record, nested) };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The account configuration a pasted export carries beside its tokens.
|
|
175
|
+
*
|
|
176
|
+
* Kiro refuses a generation request that omits the profile the account is bound
|
|
177
|
+
* to, and exports from older installs carry that profile under a different key.
|
|
178
|
+
* Reading neither is how an imported account probes with no
|
|
179
|
+
* profile and is answered `400 profileArn is required`.
|
|
180
|
+
*
|
|
181
|
+
* A wrapper's own `authState` bag travels wholesale rather than cherry-picked:
|
|
182
|
+
* adapters read provider-specific keys this picker cannot enumerate (Qoder's
|
|
183
|
+
* `machineId`, buddy's `qimei36` device binding, cursor's `method`, coreweave's
|
|
184
|
+
* project, kiro's `startUrl`), and dropping the bag silently degraded every
|
|
185
|
+
* imported account to a half-identity credential — failing closed at dispatch
|
|
186
|
+
* (Qoder answered 401 "missing its user identity"). The flat aliases below only
|
|
187
|
+
* fill what an older export shape carries beside the bag, and can never invent
|
|
188
|
+
* a key the bag did not have.
|
|
189
|
+
*/
|
|
190
|
+
function pastedAuthState(
|
|
191
|
+
record: Record<string, unknown>,
|
|
192
|
+
nested: Record<string, unknown> | undefined,
|
|
193
|
+
): Readonly<Record<string, unknown>> {
|
|
194
|
+
const carried = {
|
|
195
|
+
...(asJsonbRecord(record["providerSpecificData"]) ?? {}),
|
|
196
|
+
...(asJsonbRecord(record["authState"]) ?? {}),
|
|
197
|
+
...(asJsonbRecord(nested?.["authState"]) ?? {}),
|
|
198
|
+
};
|
|
199
|
+
// These three are recomputed for the pasted tokens at insert time; a carried
|
|
200
|
+
// copy would describe the previous token, not this one.
|
|
201
|
+
delete carried.credentialMode;
|
|
202
|
+
delete carried.tokenExpiresAt;
|
|
203
|
+
delete carried.jwtIssuer;
|
|
204
|
+
const sources = [record, nested, asJsonbRecord(record["providerSpecificData"])];
|
|
205
|
+
const profileArn = firstStringFrom(sources, ["profileArn", "profile_arn"]);
|
|
206
|
+
const authMethod = firstStringFrom(sources, ["authMethod", "auth_method"]);
|
|
207
|
+
const region = firstStringFrom(sources, ["region"]);
|
|
208
|
+
// The upstream user id, carried on a re-import of an exported account. Trae
|
|
209
|
+
// refresh is addressed per upstream user id, so restoring it is what keeps a
|
|
210
|
+
// re-imported account refreshable.
|
|
211
|
+
const userId = firstStringFrom(sources, ["userId", "user_id", "uid", "UserID"]);
|
|
212
|
+
const machineId = firstStringFrom(sources, ["machineId", "machine_id"]);
|
|
213
|
+
return {
|
|
214
|
+
...carried,
|
|
215
|
+
...(profileArn === undefined ? {} : { profileArn }),
|
|
216
|
+
...(authMethod === undefined ? {} : { authMethod }),
|
|
217
|
+
...(region === undefined ? {} : { region }),
|
|
218
|
+
...(userId === undefined ? {} : { userId }),
|
|
219
|
+
...(machineId === undefined ? {} : { machineId }),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Adds or removes the `image` input modality on a stored modalities blob.
|
|
225
|
+
*
|
|
226
|
+
* The blob is a JSON string whose shape varies by source (bundled seed,
|
|
227
|
+
* discovery, manual add), so it is normalized rather than pattern-matched: an
|
|
228
|
+
* unparseable blob is replaced with the minimal text shape rather than left to
|
|
229
|
+
* corrupt capability preflight.
|
|
230
|
+
*/
|
|
231
|
+
export function withImageModality(stored: string | null | undefined, image: boolean): string {
|
|
232
|
+
const parsed = asRecord(safeParseJson(stored));
|
|
233
|
+
const input = Array.isArray(parsed?.input)
|
|
234
|
+
? (parsed.input as unknown[]).filter((m): m is string => typeof m === "string")
|
|
235
|
+
: ["text"];
|
|
236
|
+
const output = Array.isArray(parsed?.output)
|
|
237
|
+
? (parsed.output as unknown[]).filter((m): m is string => typeof m === "string")
|
|
238
|
+
: ["text"];
|
|
239
|
+
const nextInput = image
|
|
240
|
+
? Array.from(new Set([...input, "image"]))
|
|
241
|
+
: input.filter((m) => m !== "image");
|
|
242
|
+
return JSON.stringify({
|
|
243
|
+
...(parsed ?? {}),
|
|
244
|
+
input: nextInput.length > 0 ? nextInput : ["text"],
|
|
245
|
+
output: output.length > 0 ? output : ["text"],
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Normalizes a text column read as `unknown` back to a string or null. */
|
|
250
|
+
export function asStoredText(value: unknown): string | null {
|
|
251
|
+
return typeof value === "string" ? value : null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function firstStringFrom(
|
|
255
|
+
sources: readonly (Record<string, unknown> | undefined)[],
|
|
256
|
+
fields: readonly string[],
|
|
257
|
+
): string | undefined {
|
|
258
|
+
for (const source of sources) {
|
|
259
|
+
if (source === undefined) continue;
|
|
260
|
+
const value = firstString(source, fields);
|
|
261
|
+
if (value !== undefined) return value;
|
|
262
|
+
}
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* The account's still-active per-model backoffs, or `undefined` when none are.
|
|
268
|
+
*
|
|
269
|
+
* Expired entries are dropped rather than returned: the sweep that clears them
|
|
270
|
+
* runs on a timer, so between the deadline passing and the sweep a raw read
|
|
271
|
+
* would report a cooldown that is not in force.
|
|
272
|
+
*/
|
|
273
|
+
export function liveModelCooldowns(
|
|
274
|
+
raw: unknown,
|
|
275
|
+
): { modelCooldowns: Record<string, string> } | undefined {
|
|
276
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return undefined;
|
|
277
|
+
const now = Date.now();
|
|
278
|
+
const live: Record<string, string> = {};
|
|
279
|
+
for (const [modelId, value] of Object.entries(raw as Record<string, unknown>)) {
|
|
280
|
+
if (typeof value !== "string") continue;
|
|
281
|
+
const at = new Date(value).getTime();
|
|
282
|
+
if (Number.isFinite(at) && at > now) live[modelId] = value;
|
|
283
|
+
}
|
|
284
|
+
return Object.keys(live).length > 0 ? { modelCooldowns: live } : undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Projects one persisted `models` row into the console's catalog entry.
|
|
290
|
+
*
|
|
291
|
+
* `disabled` is resolved by the caller (from `tenant_disabled_models`), because
|
|
292
|
+
* the bulk read has to look it up across every provider at once while the
|
|
293
|
+
* single-provider read looks it up for one; the projection itself is identical
|
|
294
|
+
* either way, so it lives here rather than being written twice.
|
|
295
|
+
*
|
|
296
|
+
* Exported so a test can pin the unsized-model substitution without a database:
|
|
297
|
+
* which rows get the fallback, and that they are the same rows marked estimated.
|
|
298
|
+
*/
|
|
299
|
+
export function mapModelRow(
|
|
300
|
+
row: typeof models.$inferSelect,
|
|
301
|
+
disabled: boolean,
|
|
302
|
+
): ModelCatalogEntry {
|
|
303
|
+
const modalities =
|
|
304
|
+
row.modalities && typeof row.modalities === "object"
|
|
305
|
+
? (row.modalities as { input?: unknown; output?: unknown })
|
|
306
|
+
: undefined;
|
|
307
|
+
const inputModalities = Array.isArray(modalities?.input) ? modalities.input : [];
|
|
308
|
+
const outputModalities = Array.isArray(modalities?.output) ? modalities.output : [];
|
|
309
|
+
// Nothing filed this model — not its own row, not the base catalog under this
|
|
310
|
+
// provider's id, not a majority across the catalog's other resellers. The
|
|
311
|
+
// fallback is substituted here rather than persisted: the stored `null` is the
|
|
312
|
+
// honest record of what upstream said, and substituting at the write would
|
|
313
|
+
// make a guess indistinguishable from a measurement on the next read. The
|
|
314
|
+
// marker rides along so the card can say which one it is showing.
|
|
315
|
+
const unfiled =
|
|
316
|
+
row.contextLimit === null &&
|
|
317
|
+
modelsDevCatalog.resolve(row.providerId, row.modelId) === undefined &&
|
|
318
|
+
modelsDevCatalog.majorityFor(row.modelId) === undefined;
|
|
319
|
+
return {
|
|
320
|
+
modelId: row.modelId,
|
|
321
|
+
route: row.endpointPath,
|
|
322
|
+
provider: row.providerId,
|
|
323
|
+
wireFamily: row.wireFamily,
|
|
324
|
+
serviceKind: row.serviceKind,
|
|
325
|
+
enabled: disabled ? false : row.enabled,
|
|
326
|
+
// A fetched model that published no context size read `n/a ctx · n/a out`
|
|
327
|
+
// while still being routable — a working model that looked unusable. These
|
|
328
|
+
// rows get the same ceilings a manual add is offered, flagged as estimated.
|
|
329
|
+
contextLimit: unfiled ? FALLBACK_CONTEXT_LIMIT : row.contextLimit,
|
|
330
|
+
outputLimit: unfiled ? FALLBACK_OUTPUT_LIMIT : row.outputLimit,
|
|
331
|
+
reasoning: row.reasoning,
|
|
332
|
+
toolCall: row.toolCall,
|
|
333
|
+
vision: inputModalities.includes("image"),
|
|
334
|
+
document: inputModalities.includes("document"),
|
|
335
|
+
audio: inputModalities.includes("audio"),
|
|
336
|
+
mediaGeneration: outputModalities.includes("image"),
|
|
337
|
+
cost: row.cost as ModelCatalogEntry["cost"],
|
|
338
|
+
source: row.source,
|
|
339
|
+
...(unfiled ? { limitsEstimated: true } : {}),
|
|
340
|
+
// Three-valued on the wire as it is in the column: only a row that claims
|
|
341
|
+
// `true` is pro-only. Absence means nobody established the tier, and the
|
|
342
|
+
// surface must render that as unknown rather than as free.
|
|
343
|
+
...(row.proOnly === true ? { proOnly: true } : {}),
|
|
344
|
+
// Same three-valued rule, from the provider's own directory. Qoder states
|
|
345
|
+
// `is_free` per row, so a plan change moves the pill without a code change.
|
|
346
|
+
...(row.freeTier === true ? { freeTier: true } : {}),
|
|
347
|
+
sourceUpdatedAt: row.sourceUpdatedAt?.toISOString() ?? null,
|
|
348
|
+
} satisfies ModelCatalogEntry;
|
|
349
|
+
}
|