cartethyia 2.7.8 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -3
- package/README.md +29 -59
- package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js +1 -0
- package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-Bqj2aa1T.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-CXsaOAO2.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js +1 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BWFLECmz.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-CgoPc5B9.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-D9bFQern.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js +1 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BwNC_-Cx.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js +1 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-febLU4r1.js} +1 -1
- package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-M6fvR5Wz.js} +1 -1
- package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js +56 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js +1 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css +2 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
- package/dist/dashboard/assets/{assert-CidQVoNo.js → assert-CqTLw5e7.js} +1 -1
- package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-C83EqLB0.js} +1 -1
- package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
- package/dist/dashboard/assets/ban-durations-B58FyRxC.js +1 -0
- package/dist/dashboard/assets/ban-durations-B58FyRxC.js.gz +0 -0
- package/dist/dashboard/assets/{banner-B-yXtCqb.js → banner-D1xn7pIe.js} +1 -1
- package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
- package/dist/dashboard/assets/brand/bansos_page.webp +0 -0
- package/dist/dashboard/assets/brand/denia_bansos.webp +0 -0
- package/dist/dashboard/assets/brand/favicon.webp +0 -0
- package/dist/dashboard/assets/brand/login_register.webp +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.webp +0 -0
- package/dist/dashboard/assets/brand/og_bansos.webp +0 -0
- package/dist/dashboard/assets/brand/og_image.webp +0 -0
- package/dist/dashboard/assets/brand/sidebar_ico.webp +0 -0
- package/dist/dashboard/assets/{card-_HwAEqFF.js → card-DU3Lq5Lm.js} +1 -1
- package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js +1 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CEL5Sg_J.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-BjFR5UHy.js} +1 -1
- package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-HvBW0AjI.js} +1 -1
- package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js +4 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
- package/dist/dashboard/assets/{entry-COaC-irN.js → entry-CECVELyy.js} +1 -1
- package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js +1 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-DLW4i8zK.js} +1 -1
- package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
- package/dist/dashboard/assets/{index-CgKmFngv.js → index-1-fCqe64.js} +2 -2
- package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2BirKry.js → inline-DGvU2oLK.js} +1 -1
- package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
- package/dist/dashboard/assets/{input-D-qV9dKY.js → input-iNnO1X2K.js} +1 -1
- package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
- package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-0q8UGypb.js} +1 -1
- package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-C0w2bQjJ.js} +1 -1
- package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
- package/dist/dashboard/assets/{live-DGizF06-.js → live-M5aJ_xCa.js} +1 -1
- package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-GU7XdEjz.js → mutation-BQ0ccH_A.js} +1 -1
- package/dist/dashboard/assets/mutation-BQ0ccH_A.js.gz +0 -0
- package/dist/dashboard/assets/{network-C9UE-bOr.js → network-BKeQGT3K.js} +1 -1
- package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-CivAk3da.js} +1 -1
- package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-CpzvI5ep.js} +1 -1
- package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
- package/dist/dashboard/assets/providers/devin.svg +9 -0
- package/dist/dashboard/assets/providers/devin.svg.gz +0 -0
- package/dist/dashboard/assets/providers/windsurf.svg +10 -0
- package/dist/dashboard/assets/providers/windsurf.svg.gz +0 -0
- package/dist/dashboard/assets/providers/zro.svg +3 -0
- package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
- package/dist/dashboard/assets/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
- package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js +1 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
- package/dist/dashboard/assets/{select-BCOcv8WK.js → select-CXbck0jG.js} +2 -2
- package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js +1 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
- package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DggRiCxE.js} +1 -1
- package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
- package/dist/dashboard/assets/{state-DXbGTx7N.js → state-ClFzfbYp.js} +1 -1
- package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
- package/dist/dashboard/assets/story/25817331.webp +0 -0
- package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
- package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
- package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
- package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
- package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
- package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
- package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
- package/dist/dashboard/assets/{switch-ZeTsW623.js → switch-CTIFxKqS.js} +1 -1
- package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BClG3yWj.js} +1 -1
- package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.webp +0 -0
- package/dist/dashboard/console-backgrounds_initial.webp +0 -0
- package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
- package/dist/dashboard/index.html +3 -3
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +59 -56
- package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
- package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
- package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
- package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
- package/package.json +1 -1
- package/src/boot/app.ts +10 -13
- package/src/boot/dependencies.ts +12 -4
- package/src/boot/flush.ts +20 -0
- package/src/boot/lifecycle.ts +7 -5
- package/src/config/cooldowns.ts +1 -1
- package/src/config/routing.ts +26 -0
- package/src/config/spec-metadata.ts +72 -0
- package/src/config/spec.ts +20 -6
- package/src/console/api-keys/contracts.ts +12 -1
- package/src/console/api-keys/routes.ts +50 -31
- package/src/console/auth/account-reset.ts +7 -9
- package/src/console/backup/contracts.ts +4 -0
- package/src/console/backup/export-endpoint.ts +7 -5
- package/src/console/cli-tools/service.ts +6 -12
- package/src/console/console-router-guards.ts +23 -16
- package/src/console/console-router.ts +7 -0
- package/src/console/dashboard-assets.ts +5 -5
- package/src/console/domain-registration.ts +27 -2
- package/src/console/inferhub/normalize.ts +24 -28
- package/src/console/inferhub/types.ts +3 -0
- package/src/console/ip-bans/routes.ts +93 -0
- package/src/console/observability/store.ts +0 -53
- package/src/console/providers/catalog/catalog-projections.ts +5 -2
- package/src/console/providers/catalog/contracts.ts +123 -0
- package/src/console/providers/catalog/discover-constants.ts +6 -0
- package/src/console/providers/catalog/model-operations.ts +122 -10
- package/src/console/providers/catalog/provider-operations.ts +116 -8
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +65 -4
- package/src/console/providers/catalog/store-helpers.ts +30 -1
- package/src/console/providers/catalog/store.ts +249 -4
- package/src/console/quota/account-quota-global.ts +97 -19
- package/src/console/quota/account-quota-shared.ts +49 -0
- package/src/console/quota/account-quota-tenant.ts +6 -18
- package/src/console/quota/native-identity.ts +2 -0
- package/src/console/quota/quota-view.ts +19 -3
- package/src/console/routing/pools/operations.ts +31 -46
- package/src/console/settings/contracts.ts +18 -0
- package/src/console/settings/store.ts +32 -0
- package/src/console/shared/errors.ts +33 -6
- package/src/console/system-prompt/contracts.ts +0 -1
- package/src/main.ts +3 -3
- package/src/network/pool/byte-accounting.ts +3 -2
- package/src/network/pool/selector.ts +182 -39
- package/src/network/pool-cooldown-store.ts +89 -14
- package/src/network/pool-health.ts +49 -61
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/ssrf.ts +6 -6
- package/src/observability/event-log.ts +7 -361
- package/src/observability/event-query.ts +18 -4
- package/src/observability/performance-metrics.ts +9 -0
- package/src/observability/runtime-metrics.ts +14 -9
- package/src/observability/telemetry-buffer.ts +15 -25
- package/src/observability/telemetry-reconciliation.ts +9 -37
- package/src/observability/telemetry-row.ts +125 -0
- package/src/persistence/db-mode.ts +4 -1
- package/src/persistence/dialect.ts +30 -0
- package/src/persistence/migrate.ts +6 -3
- package/src/persistence/schema.ts +28 -2
- package/src/persistence/ttl-cache.ts +20 -2
- package/src/protocol/codecs.ts +1 -1
- package/src/protocol/dsml.ts +35 -87
- package/src/protocol/gateway-error.ts +1 -0
- package/src/protocol/markup-stream-parser.ts +195 -0
- package/src/protocol/reasoning-text.ts +19 -0
- package/src/protocol/reasoning.ts +1 -1
- package/src/protocol/request/chat.ts +14 -14
- package/src/protocol/request/gemini.ts +8 -2
- package/src/protocol/request/messages.ts +4 -5
- package/src/protocol/response/chat.ts +12 -20
- package/src/protocol/response/gemini.ts +6 -1
- package/src/protocol/response/omp-tool-call-markup.ts +43 -86
- package/src/protocol/response/responses.ts +18 -2
- package/src/protocol/sse-decoder.ts +25 -8
- package/src/protocol/stop-reason.ts +23 -1
- package/src/protocol/stream-error-frames.ts +13 -3
- package/src/protocol/think-split.ts +112 -0
- package/src/protocol/translation/dialect/qwen.ts +26 -58
- package/src/protocol/translation/quirks.ts +1 -1
- package/src/protocol/translation/tool-repair.ts +2 -2
- package/src/protocol/upstream/send-claude-messages.ts +15 -4
- package/src/protocol/upstream-parsing.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +78 -17
- package/src/providers/authentication/oauth-client.ts +1 -1
- package/src/providers/authentication/oauth-device-flow.ts +98 -8
- package/src/providers/authentication/oauth-refresh-service.ts +153 -59
- package/src/providers/configured-provider.ts +148 -0
- package/src/providers/default-registry.ts +117 -27
- package/src/providers/discovery/discovery-types.ts +12 -0
- package/src/providers/discovery/probe-phases.ts +74 -33
- package/src/providers/discovery/probing-service.ts +293 -165
- package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
- package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +121 -2
- package/src/providers/integrations/antigravity/antigravity.ts +13 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +191 -35
- package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
- package/src/providers/integrations/buddy/buddy-identity.ts +89 -6
- package/src/providers/integrations/buddy/buddy-media.ts +4 -5
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
- package/src/providers/integrations/buddy/capabilities.ts +6 -43
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +23 -1
- package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
- package/src/providers/integrations/buddy/codebuddy/shared.ts +38 -27
- package/src/providers/integrations/claude/claude-quota.ts +14 -17
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/commandcode/quota.ts +22 -28
- package/src/providers/integrations/cursor/cursor-auth.ts +0 -8
- package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
- package/src/providers/integrations/cursor/cursor-exec.ts +745 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
- package/src/providers/integrations/cursor/cursor-request.ts +35 -20
- package/src/providers/integrations/cursor/cursor-shared.ts +26 -5
- package/src/providers/integrations/cursor/cursor.ts +139 -70
- package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
- package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
- package/src/providers/integrations/devin/devin-auth.ts +353 -0
- package/src/providers/integrations/devin/devin-catalog.ts +207 -0
- package/src/providers/integrations/devin/devin-http.ts +91 -0
- package/src/providers/integrations/devin/devin-quota.ts +214 -0
- package/src/providers/integrations/devin/devin-shared.ts +111 -0
- package/src/providers/integrations/devin/devin.ts +539 -0
- package/src/providers/integrations/devin/protocol/devin-proto.ts +558 -0
- package/src/providers/integrations/devin/protocol/framing.ts +124 -0
- package/src/providers/integrations/factory/factory-oauth.ts +17 -44
- package/src/providers/integrations/factory/factory.ts +2 -2
- package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
- package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
- package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
- package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
- package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
- package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
- package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
- package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
- package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
- package/src/providers/integrations/freebuff/freebuff.ts +682 -0
- package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
- package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
- package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
- package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
- package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
- package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
- package/src/providers/integrations/freepi/freepi.ts +292 -0
- package/src/providers/integrations/gemini.ts +2 -2
- package/src/providers/integrations/grok/grok-oauth.ts +48 -45
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
- package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
- package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
- package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +13 -21
- package/src/providers/integrations/hermes/index.ts +2 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
- package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
- package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
- package/src/providers/integrations/kiro/kiro.ts +15 -19
- package/src/providers/integrations/ollamacloud/index.ts +2 -0
- package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
- package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
- package/src/providers/integrations/perch/perch-catalog.ts +105 -0
- package/src/providers/integrations/perch/perch-credential.ts +25 -0
- package/src/providers/integrations/perch/perch-oauth.ts +170 -0
- package/src/providers/integrations/perch/perch-protocol.ts +208 -0
- package/src/providers/integrations/perch/perch-quota.ts +123 -0
- package/src/providers/integrations/perch/perch-realm.ts +82 -0
- package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
- package/src/providers/integrations/perch/perch.ts +305 -0
- package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
- package/src/providers/integrations/qoder/chat-proto.ts +328 -328
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
- package/src/providers/integrations/qoder/qoder-adapter.ts +90 -40
- package/src/providers/integrations/qoder/qoder-auth.ts +101 -29
- package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +26 -6
- package/src/providers/integrations/qoder/qoder-chat.ts +7 -21
- package/src/providers/integrations/qoder/qoder-checkin.ts +6 -6
- package/src/providers/integrations/qoder/qoder-grpc.ts +210 -7
- package/src/providers/integrations/qoder/qoder-ide.ts +5 -108
- package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
- package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
- package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
- package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
- package/src/providers/integrations/qoder/qoder.ts +3 -7
- package/src/providers/integrations/trae/trae-catalog.ts +9 -14
- package/src/providers/integrations/trae/trae-headers.ts +36 -11
- package/src/providers/integrations/trae/trae-oauth.ts +14 -17
- package/src/providers/integrations/trae/trae-protocol.ts +12 -11
- package/src/providers/integrations/trae/trae-quota.ts +96 -36
- package/src/providers/integrations/trae/trae-realm.ts +74 -15
- package/src/providers/integrations/trae/trae.ts +99 -26
- package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
- package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
- package/src/providers/integrations/zro/zro.ts +204 -0
- package/src/providers/operations/account-error-classification.ts +235 -278
- package/src/providers/operations/account-health-service.ts +273 -26
- package/src/providers/operations/account-refresh-status.ts +269 -0
- package/src/providers/operations/account-tier.ts +66 -0
- package/src/providers/operations/client-version-resolver.ts +1 -1
- package/src/providers/operations/client-versions.ts +42 -150
- package/src/providers/operations/cooldown-rules.ts +56 -0
- package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
- package/src/providers/operations/provider-catalog-seeder.ts +4 -0
- package/src/providers/operations/provider-catalog-service.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +102 -32
- package/src/providers/operations/provider-rules/github.ts +48 -0
- package/src/providers/operations/provider-rules/grok.ts +33 -0
- package/src/providers/operations/provider-rules/inception.ts +49 -0
- package/src/providers/operations/provider-rules/index.ts +32 -0
- package/src/providers/operations/provider-rules/perch.ts +37 -0
- package/src/providers/operations/provider-rules/qoder.ts +70 -0
- package/src/providers/operations/provider-rules/trae.ts +81 -0
- package/src/providers/provider-contracts.ts +33 -2
- package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
- package/src/providers/provider-metadata.ts +99 -14
- package/src/providers/provider-model-definition.ts +36 -0
- package/src/providers/provider-probe.ts +15 -1
- package/src/providers/provider-registry.ts +42 -33
- package/src/providers/quota/quota-limit-windows.ts +16 -5
- package/src/providers/quota/quota-support.ts +1 -1
- package/src/providers/quota/quota-window-parser.ts +90 -18
- package/src/providers/quota/refresh.ts +96 -18
- package/src/security/abuse.ts +71 -0
- package/src/security/api-key-auth-cache.ts +1 -1
- package/src/security/api-key-auth.ts +6 -2
- package/src/transport/dispatch/attempt-finalize.ts +14 -13
- package/src/transport/dispatch/attempt-loop.ts +71 -17
- package/src/transport/dispatch/generation.ts +2 -8
- package/src/transport/dispatch/native-shared.ts +25 -0
- package/src/transport/dispatch/proxy-request.ts +9 -46
- package/src/transport/dispatch/responses-compact.ts +2 -8
- package/src/transport/dispatch/streaming/finalize.ts +9 -6
- package/src/transport/dispatch/streaming/stream.ts +9 -36
- package/src/transport/dispatch/systemone.ts +2 -8
- package/src/transport/dispatch/token-count.ts +2 -6
- package/src/transport/dispatch/upstream.ts +67 -1
- package/src/transport/drain-endpoint.ts +31 -4
- package/src/transport/failure-policy.ts +129 -37
- package/src/transport/guards/body-policy.ts +7 -1
- package/src/transport/guards/error-lifecycle.ts +13 -1
- package/src/transport/guards/request-context.ts +22 -14
- package/src/transport/request/preparer.ts +81 -71
- package/src/transport/routing/bounded-lru.ts +14 -0
- package/src/transport/routing/drain-reserve.ts +129 -0
- package/src/transport/routing/plan-gate.ts +7 -1
- package/src/transport/routing/round-robin.ts +60 -0
- package/src/transport/routing/route-catalog.ts +123 -111
- package/src/transport/routing/route-model.ts +51 -9
- package/src/transport/routing/router.ts +418 -33
- package/src/transport/surface/adapters.ts +11 -2
- package/src/transport/surface/chat/adapter.ts +2 -2
- package/src/transport/surface/chat/encode.ts +47 -4
- package/src/transport/surface/chat/parse.ts +2 -12
- package/src/transport/surface/completion.ts +44 -33
- package/src/transport/surface/content-parts.ts +21 -0
- package/src/transport/surface/messages/adapter.ts +9 -2
- package/src/transport/surface/messages/encode.ts +11 -3
- package/src/transport/surface/messages/parse.ts +2 -1
- package/src/transport/surface/messages/stream.ts +33 -17
- package/src/transport/surface/responses/encode.ts +34 -6
- package/src/transport/surface/responses/parse.ts +3 -11
- package/src/workers/daily-checkin.ts +1 -1
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/scheduler.ts +22 -4
- package/dist/dashboard/assets/BannedPage-DeIbIiBU.js +0 -1
- package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js +0 -1
- package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-BEsU8hKs.css +0 -2
- package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
- package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
- package/dist/dashboard/assets/brand/bansos_page.png +0 -0
- package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/favicon.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
- package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
- package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
- package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
- package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
- package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
- package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
- package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
- package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
- package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
- package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
- package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
- package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
- package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
- package/dist/dashboard/assets/switch-ZeTsW623.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/donation/qris-risun-marke.png +0 -0
- package/src/providers/bundled-discovery-providers.ts +0 -98
- package/src/providers/configured-openai-providers.ts +0 -120
- package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
- package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
- package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
- package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
- package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
- package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
- package/src/providers/operations/model-discovery-cache.ts +0 -34
- package/src/providers/operations/probe-fetch.ts +0 -15
- package/src/providers/operations/provider-version-cache.ts +0 -30
- package/src/providers/provider-capabilities.ts +0 -33
- package/src/providers/provider-credential-envelope.ts +0 -43
- /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-DIXLjojg.js} +0 -0
- /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-DIXLjojg.js.gz} +0 -0
|
@@ -62,6 +62,72 @@ export const ACCOUNT_STATUSES = ["active", "disabled"] as const;
|
|
|
62
62
|
/** One operator-settable account status. */
|
|
63
63
|
export type AccountStatus = (typeof ACCOUNT_STATUSES)[number];
|
|
64
64
|
|
|
65
|
+
/** Display-only warning type shared with the dashboard contract mirror. */
|
|
66
|
+
export type { ModelWarning, ModelWarningSeverity } from "../../../providers/provider-model-definition";
|
|
67
|
+
import type { ModelWarning } from "../../../providers/provider-model-definition";
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Outcome of one provider-scoped bulk account action (area 02).
|
|
71
|
+
*
|
|
72
|
+
* A tenant-scoped action never touches shared (`tenant_id IS NULL`) rows; the
|
|
73
|
+
* global-admin pair of routes does. `globalSkipped` counts those rows on the
|
|
74
|
+
* tenant path so the console can say why its visible list and the DB disagree.
|
|
75
|
+
* `deleted`/`missing` are present on delete; `updated` on disable. Both actions
|
|
76
|
+
* are idempotent: a second call finds no matching row and reports zero.
|
|
77
|
+
*/
|
|
78
|
+
export interface ProviderAccountBulkActionResult {
|
|
79
|
+
readonly ok: boolean;
|
|
80
|
+
readonly updated: number;
|
|
81
|
+
readonly deleted?: readonly string[];
|
|
82
|
+
readonly missing?: readonly string[];
|
|
83
|
+
readonly globalSkipped: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Cap on one discovery-commit request (D29). Kept pure (no `node:*`) so the dashboard can import it. */
|
|
87
|
+
export { DISCOVER_MODEL_COMMIT_MAX } from "./discover-constants";
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* One model the provider's own listing advertised, projected for the Add-Model
|
|
91
|
+
* picker. Read-only: `GET .../models/discover` writes nothing. `models`
|
|
92
|
+
* metadata here is the fetched `ModelDefinition`, not the static catalog's
|
|
93
|
+
* guess, so the operator can see exactly what the commit will store.
|
|
94
|
+
*/
|
|
95
|
+
export interface DiscoverModelCandidate {
|
|
96
|
+
readonly modelId: string;
|
|
97
|
+
readonly wireFamily: string;
|
|
98
|
+
readonly serviceKind: string;
|
|
99
|
+
readonly endpointPath: string;
|
|
100
|
+
readonly contextLimit: number | null;
|
|
101
|
+
readonly outputLimit: number | null;
|
|
102
|
+
readonly modalities: { readonly input: readonly string[]; readonly output: readonly string[] };
|
|
103
|
+
readonly reasoning: boolean;
|
|
104
|
+
readonly toolCall: boolean;
|
|
105
|
+
readonly freeTier?: boolean;
|
|
106
|
+
readonly proOnly?: boolean;
|
|
107
|
+
readonly alreadyRegistered: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Result of `GET .../models/discover`. */
|
|
111
|
+
export interface DiscoverModelCandidatesResponse {
|
|
112
|
+
readonly supported: boolean;
|
|
113
|
+
readonly candidates: readonly DiscoverModelCandidate[];
|
|
114
|
+
/** Label of the usable account whose credential fetched the listing (D27). */
|
|
115
|
+
readonly accountLabel?: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Body of `POST .../models/discover/commit`. */
|
|
119
|
+
export interface DiscoverModelCommitRequest {
|
|
120
|
+
readonly modelIds: readonly string[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Outcome of a discovery commit: the ids persisted and the ones skipped. */
|
|
124
|
+
export interface DiscoverModelCommitResult {
|
|
125
|
+
readonly ok: boolean;
|
|
126
|
+
readonly committed: readonly string[];
|
|
127
|
+
readonly skipped: readonly string[];
|
|
128
|
+
readonly synced: number;
|
|
129
|
+
}
|
|
130
|
+
|
|
65
131
|
const VALID_WIRE_FAMILIES: Record<WireFamily, true> = {
|
|
66
132
|
chat: true,
|
|
67
133
|
responses: true,
|
|
@@ -121,6 +187,16 @@ const ADAPTER_OWNED: Record<string, true> = {
|
|
|
121
187
|
};
|
|
122
188
|
|
|
123
189
|
export function validateCompatibilityProfile(profile: CompatibilityProfile): void {
|
|
190
|
+
if (profile.service_kinds !== undefined) {
|
|
191
|
+
if (!Array.isArray(profile.service_kinds)) {
|
|
192
|
+
throw new GatewayError("invalid_request", 400, "service_kinds must be an array (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
193
|
+
}
|
|
194
|
+
for (const kind of profile.service_kinds) {
|
|
195
|
+
if (!isServiceKind(kind)) {
|
|
196
|
+
throw new GatewayError("invalid_request", 400, `unknown service kind ${String(kind)} (request {requestId}). Check the shape of what you sent — then resend 🪞`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
124
200
|
if (profile.cli_identity !== undefined) {
|
|
125
201
|
if (typeof profile.cli_identity !== "boolean") {
|
|
126
202
|
throw new GatewayError("invalid_request", 400, "cli_identity must be boolean (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
@@ -358,6 +434,8 @@ export interface ModelCatalogEntry {
|
|
|
358
434
|
* provider's own model directory states it.
|
|
359
435
|
*/
|
|
360
436
|
freeTier?: boolean;
|
|
437
|
+
/** Display-only warnings; `null` in storage means none. */
|
|
438
|
+
warnings?: readonly ModelWarning[];
|
|
361
439
|
}
|
|
362
440
|
export interface FlatModelCatalogEntry {
|
|
363
441
|
readonly providerId: string;
|
|
@@ -407,6 +485,11 @@ export interface ModelMetadataOverride {
|
|
|
407
485
|
readonly image?: boolean;
|
|
408
486
|
readonly reasoning?: boolean;
|
|
409
487
|
readonly toolCall?: boolean;
|
|
488
|
+
/**
|
|
489
|
+
* Display-only warnings, editable only on non-builtin rows. `null` clears
|
|
490
|
+
* the stored value; absence leaves it alone.
|
|
491
|
+
*/
|
|
492
|
+
readonly warnings?: readonly ModelWarning[] | null;
|
|
410
493
|
}
|
|
411
494
|
|
|
412
495
|
/** Register payload: model ids plus optional operator metadata. */
|
|
@@ -484,6 +567,27 @@ export interface UpdateProviderAccountRequest {
|
|
|
484
567
|
*/
|
|
485
568
|
staticToken?: boolean;
|
|
486
569
|
}
|
|
570
|
+
/**
|
|
571
|
+
* Response of `POST /providers/:providerId/accounts/:accountId/refresh-token`
|
|
572
|
+
* (area 04). The refresh never changes `status`: `statusAfter` is re-read from
|
|
573
|
+
* the database after the attempt and is never `"active"` by fiat — a disabled
|
|
574
|
+
* account stays disabled, only an operator can re-enable it.
|
|
575
|
+
*/
|
|
576
|
+
export interface RefreshAccountTokenResponse {
|
|
577
|
+
readonly ok: boolean;
|
|
578
|
+
readonly statusAfter: string;
|
|
579
|
+
/** True when an upstream token refresh actually minted a fresh token. */
|
|
580
|
+
readonly tokenRefreshed?: boolean;
|
|
581
|
+
/** ISO timestamp of the fresh access-token expiry, when known. */
|
|
582
|
+
readonly expiresAt?: string;
|
|
583
|
+
/** ISO timestamp of the refresh attempt. */
|
|
584
|
+
readonly refreshedAt?: string;
|
|
585
|
+
/** Structured reason when `ok` is false (`not_oauth`, `static_token`, …). */
|
|
586
|
+
readonly error?: string;
|
|
587
|
+
/** Human detail (failure message) when available. */
|
|
588
|
+
readonly category?: string;
|
|
589
|
+
}
|
|
590
|
+
|
|
487
591
|
export interface ProviderAccountTokenUsage {
|
|
488
592
|
readonly requests: number;
|
|
489
593
|
readonly errors: number;
|
|
@@ -786,6 +890,7 @@ export interface ProviderCatalogStore {
|
|
|
786
890
|
probeAllModels(
|
|
787
891
|
tenantId: string,
|
|
788
892
|
providerId: string,
|
|
893
|
+
mode?: ProbeModelRequest["mode"],
|
|
789
894
|
): Promise<ProbeAllModelsResult>;
|
|
790
895
|
probeAllAccounts(
|
|
791
896
|
tenantId: string,
|
|
@@ -817,6 +922,20 @@ export interface ProviderCatalogStore {
|
|
|
817
922
|
request: UpdateModelMetadataRequest,
|
|
818
923
|
): Promise<boolean>;
|
|
819
924
|
syncModels(tenantId: string, providerId: string): Promise<{ synced: number }>;
|
|
925
|
+
/**
|
|
926
|
+
* Read-only `/models` discovery. Never persists. `supported: false` names a
|
|
927
|
+
* provider with no listing endpoint so the UI can fall back to manual add.
|
|
928
|
+
*/
|
|
929
|
+
listDiscoveryCandidates(
|
|
930
|
+
tenantId: string,
|
|
931
|
+
providerId: string,
|
|
932
|
+
): Promise<{ readonly supported: boolean; readonly candidates: readonly DiscoverModelCandidate[]; readonly accountLabel?: string }>;
|
|
933
|
+
/** Persists a verified subset of a fresh discovery as `source="manual"`. */
|
|
934
|
+
commitDiscoveredModels(
|
|
935
|
+
tenantId: string,
|
|
936
|
+
providerId: string,
|
|
937
|
+
modelIds: readonly string[],
|
|
938
|
+
): Promise<{ readonly committed: readonly string[]; readonly skipped: readonly string[]; readonly synced: number }>;
|
|
820
939
|
listAccounts(tenantId: string, providerId: string): Promise<readonly ProviderAccountResponse[]>;
|
|
821
940
|
listAllAccounts(tenantId: string): Promise<readonly ProviderAccountResponse[]>;
|
|
822
941
|
/**
|
|
@@ -891,6 +1010,10 @@ export interface ProviderCatalogStore {
|
|
|
891
1010
|
dayKey: string,
|
|
892
1011
|
): Promise<readonly BuddyGrowthTaskLedgerEntry[]>;
|
|
893
1012
|
recoverAccount(tenantId: string, providerId: string, accountId: string): Promise<boolean>;
|
|
1013
|
+
/** One UPDATE over this tenant's cooling accounts (D07/D08); evidence kept. */
|
|
1014
|
+
disableCooldowns(tenantId: string, providerId: string): Promise<ProviderAccountBulkActionResult>;
|
|
1015
|
+
/** One DELETE over this tenant's banned accounts (D12); idempotent. */
|
|
1016
|
+
deleteBanned(tenantId: string, providerId: string): Promise<ProviderAccountBulkActionResult>;
|
|
894
1017
|
}
|
|
895
1018
|
|
|
896
1019
|
/**
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure discovery constants, kept free of any `node:*` imports so the
|
|
3
|
+
* dashboard can import them without dragging Node crypto into the browser
|
|
4
|
+
* bundle — the same reason `ban-constants.ts` and `usage-dimensions.ts` exist.
|
|
5
|
+
*/
|
|
6
|
+
export const DISCOVER_MODEL_COMMIT_MAX = 100;
|
|
@@ -11,7 +11,17 @@
|
|
|
11
11
|
import type { AccessDecision } from "../../../security/access-control";
|
|
12
12
|
import { ConsoleDomainError, requireScope, requireTenantAnyScope } from "../../shared/errors";
|
|
13
13
|
import { GatewayError } from "../../../protocol/gateway-error";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
DISCOVER_MODEL_COMMIT_MAX,
|
|
16
|
+
MODEL_READ_SCOPES,
|
|
17
|
+
MODEL_WRITE_SCOPES,
|
|
18
|
+
} from "./contracts";
|
|
19
|
+
import type { DiscoverModelCandidatesResponse, DiscoverModelCommitResult } from "./contracts";
|
|
20
|
+
import {
|
|
21
|
+
MODEL_WARNING_KIND_MAX_LENGTH,
|
|
22
|
+
MODEL_WARNING_MESSAGE_MAX_LENGTH,
|
|
23
|
+
MODEL_WARNING_SEVERITIES,
|
|
24
|
+
} from "../../../providers/provider-model-definition";
|
|
15
25
|
import type {
|
|
16
26
|
ByokConnectionTestRequest,
|
|
17
27
|
ByokConnectionTestResult,
|
|
@@ -47,6 +57,60 @@ function validateModelMetadata(metadata: ModelMetadataOverride | undefined): voi
|
|
|
47
57
|
`${field} must be a positive integer, or null for unknown`,
|
|
48
58
|
);
|
|
49
59
|
}
|
|
60
|
+
// `null` clears the stored blob; an absent field leaves it alone. Only a
|
|
61
|
+
// non-builtin row can actually carry an edit — the store enforces that
|
|
62
|
+
// invariant. Bounds here stop a hostile payload from persisting a multi-MB
|
|
63
|
+
// display banner; severity is a closed whitelist.
|
|
64
|
+
if (metadata.warnings === null || metadata.warnings === undefined) return;
|
|
65
|
+
if (!Array.isArray(metadata.warnings)) {
|
|
66
|
+
throw new ConsoleDomainError("invalid_request", 400, "warnings must be an array or null");
|
|
67
|
+
}
|
|
68
|
+
for (const [index, warning] of metadata.warnings.entries()) {
|
|
69
|
+
if (warning === null || typeof warning !== "object" || Array.isArray(warning)) {
|
|
70
|
+
throw new ConsoleDomainError("invalid_request", 400, `warnings[${index}] must be an object`);
|
|
71
|
+
}
|
|
72
|
+
const kind = (warning as { kind?: unknown }).kind;
|
|
73
|
+
const message = (warning as { message?: unknown }).message;
|
|
74
|
+
const severity = (warning as { severity?: unknown }).severity;
|
|
75
|
+
if (typeof kind !== "string" || kind.trim().length === 0 || kind.length > MODEL_WARNING_KIND_MAX_LENGTH) {
|
|
76
|
+
throw new ConsoleDomainError(
|
|
77
|
+
"invalid_request",
|
|
78
|
+
400,
|
|
79
|
+
`warnings[${index}].kind must be a non-empty string up to ${MODEL_WARNING_KIND_MAX_LENGTH} chars`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (typeof message !== "string" || message.trim().length === 0 || message.length > MODEL_WARNING_MESSAGE_MAX_LENGTH) {
|
|
83
|
+
throw new ConsoleDomainError(
|
|
84
|
+
"invalid_request",
|
|
85
|
+
400,
|
|
86
|
+
`warnings[${index}].message must be a non-empty string up to ${MODEL_WARNING_MESSAGE_MAX_LENGTH} chars`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (
|
|
90
|
+
severity !== undefined &&
|
|
91
|
+
(typeof severity !== "string" ||
|
|
92
|
+
!(MODEL_WARNING_SEVERITIES as readonly string[]).includes(severity))
|
|
93
|
+
) {
|
|
94
|
+
throw new ConsoleDomainError(
|
|
95
|
+
"invalid_request",
|
|
96
|
+
400,
|
|
97
|
+
`warnings[${index}].severity must be one of ${MODEL_WARNING_SEVERITIES.join(", ")}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Discovery lives in the provider layer and raises `GatewayError`; the console
|
|
105
|
+
* speaks `ConsoleDomainError`. Keeping the exact code/status/message lets the
|
|
106
|
+
* dashboard toast name the real upstream reason rather than a generic failure.
|
|
107
|
+
*/
|
|
108
|
+
function translateDiscoveryError(error: unknown): unknown {
|
|
109
|
+
if (error instanceof ConsoleDomainError) return error;
|
|
110
|
+
if (error instanceof GatewayError) {
|
|
111
|
+
return new ConsoleDomainError(error.code, error.status, error.message);
|
|
112
|
+
}
|
|
113
|
+
return error;
|
|
50
114
|
}
|
|
51
115
|
|
|
52
116
|
export function createModelCatalogOperations(config: ProviderCatalogConfig) {
|
|
@@ -164,14 +228,7 @@ export function createModelCatalogOperations(config: ProviderCatalogConfig) {
|
|
|
164
228
|
await config.snapshotInvalidator?.invalidate();
|
|
165
229
|
return result;
|
|
166
230
|
} catch (error) {
|
|
167
|
-
|
|
168
|
-
// console speaks `ConsoleDomainError`. Translating here keeps the
|
|
169
|
-
// upstream-vs-operator distinction and the status code intact, so the
|
|
170
|
-
// dashboard toast names the real reason instead of a generic failure.
|
|
171
|
-
if (error instanceof GatewayError) {
|
|
172
|
-
throw new ConsoleDomainError(error.code, error.status, error.message);
|
|
173
|
-
}
|
|
174
|
-
throw error;
|
|
231
|
+
throw translateDiscoveryError(error);
|
|
175
232
|
}
|
|
176
233
|
}
|
|
177
234
|
/** Manually registers additional model ids on an existing provider (tenant-bound, no upstream call). */,
|
|
@@ -247,9 +304,10 @@ export function createModelCatalogOperations(config: ProviderCatalogConfig) {
|
|
|
247
304
|
async probeAllModels(
|
|
248
305
|
access: AccessDecision | undefined,
|
|
249
306
|
providerId: string,
|
|
307
|
+
mode?: ProbeModelRequest["mode"],
|
|
250
308
|
): Promise<ProbeAllModelsResult> {
|
|
251
309
|
const a = requireTenantAnyScope(access, MODEL_WRITE_SCOPES);
|
|
252
|
-
return config.store.probeAllModels(a.tenantId, providerId);
|
|
310
|
+
return config.store.probeAllModels(a.tenantId, providerId, mode);
|
|
253
311
|
},
|
|
254
312
|
async probeAllAccounts(
|
|
255
313
|
access: AccessDecision | undefined,
|
|
@@ -299,6 +357,60 @@ export function createModelCatalogOperations(config: ProviderCatalogConfig) {
|
|
|
299
357
|
await config.snapshotInvalidator?.invalidate();
|
|
300
358
|
return { success: true };
|
|
301
359
|
},
|
|
360
|
+
/**
|
|
361
|
+
* `GET .../models/discover`: fetch the provider's own listing without any
|
|
362
|
+
* write (D30). A provider with no listing endpoint answers `supported:false`
|
|
363
|
+
* so the UI can fall back to manual add instead of showing an error.
|
|
364
|
+
*/
|
|
365
|
+
async listDiscoveryCandidates(
|
|
366
|
+
access: AccessDecision | undefined,
|
|
367
|
+
providerId: string,
|
|
368
|
+
): Promise<DiscoverModelCandidatesResponse> {
|
|
369
|
+
const a = requireTenantAnyScope(access, MODEL_READ_SCOPES);
|
|
370
|
+
try {
|
|
371
|
+
return await config.store.listDiscoveryCandidates(a.tenantId, providerId);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
throw translateDiscoveryError(error);
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
/**
|
|
377
|
+
* `POST .../models/discover/commit`: the store re-fetches the roster and
|
|
378
|
+
* rejects ids outside it (D31), so no hand-forged id can be persisted.
|
|
379
|
+
*/
|
|
380
|
+
async commitDiscoveredModels(
|
|
381
|
+
access: AccessDecision | undefined,
|
|
382
|
+
providerId: string,
|
|
383
|
+
modelIds: readonly string[],
|
|
384
|
+
): Promise<DiscoverModelCommitResult> {
|
|
385
|
+
const a = requireTenantAnyScope(access, MODEL_WRITE_SCOPES);
|
|
386
|
+
if (modelIds.some((id) => typeof id !== "string" || id.trim().length === 0)) {
|
|
387
|
+
throw new ConsoleDomainError(
|
|
388
|
+
"invalid_request",
|
|
389
|
+
400,
|
|
390
|
+
"modelIds must be non-empty model ids",
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
if (modelIds.length === 0 || modelIds.length > DISCOVER_MODEL_COMMIT_MAX) {
|
|
394
|
+
throw new ConsoleDomainError(
|
|
395
|
+
"invalid_request",
|
|
396
|
+
400,
|
|
397
|
+
`modelIds must contain 1-${DISCOVER_MODEL_COMMIT_MAX} entries`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
const result = await config.store.commitDiscoveredModels(a.tenantId, providerId, modelIds);
|
|
402
|
+
await config.auditSink?.record({
|
|
403
|
+
access: a,
|
|
404
|
+
action: "provider.models.registered",
|
|
405
|
+
target: providerId,
|
|
406
|
+
detail: { modelIds: result.committed, source: "discover-commit" },
|
|
407
|
+
});
|
|
408
|
+
await config.snapshotInvalidator?.invalidate();
|
|
409
|
+
return { ok: true, ...result };
|
|
410
|
+
} catch (error) {
|
|
411
|
+
throw translateDiscoveryError(error);
|
|
412
|
+
}
|
|
413
|
+
},
|
|
302
414
|
async deleteModelsBulk(
|
|
303
415
|
access: AccessDecision | undefined,
|
|
304
416
|
providerId: string,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ConsoleDomainError,
|
|
16
16
|
requireGlobalAdmin,
|
|
17
17
|
requireTenantAnyScope,
|
|
18
|
+
requireTenantScope,
|
|
18
19
|
} from "../../shared/errors";
|
|
19
20
|
import { cachedReadWithEtag, conditionalResponse } from "../../shared/read-through-cache";
|
|
20
21
|
import type { RedisClient } from "../../../persistence/redis";
|
|
@@ -24,10 +25,11 @@ import { isBundledProviderId } from "../../../providers/provider-contracts";
|
|
|
24
25
|
import type { ProviderRegistry } from "../../../providers/provider-registry";
|
|
25
26
|
import { getProviderClientVersion, getRouteIdentityVersions } from "../../../providers/operations/client-versions";
|
|
26
27
|
import type { OAuthLoginField } from "../../../providers/authentication/oauth-flow-store";
|
|
27
|
-
import { providerCredentialHint, providerCredentialUrl, providerDisplayName, providerHasAdapterUserAgent, providerOperatorNotice, providerServiceKinds, type CompatibilityProfile } from "../../../providers/provider-metadata";
|
|
28
|
+
import { providerCredentialHint, providerCredentialUrl, providerDisplayName, providerHasAdapterUserAgent, providerOperatorNotice, providerServiceKinds, customProviderServiceKinds, type CompatibilityProfile } from "../../../providers/provider-metadata";
|
|
28
29
|
import type { AccountHealthEventRecord } from "../../../providers/operations/account-health-service";
|
|
29
30
|
import { healthEventBuffer } from "../../../persistence/health-event-flush";
|
|
30
|
-
import { CREDENTIAL_MODES, PROVIDER_READ_SCOPES, PROVIDER_WRITE_SCOPES, type BuddyGrowthTaskLedgerEntry } from "./contracts";
|
|
31
|
+
import { CREDENTIAL_MODES, PROVIDER_READ_SCOPES, PROVIDER_WRITE_SCOPES, type BuddyGrowthTaskLedgerEntry, type ProviderAccountBulkActionResult, type RefreshAccountTokenResponse } from "./contracts";
|
|
32
|
+
import type { RefreshAccountStatusOutcome } from "../../../providers/operations/account-refresh-status";
|
|
31
33
|
import { enqueueCheckin } from "../../../workers/daily-checkin";
|
|
32
34
|
import {
|
|
33
35
|
isWireFamily,
|
|
@@ -87,12 +89,16 @@ export function sanitizeProviderResponse(
|
|
|
87
89
|
const credentialHint = providerCredentialHint(p.providerId);
|
|
88
90
|
const operatorNotice = providerOperatorNotice(p.providerId);
|
|
89
91
|
const clientVersion = getProviderClientVersion(p.providerId);
|
|
92
|
+
const compatibilityProfile = p.compatibilityProfile as CompatibilityProfile | undefined;
|
|
93
|
+
const isBuiltIn = (p.isBuiltIn as boolean | undefined) ?? false;
|
|
90
94
|
const response: ProviderResponse = {
|
|
91
95
|
providerId: p.providerId,
|
|
92
|
-
serviceKinds:
|
|
96
|
+
serviceKinds: isBuiltIn
|
|
97
|
+
? providerServiceKinds(p.providerId)
|
|
98
|
+
: customProviderServiceKinds(compatibilityProfile),
|
|
93
99
|
displayName: providerDisplayName(p.providerId),
|
|
94
100
|
enabled: (p.enabled as boolean | undefined) ?? true,
|
|
95
|
-
isBuiltIn
|
|
101
|
+
isBuiltIn,
|
|
96
102
|
requiresAccount: (p.requiresAccount as boolean | undefined) ?? true,
|
|
97
103
|
hasAdapterUserAgent: providerHasAdapterUserAgent(p.providerId),
|
|
98
104
|
// No model advertises search; see the note in provider-metadata.ts.
|
|
@@ -102,7 +108,7 @@ export function sanitizeProviderResponse(
|
|
|
102
108
|
...(operatorNotice === undefined ? {} : { operatorNotice }),
|
|
103
109
|
...(clientVersion === undefined ? {} : { clientVersion }),
|
|
104
110
|
routeIdentityVersions: getRouteIdentityVersions(),
|
|
105
|
-
supportsModelDiscovery: (p.supportsModelDiscovery as boolean | undefined) ?? !
|
|
111
|
+
supportsModelDiscovery: (p.supportsModelDiscovery as boolean | undefined) ?? !isBuiltIn,
|
|
106
112
|
...(typeof p.createdAt === "string" ? { createdAt: p.createdAt } : {}),
|
|
107
113
|
...(typeof p.updatedAt === "string" ? { updatedAt: p.updatedAt } : {}),
|
|
108
114
|
};
|
|
@@ -182,6 +188,15 @@ export interface ProviderCatalogConfig {
|
|
|
182
188
|
providerId: string,
|
|
183
189
|
accountId: string,
|
|
184
190
|
) => Promise<{ readonly accessToken: string; readonly refreshToken?: string }>;
|
|
191
|
+
/**
|
|
192
|
+
* One-shot manual OAuth token refresh (area 04). Provided by the composition
|
|
193
|
+
* root; when absent the refresh-token route answers 501 rather than silently
|
|
194
|
+
* reporting success.
|
|
195
|
+
*/
|
|
196
|
+
readonly refreshAccountStatus?: (
|
|
197
|
+
accountId: string,
|
|
198
|
+
opts?: { readonly origin?: "ui" | "sweep" | "dispatch" },
|
|
199
|
+
) => Promise<RefreshAccountStatusOutcome>;
|
|
185
200
|
}
|
|
186
201
|
async function attachProviderCapabilities(
|
|
187
202
|
response: ProviderResponse,
|
|
@@ -715,6 +730,101 @@ export function createProviderCatalogOperations(config: ProviderCatalogConfig) {
|
|
|
715
730
|
await config.snapshotInvalidator?.invalidate();
|
|
716
731
|
return { success: true };
|
|
717
732
|
},
|
|
733
|
+
/**
|
|
734
|
+
* One UPDATE over the tenant's cooling accounts, with `cooldownUntil` and
|
|
735
|
+
* `modelCooldowns` preserved as evidence (D07/D08). Shared rows are not
|
|
736
|
+
* writable here: the platform-admin pair of routes owns them (D10).
|
|
737
|
+
*/
|
|
738
|
+
async disableCooldowns(
|
|
739
|
+
access: AccessDecision | undefined,
|
|
740
|
+
providerId: string,
|
|
741
|
+
): Promise<ProviderAccountBulkActionResult> {
|
|
742
|
+
const a = requireTenantAnyScope(access, PROVIDER_WRITE_SCOPES);
|
|
743
|
+
const result = await config.store.disableCooldowns(a.tenantId, providerId);
|
|
744
|
+
await config.auditSink?.record({
|
|
745
|
+
access: a,
|
|
746
|
+
action: "provider_account.bulk_disabled_cooldowns",
|
|
747
|
+
target: providerId,
|
|
748
|
+
detail: { providerId, updated: result.updated, globalSkipped: result.globalSkipped },
|
|
749
|
+
});
|
|
750
|
+
await config.snapshotInvalidator?.invalidate();
|
|
751
|
+
return { ok: true, updated: result.updated, globalSkipped: result.globalSkipped };
|
|
752
|
+
},
|
|
753
|
+
/**
|
|
754
|
+
* One DELETE over the tenant's strictly banned accounts (D12). Empty
|
|
755
|
+
* matches are a successful no-op, so a second click is idempotent.
|
|
756
|
+
*/
|
|
757
|
+
async deleteBanned(
|
|
758
|
+
access: AccessDecision | undefined,
|
|
759
|
+
providerId: string,
|
|
760
|
+
): Promise<ProviderAccountBulkActionResult> {
|
|
761
|
+
const a = requireTenantAnyScope(access, PROVIDER_WRITE_SCOPES);
|
|
762
|
+
const result = await config.store.deleteBanned(a.tenantId, providerId);
|
|
763
|
+
await config.auditSink?.record({
|
|
764
|
+
access: a,
|
|
765
|
+
action: "provider_account.bulk_deleted_banned",
|
|
766
|
+
target: providerId,
|
|
767
|
+
detail: {
|
|
768
|
+
providerId,
|
|
769
|
+
deleted: result.deleted ?? [],
|
|
770
|
+
missing: result.missing ?? [],
|
|
771
|
+
globalSkipped: result.globalSkipped,
|
|
772
|
+
},
|
|
773
|
+
});
|
|
774
|
+
await config.snapshotInvalidator?.invalidate();
|
|
775
|
+
return {
|
|
776
|
+
ok: true,
|
|
777
|
+
updated: 0,
|
|
778
|
+
deleted: result.deleted ?? [],
|
|
779
|
+
missing: result.missing ?? [],
|
|
780
|
+
globalSkipped: result.globalSkipped,
|
|
781
|
+
};
|
|
782
|
+
},
|
|
783
|
+
async refreshAccountToken(
|
|
784
|
+
access: AccessDecision | undefined,
|
|
785
|
+
providerId: string,
|
|
786
|
+
accountId: string,
|
|
787
|
+
): Promise<RefreshAccountTokenResponse> {
|
|
788
|
+
// The refresh changes no routing state and is a per-account operator
|
|
789
|
+
// action, so the browser session's own write scope is sufficient.
|
|
790
|
+
const a = requireTenantScope(access, "dashboard:write");
|
|
791
|
+
// 404 unless the account exists and belongs to this provider/tenant.
|
|
792
|
+
const accounts = await config.store.listAccounts(a.tenantId, providerId);
|
|
793
|
+
const owned = accounts.find((account) => account.id === accountId && account.tenantId === a.tenantId);
|
|
794
|
+
if (!owned)
|
|
795
|
+
throw new ConsoleDomainError("account_not_found", 404, `Account ${accountId} not found`);
|
|
796
|
+
// 409 for a static token (nothing to re-mint), 400 for non-OAuth —
|
|
797
|
+
// before calling the refresher, so the row is never touched on a
|
|
798
|
+
// rejectable request.
|
|
799
|
+
if (owned.staticToken === true)
|
|
800
|
+
throw new ConsoleDomainError("static_token", 409, "Account carries a static token that cannot be refreshed");
|
|
801
|
+
if (owned.credentialKind !== "oauth")
|
|
802
|
+
throw new ConsoleDomainError("not_oauth", 400, "Account is not OAuth; token refresh is not applicable");
|
|
803
|
+
if (!config.refreshAccountStatus)
|
|
804
|
+
throw new ConsoleDomainError("refresh_unavailable", 501, "Token refresh is not configured on this host");
|
|
805
|
+
const outcome = await config.refreshAccountStatus(accountId, { origin: "ui" });
|
|
806
|
+
await config.auditSink?.record({
|
|
807
|
+
access: a,
|
|
808
|
+
action: "provider_account.token_refreshed",
|
|
809
|
+
target: accountId,
|
|
810
|
+
detail: {
|
|
811
|
+
providerId,
|
|
812
|
+
ok: outcome.ok,
|
|
813
|
+
statusAfter: outcome.statusAfter,
|
|
814
|
+
...(outcome.error === undefined ? {} : { error: outcome.error }),
|
|
815
|
+
},
|
|
816
|
+
});
|
|
817
|
+
await config.snapshotInvalidator?.invalidate();
|
|
818
|
+
return {
|
|
819
|
+
ok: outcome.ok,
|
|
820
|
+
statusAfter: outcome.statusAfter,
|
|
821
|
+
...(outcome.tokenRefreshed ? { tokenRefreshed: true } : {}),
|
|
822
|
+
...(outcome.expiresAt === undefined ? {} : { expiresAt: outcome.expiresAt }),
|
|
823
|
+
...(outcome.refreshedAt === undefined ? {} : { refreshedAt: outcome.refreshedAt }),
|
|
824
|
+
...(outcome.error === undefined ? {} : { error: outcome.error }),
|
|
825
|
+
...(outcome.category === undefined ? {} : { category: outcome.category }),
|
|
826
|
+
};
|
|
827
|
+
},
|
|
718
828
|
/**
|
|
719
829
|
* Exports the requested accounts with their decrypted credentials.
|
|
720
830
|
*
|
|
@@ -778,9 +888,7 @@ export function createProviderCatalogOperations(config: ProviderCatalogConfig) {
|
|
|
778
888
|
// channel check (11128) rejects the imported account.
|
|
779
889
|
const materialized =
|
|
780
890
|
account.providerId === "cb" ||
|
|
781
|
-
account.providerId === "cbcn"
|
|
782
|
-
account.providerId === "workbuddy" ||
|
|
783
|
-
account.providerId === "workbuddycn"
|
|
891
|
+
account.providerId === "cbcn"
|
|
784
892
|
// dynamic-import: B-02 lazy graph — buddy device-binding materializer, buddy-family only.
|
|
785
893
|
// The buddy-identity graph is lazy-loaded: the device-binding
|
|
786
894
|
// materializer only runs for buddy-family accounts (B-02).
|
|
Binary file
|
|
@@ -2,9 +2,10 @@ import { consoleErrorHandler } from "../../shared/errors";
|
|
|
2
2
|
import { literalUnion } from "../../shared/elysia-schema";
|
|
3
3
|
import { Elysia, t, type AnyElysia } from "elysia";
|
|
4
4
|
import { SERVICE_KINDS, WIRE_FAMILIES } from "../../../protocol/canonical-model";
|
|
5
|
-
import { PROBE_REASONING_EFFORTS } from "../../../providers/discovery/discovery-types";
|
|
5
|
+
import { PROBE_MODES, PROBE_REASONING_EFFORTS } from "../../../providers/discovery/discovery-types";
|
|
6
6
|
import {
|
|
7
7
|
ACCOUNT_STATUSES,
|
|
8
|
+
DISCOVER_MODEL_COMMIT_MAX,
|
|
8
9
|
CREDENTIAL_KINDS,
|
|
9
10
|
CREDENTIAL_MODES,
|
|
10
11
|
} from "./contracts";
|
|
@@ -18,6 +19,7 @@ import type {
|
|
|
18
19
|
ByokConnectionTestRequest,
|
|
19
20
|
CreateProviderAccountRequest,
|
|
20
21
|
CreateProviderRequest,
|
|
22
|
+
DiscoverModelCommitRequest,
|
|
21
23
|
ProbeModelRequest,
|
|
22
24
|
RegisterModelsRequest,
|
|
23
25
|
SetModelEnabledRequest,
|
|
@@ -42,6 +44,7 @@ const compatibilityProfileSchema = t.Object({
|
|
|
42
44
|
}),
|
|
43
45
|
),
|
|
44
46
|
),
|
|
47
|
+
service_kinds: t.Optional(t.Array(literalUnion(SERVICE_KINDS))),
|
|
45
48
|
streaming_usage_mode: t.Optional(t.Union([t.Literal("include_usage"), t.Literal("none")])),
|
|
46
49
|
structured_output: t.Optional(
|
|
47
50
|
t.Object({
|
|
@@ -110,12 +113,28 @@ const modelMetadataOverrideBody = t.Object({
|
|
|
110
113
|
image: t.Optional(t.Boolean()),
|
|
111
114
|
reasoning: t.Optional(t.Boolean()),
|
|
112
115
|
toolCall: t.Optional(t.Boolean()),
|
|
116
|
+
warnings: t.Optional(
|
|
117
|
+
t.Union([
|
|
118
|
+
t.Null(),
|
|
119
|
+
t.Array(
|
|
120
|
+
t.Object({
|
|
121
|
+
kind: t.String(),
|
|
122
|
+
message: t.String(),
|
|
123
|
+
severity: t.Optional(literalUnion(["info", "warn", "critical"])),
|
|
124
|
+
updatedAt: t.Optional(t.String()),
|
|
125
|
+
}),
|
|
126
|
+
),
|
|
127
|
+
]),
|
|
128
|
+
),
|
|
113
129
|
});
|
|
114
130
|
const registerModelsBody = t.Object({
|
|
115
131
|
modelIds: t.Array(t.String()),
|
|
116
132
|
wireFamily: t.Optional(t.String()),
|
|
117
133
|
metadata: t.Optional(modelMetadataOverrideBody),
|
|
118
134
|
});
|
|
135
|
+
const discoverCommitBody = t.Object({
|
|
136
|
+
modelIds: t.Array(t.String(), { minItems: 1, maxItems: DISCOVER_MODEL_COMMIT_MAX }),
|
|
137
|
+
});
|
|
119
138
|
const probeModelBody = t.Object({
|
|
120
139
|
modelId: t.String(),
|
|
121
140
|
serviceKind: t.Optional(literalUnion(SERVICE_KINDS)),
|
|
@@ -127,6 +146,7 @@ const probeModelBody = t.Object({
|
|
|
127
146
|
maxOutputTokens: t.Optional(t.Integer({ minimum: 1, maximum: 131072 })),
|
|
128
147
|
stream: t.Optional(t.Boolean()),
|
|
129
148
|
surface: t.Optional(literalUnion(["default", "cosy", "grpc", "ide"])),
|
|
149
|
+
mode: t.Optional(literalUnion(PROBE_MODES)),
|
|
130
150
|
});
|
|
131
151
|
const byokConnectionTestBody = t.Object({
|
|
132
152
|
baseUrl: t.String(),
|
|
@@ -275,9 +295,42 @@ export function createProviderCatalogRoutes(config: ProviderCatalogConfig): AnyE
|
|
|
275
295
|
);
|
|
276
296
|
},
|
|
277
297
|
)
|
|
278
|
-
.post(
|
|
279
|
-
|
|
280
|
-
})
|
|
298
|
+
.post(
|
|
299
|
+
"/:providerId/models/test-models",
|
|
300
|
+
{ body: t.Optional(t.Object({ mode: t.Optional(literalUnion(PROBE_MODES)) })) },
|
|
301
|
+
async ({ request, params, body }) => {
|
|
302
|
+
return await modelFactory.probeAllModels(
|
|
303
|
+
config.accessResolver(request),
|
|
304
|
+
params.providerId,
|
|
305
|
+
body?.mode,
|
|
306
|
+
);
|
|
307
|
+
},
|
|
308
|
+
)
|
|
309
|
+
// Discovery must be declared before `/:providerId/models/discover/commit`
|
|
310
|
+
// would be shadowed by a dynamic segment; Elysia resolves both here.
|
|
311
|
+
.get("/:providerId/models/discover", async ({ request, params }) => {
|
|
312
|
+
return await modelFactory.listDiscoveryCandidates(
|
|
313
|
+
config.accessResolver(request),
|
|
314
|
+
params.providerId,
|
|
315
|
+
);
|
|
316
|
+
})
|
|
317
|
+
.post(
|
|
318
|
+
"/:providerId/models/discover/commit",
|
|
319
|
+
{ body: discoverCommitBody },
|
|
320
|
+
async ({ request, params, body }) => {
|
|
321
|
+
return await modelFactory.commitDiscoveredModels(
|
|
322
|
+
config.accessResolver(request),
|
|
323
|
+
params.providerId,
|
|
324
|
+
(body as DiscoverModelCommitRequest).modelIds,
|
|
325
|
+
);
|
|
326
|
+
},
|
|
327
|
+
)
|
|
328
|
+
.post("/:providerId/accounts/actions/disable-cooldowns", async ({ request, params }) => {
|
|
329
|
+
return await factory.disableCooldowns(config.accessResolver(request), params.providerId);
|
|
330
|
+
})
|
|
331
|
+
.post("/:providerId/accounts/actions/delete-banned", async ({ request, params }) => {
|
|
332
|
+
return await factory.deleteBanned(config.accessResolver(request), params.providerId);
|
|
333
|
+
})
|
|
281
334
|
.post(
|
|
282
335
|
"/:providerId/accounts/probe",
|
|
283
336
|
{ body: probeModelBody },
|
|
@@ -408,6 +461,14 @@ export function createProviderCatalogRoutes(config: ProviderCatalogConfig): AnyE
|
|
|
408
461
|
params.accountId,
|
|
409
462
|
);
|
|
410
463
|
return recovered;
|
|
464
|
+
})
|
|
465
|
+
.post("/:providerId/accounts/:accountId/refresh-token", async ({ request, params }) => {
|
|
466
|
+
const refreshed = await factory.refreshAccountToken(
|
|
467
|
+
config.accessResolver(request),
|
|
468
|
+
params.providerId,
|
|
469
|
+
params.accountId,
|
|
470
|
+
);
|
|
471
|
+
return refreshed;
|
|
411
472
|
})
|
|
412
473
|
.post(
|
|
413
474
|
"/:providerId/accounts/export",
|