swayrouter 1.0.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 +43 -0
- package/CHANGELOG.md +132 -0
- package/LICENSE +30 -0
- package/README.id.md +633 -0
- package/README.md +628 -0
- package/cli/cli.js +388 -0
- package/cli/package.json +4 -0
- package/cli/src/cli/commands/xaiVideo.js +280 -0
- package/cli/src/cli/tray/SwayAvatar.svg +20 -0
- package/cli/src/cli/tray/autostart.js +351 -0
- package/cli/src/cli/tray/icon.ico +0 -0
- package/cli/src/cli/tray/icon.png +0 -0
- package/cli/src/cli/tray/tray.js +258 -0
- package/cli/src/cli/tray/tray.ps1 +129 -0
- package/cli/src/cli/tray/trayHost.js +157 -0
- package/cli/src/cli/tray/trayWin.js +83 -0
- package/cli/src/cli/utils/display.js +123 -0
- package/cli/src/cli/utils/input.js +138 -0
- package/cli/src/runtime/discover.js +153 -0
- package/cli/src/runtime/lifecycle.js +333 -0
- package/cli/src/runtime/runMenu.js +250 -0
- package/cli/src/runtime/uiAdapters.js +12 -0
- package/cli/src/runtime/update.js +130 -0
- package/dashboard/dist/assets/CliTools-Ch3gKuNw.js +24 -0
- package/dashboard/dist/assets/Combo-BBCldjWi.js +1 -0
- package/dashboard/dist/assets/ConsoleLog-wywAYcPT.js +6 -0
- package/dashboard/dist/assets/Dashboard-DxzyF41b.js +1 -0
- package/dashboard/dist/assets/Header-C_9Su9BF.js +1 -0
- package/dashboard/dist/assets/Keys-D0ufgcsc.js +1 -0
- package/dashboard/dist/assets/Login-BeKpipS_.js +1 -0
- package/dashboard/dist/assets/ManageApiKey-CVXsmW2B.js +1 -0
- package/dashboard/dist/assets/OAuthCallback-D_UZYcF6.js +1 -0
- package/dashboard/dist/assets/Provider-BU7JfTqm.js +4 -0
- package/dashboard/dist/assets/ProviderModelAccordion-BAifkfVR.js +1 -0
- package/dashboard/dist/assets/Proxy-CU15VJ--.js +1 -0
- package/dashboard/dist/assets/QuotaMonitor-HzGK7bcc.js +1 -0
- package/dashboard/dist/assets/Settings-Dt4y2dC0.js +1 -0
- package/dashboard/dist/assets/Setup-DSDUwU7N.js +1 -0
- package/dashboard/dist/assets/StatusBadge-1nAOyHU2.js +1 -0
- package/dashboard/dist/assets/SwayChat-Bup7up6D.js +10 -0
- package/dashboard/dist/assets/Usage-eWnrPQ_v.js +4 -0
- package/dashboard/dist/assets/accordion-DOOPDDgt.js +1 -0
- package/dashboard/dist/assets/admin-extras-api-CCdu5TFM.js +1 -0
- package/dashboard/dist/assets/api-keys-api-ifAC2Ok3.js +1 -0
- package/dashboard/dist/assets/card-CC0Omjcv.js +1 -0
- package/dashboard/dist/assets/chatStudio-C0zPz_Z_.js +2 -0
- package/dashboard/dist/assets/checkbox-7SZBrmbN.js +1 -0
- package/dashboard/dist/assets/connections-api-_N54UD3S.js +1 -0
- package/dashboard/dist/assets/copy-button-qBvM6H9z.js +1 -0
- package/dashboard/dist/assets/currency-api-SV9CBf5e.js +1 -0
- package/dashboard/dist/assets/flip-button-BGgkjtl0.js +1 -0
- package/dashboard/dist/assets/frame-cUiti6Lq.js +1 -0
- package/dashboard/dist/assets/icons-vendor-CUx7SB5V.js +541 -0
- package/dashboard/dist/assets/index-B6j-9zHy.js +10 -0
- package/dashboard/dist/assets/index-C-_l0yYz.css +1 -0
- package/dashboard/dist/assets/live-mode-CHShLY4E.js +1 -0
- package/dashboard/dist/assets/markdown-vendor-BY8e1gQ8.js +29 -0
- package/dashboard/dist/assets/motion-vendor-g5fpGzmJ.js +9 -0
- package/dashboard/dist/assets/oauth-callback-CqYQzyd7.js +1 -0
- package/dashboard/dist/assets/query-vendor-DVevaSVS.js +1 -0
- package/dashboard/dist/assets/react-vendor-xnYrwzsL.js +61 -0
- package/dashboard/dist/assets/ripple-button-l8aMiVVG.js +1 -0
- package/dashboard/dist/assets/segmented-CqmEnQjZ.js +1 -0
- package/dashboard/dist/assets/select-D8pUtMXp.js +1 -0
- package/dashboard/dist/assets/skeleton-7uJDiIXe.js +1 -0
- package/dashboard/dist/assets/table-D0M60Tu1.js +1 -0
- package/dashboard/dist/assets/tabs-C6YkuulG.js +1 -0
- package/dashboard/dist/assets/tunnel-api-DDPsBaDV.js +1 -0
- package/dashboard/dist/assets/ui-vendor-BuM9f2NY.js +17 -0
- package/dashboard/dist/image2/SwayAvatar.svg +20 -0
- package/dashboard/dist/index.html +32 -0
- package/dashboard/dist/logo/avatar.svg +1 -0
- package/dashboard/dist/logo/sway.svg +20 -0
- package/dashboard/dist/logo/swaychat.svg +20 -0
- package/dashboard/dist/providers/SwayCustom.svg +19 -0
- package/dashboard/dist/providers/alicode.svg +12 -0
- package/dashboard/dist/providers/anthropic.svg +11 -0
- package/dashboard/dist/providers/antigravity.svg +3 -0
- package/dashboard/dist/providers/azure.svg +5 -0
- package/dashboard/dist/providers/baidu.svg +3 -0
- package/dashboard/dist/providers/blackbox.svg +5 -0
- package/dashboard/dist/providers/byteplus.svg +3 -0
- package/dashboard/dist/providers/cerebras.svg +4 -0
- package/dashboard/dist/providers/chutes.svg +4 -0
- package/dashboard/dist/providers/claude.svg +3 -0
- package/dashboard/dist/providers/cline.svg +11 -0
- package/dashboard/dist/providers/cloudflare-ai.svg +4 -0
- package/dashboard/dist/providers/codebuddy.svg +11 -0
- package/dashboard/dist/providers/codex.svg +10 -0
- package/dashboard/dist/providers/commandcode.svg +3 -0
- package/dashboard/dist/providers/cursor.svg +3 -0
- package/dashboard/dist/providers/deepseek.svg +10 -0
- package/dashboard/dist/providers/featherless.svg +3 -0
- package/dashboard/dist/providers/fireworks.svg +3 -0
- package/dashboard/dist/providers/gemini.svg +3 -0
- package/dashboard/dist/providers/geminicli.svg +11 -0
- package/dashboard/dist/providers/github.svg +3 -0
- package/dashboard/dist/providers/grok.svg +3 -0
- package/dashboard/dist/providers/groq.svg +3 -0
- package/dashboard/dist/providers/hermesagent.svg +1 -0
- package/dashboard/dist/providers/kilocode.svg +15 -0
- package/dashboard/dist/providers/kimchi.svg +3 -0
- package/dashboard/dist/providers/kimi.svg +4 -0
- package/dashboard/dist/providers/kiro.svg +10 -0
- package/dashboard/dist/providers/meta.svg +3 -0
- package/dashboard/dist/providers/mimo.svg +10 -0
- package/dashboard/dist/providers/minimax.svg +3 -0
- package/dashboard/dist/providers/mistral.svg +3 -0
- package/dashboard/dist/providers/morph.svg +3 -0
- package/dashboard/dist/providers/nebius.svg +4 -0
- package/dashboard/dist/providers/nvidia.svg +3 -0
- package/dashboard/dist/providers/ollama.svg +3 -0
- package/dashboard/dist/providers/omp.svg +5 -0
- package/dashboard/dist/providers/openai.svg +3 -0
- package/dashboard/dist/providers/opencode.svg +3 -0
- package/dashboard/dist/providers/openrouter.svg +3 -0
- package/dashboard/dist/providers/perplexity.svg +3 -0
- package/dashboard/dist/providers/poolside.svg +36 -0
- package/dashboard/dist/providers/qoder.svg +11 -0
- package/dashboard/dist/providers/sumopod.svg +3 -0
- package/dashboard/dist/providers/tencent.svg +5 -0
- package/dashboard/dist/providers/together.svg +12 -0
- package/dashboard/dist/providers/trae.svg +3 -0
- package/dashboard/dist/providers/venice.svg +4 -0
- package/dashboard/dist/providers/vercel.svg +3 -0
- package/dashboard/dist/providers/vertex.svg +17 -0
- package/dashboard/dist/providers/windsurf.svg +10 -0
- package/dashboard/dist/providers/zai.svg +3 -0
- package/dashboard/tsconfig.json +13 -0
- package/package.json +87 -0
- package/src/config/appConstants.js +153 -0
- package/src/config/defaultThinkingSignature.js +7 -0
- package/src/config/errorConfig.js +117 -0
- package/src/config/grokCli.js +9 -0
- package/src/config/kiroConstants.js +235 -0
- package/src/config/mediaConfig.js +20 -0
- package/src/config/ollamaModels.js +18 -0
- package/src/config/providerModels.js +109 -0
- package/src/config/providers.js +17 -0
- package/src/config/runtimeConfig.js +84 -0
- package/src/dashboardGuard.js +254 -0
- package/src/executors/antigravity.js +389 -0
- package/src/executors/azure.js +49 -0
- package/src/executors/base.js +225 -0
- package/src/executors/codebuddy-cn.js +23 -0
- package/src/executors/codebuddy-intl.js +60 -0
- package/src/executors/codex.js +436 -0
- package/src/executors/commandcode.js +83 -0
- package/src/executors/cursor.js +1075 -0
- package/src/executors/default.js +294 -0
- package/src/executors/gemini-cli.js +88 -0
- package/src/executors/github.js +393 -0
- package/src/executors/grok-cli.js +510 -0
- package/src/executors/grok-web.js +342 -0
- package/src/executors/index.js +86 -0
- package/src/executors/kimchi.js +116 -0
- package/src/executors/kiro.js +1155 -0
- package/src/executors/ollama-local.js +14 -0
- package/src/executors/opencode-go.js +47 -0
- package/src/executors/opencode.js +31 -0
- package/src/executors/qoder.js +433 -0
- package/src/executors/trae.js +318 -0
- package/src/executors/vertex.js +151 -0
- package/src/executors/windsurf.js +555 -0
- package/src/executors/xiaomi-tokenplan.js +16 -0
- package/src/handlers/chatCore/nonStreamingHandler.js +370 -0
- package/src/handlers/chatCore/requestDetail.js +130 -0
- package/src/handlers/chatCore/sseToJsonHandler.js +318 -0
- package/src/handlers/chatCore/streamingHandler.js +250 -0
- package/src/handlers/chatCore.js +652 -0
- package/src/lib/auth/dashboardSession.js +83 -0
- package/src/lib/auth/loginLimiter.js +99 -0
- package/src/lib/cache/ttlCache.js +153 -0
- package/src/lib/consoleLogBuffer.js +127 -0
- package/src/lib/dataDir.js +45 -0
- package/src/lib/db/adapters/betterSqliteAdapter.js +36 -0
- package/src/lib/db/adapters/bunSqliteAdapter.js +47 -0
- package/src/lib/db/adapters/nodeSqliteAdapter.js +65 -0
- package/src/lib/db/adapters/runtimeHelpers.js +23 -0
- package/src/lib/db/adapters/sqljsAdapter.js +103 -0
- package/src/lib/db/backup.js +75 -0
- package/src/lib/db/driver.js +110 -0
- package/src/lib/db/helpers/jsonCol.js +18 -0
- package/src/lib/db/helpers/kvStore.js +48 -0
- package/src/lib/db/helpers/metaStore.js +32 -0
- package/src/lib/db/index.js +473 -0
- package/src/lib/db/migrate.js +267 -0
- package/src/lib/db/migrations/001-initial.js +12 -0
- package/src/lib/db/migrations/002-prune-headroom-pxpipe-settings.js +32 -0
- package/src/lib/db/migrations/004-request-details-trace-id.js +12 -0
- package/src/lib/db/migrations/005-request-details-payload-capture.js +11 -0
- package/src/lib/db/migrations/007-headroom-readd-settings.js +35 -0
- package/src/lib/db/migrations/008-account-model-locks.js +22 -0
- package/src/lib/db/migrations/009-warp-pool-lifecycle.js +5 -0
- package/src/lib/db/migrations/010-api-key-file-permissions.js +7 -0
- package/src/lib/db/migrations/011-remove-upstream-user-agent-profile.js +41 -0
- package/src/lib/db/migrations/012-remove-headroom-settings.js +33 -0
- package/src/lib/db/migrations/013-default-fill-first-routing.js +20 -0
- package/src/lib/db/migrations/014-normalize-fill-first-routing.js +37 -0
- package/src/lib/db/migrations/015-remove-ip-ban-tables.js +8 -0
- package/src/lib/db/migrations/016-prune-legacy-codebuddy-intl-models.js +25 -0
- package/src/lib/db/migrations/index.js +20 -0
- package/src/lib/db/nineRouterMigration.js +785 -0
- package/src/lib/db/paths.js +23 -0
- package/src/lib/db/repos/accountModelLocksRepo.js +74 -0
- package/src/lib/db/repos/aliasRepo.js +108 -0
- package/src/lib/db/repos/apiKeysRepo.js +204 -0
- package/src/lib/db/repos/combosRepo.js +85 -0
- package/src/lib/db/repos/connectionsRepo.js +522 -0
- package/src/lib/db/repos/disabledModelsRepo.js +63 -0
- package/src/lib/db/repos/nodesRepo.js +127 -0
- package/src/lib/db/repos/pricingRepo.js +108 -0
- package/src/lib/db/repos/proxyPoolsRepo.js +104 -0
- package/src/lib/db/repos/requestDetailsRepo.js +345 -0
- package/src/lib/db/repos/settingsRepo.js +135 -0
- package/src/lib/db/repos/usageRepo.js +1335 -0
- package/src/lib/db/schema.js +179 -0
- package/src/lib/db/version.js +32 -0
- package/src/lib/db/writeBehind.js +174 -0
- package/src/lib/disabledModelsDb.js +3 -0
- package/src/lib/env.ts +175 -0
- package/src/lib/errors/publicError.js +18 -0
- package/src/lib/grokBuildConfig.js +243 -0
- package/src/lib/guardRequest.js +51 -0
- package/src/lib/localDb.js +21 -0
- package/src/lib/mcp/stdioSseBridge.js +174 -0
- package/src/lib/network/connectionProxy.js +200 -0
- package/src/lib/network/proxyTest.js +158 -0
- package/src/lib/network/proxyUrl.js +76 -0
- package/src/lib/network/relay.js +71 -0
- package/src/lib/oauth/constants/oauth.js +152 -0
- package/src/lib/oauth/constants/xai.js +43 -0
- package/src/lib/oauth/kiroExternalIdp.js +167 -0
- package/src/lib/oauth/providerHelpers.js +90 -0
- package/src/lib/oauth/providers/_shared.js +12 -0
- package/src/lib/oauth/providers/antigravity.js +126 -0
- package/src/lib/oauth/providers/claude.js +60 -0
- package/src/lib/oauth/providers/cline.js +62 -0
- package/src/lib/oauth/providers/clinepass.js +62 -0
- package/src/lib/oauth/providers/codebuddy-cn.js +75 -0
- package/src/lib/oauth/providers/codebuddy-intl.js +73 -0
- package/src/lib/oauth/providers/codex.js +72 -0
- package/src/lib/oauth/providers/cursor.js +18 -0
- package/src/lib/oauth/providers/gemini-cli.js +84 -0
- package/src/lib/oauth/providers/github.js +96 -0
- package/src/lib/oauth/providers/grok-cli.js +123 -0
- package/src/lib/oauth/providers/index.js +233 -0
- package/src/lib/oauth/providers/kilocode.js +60 -0
- package/src/lib/oauth/providers/kimchi.js +79 -0
- package/src/lib/oauth/providers/kimi.js +79 -0
- package/src/lib/oauth/providers/kiro.js +147 -0
- package/src/lib/oauth/providers/qoder.js +90 -0
- package/src/lib/oauth/providers/trae.js +244 -0
- package/src/lib/oauth/providers/windsurf.js +123 -0
- package/src/lib/oauth/providers/xai.js +95 -0
- package/src/lib/oauth/providers.js +16 -0
- package/src/lib/oauth/services/cursor.js +132 -0
- package/src/lib/oauth/services/kiro.js +351 -0
- package/src/lib/oauth/services/oauth.js +135 -0
- package/src/lib/oauth/services/qoder.js +151 -0
- package/src/lib/oauth/services/xai.js +204 -0
- package/src/lib/oauth/utils/ideDetect.js +55 -0
- package/src/lib/oauth/utils/pkce.js +25 -0
- package/src/lib/oauth/utils/server.js +732 -0
- package/src/lib/oauth/utils/ui.js +43 -0
- package/src/lib/providerNormalization.js +40 -0
- package/src/lib/qoder/constants.js +1 -0
- package/src/lib/requestDetailsDb.js +3 -0
- package/src/lib/resourceProfile.ts +124 -0
- package/src/lib/runtimeSecrets.ts +130 -0
- package/src/lib/security/payloadCapture.js +87 -0
- package/src/lib/tunnel/cloudflare/cloudflared.js +436 -0
- package/src/lib/tunnel/cloudflare/config.js +8 -0
- package/src/lib/tunnel/cloudflare/healthCheck.js +37 -0
- package/src/lib/tunnel/cloudflare/manager.js +149 -0
- package/src/lib/tunnel/cloudflare/pid.js +31 -0
- package/src/lib/tunnel/cloudflare/publicUrl.js +41 -0
- package/src/lib/tunnel/cloudflare/requirements.js +12 -0
- package/src/lib/tunnel/index.js +26 -0
- package/src/lib/tunnel/shared/dnsResolver.js +16 -0
- package/src/lib/tunnel/shared/internetCheck.js +30 -0
- package/src/lib/tunnel/shared/state.js +45 -0
- package/src/lib/tunnel/shared/watchdogConfig.js +6 -0
- package/src/lib/update/installer.js +52 -0
- package/src/lib/update/version.js +116 -0
- package/src/lib/usageDb.js +6 -0
- package/src/lib/usageRefresh.js +88 -0
- package/src/lifecycle/appLifecycle.ts +99 -0
- package/src/models.js +36 -0
- package/src/next/headers.js +12 -0
- package/src/next/server.js +74 -0
- package/src/observability/metrics.js +439 -0
- package/src/observability/tracing.js +107 -0
- package/src/open-sse-index.js +70 -0
- package/src/providers/capabilities.js +299 -0
- package/src/providers/display.ts +142 -0
- package/src/providers/index.js +27 -0
- package/src/providers/models/helpers.js +19 -0
- package/src/providers/models/namePatterns.js +27 -0
- package/src/providers/models/schema.js +32 -0
- package/src/providers/pricing.js +248 -0
- package/src/providers/registry/agentrouter.js +52 -0
- package/src/providers/registry/alicode-intl.js +32 -0
- package/src/providers/registry/alicode.js +33 -0
- package/src/providers/registry/alims-intl.js +30 -0
- package/src/providers/registry/anthropic.js +29 -0
- package/src/providers/registry/antigravity.js +139 -0
- package/src/providers/registry/azure.js +23 -0
- package/src/providers/registry/baidu.js +35 -0
- package/src/providers/registry/blackbox.js +41 -0
- package/src/providers/registry/byteplus.js +36 -0
- package/src/providers/registry/cerebras.js +33 -0
- package/src/providers/registry/chutes.js +26 -0
- package/src/providers/registry/claude.js +80 -0
- package/src/providers/registry/cline.js +53 -0
- package/src/providers/registry/clinepass.js +62 -0
- package/src/providers/registry/cloudflare-ai.js +84 -0
- package/src/providers/registry/codebuddy-cn.js +95 -0
- package/src/providers/registry/codebuddy-intl.js +91 -0
- package/src/providers/registry/codex.js +145 -0
- package/src/providers/registry/commandcode.js +45 -0
- package/src/providers/registry/cursor.js +61 -0
- package/src/providers/registry/deepseek.js +55 -0
- package/src/providers/registry/featherless.js +36 -0
- package/src/providers/registry/fireworks.js +34 -0
- package/src/providers/registry/gemini-cli.js +59 -0
- package/src/providers/registry/gemini.js +76 -0
- package/src/providers/registry/github.js +131 -0
- package/src/providers/registry/glm-cn.js +35 -0
- package/src/providers/registry/glm.js +58 -0
- package/src/providers/registry/grok-cli.js +87 -0
- package/src/providers/registry/grok-web.js +41 -0
- package/src/providers/registry/groq.js +38 -0
- package/src/providers/registry/index.js +77 -0
- package/src/providers/registry/kilo-gateway.js +34 -0
- package/src/providers/registry/kilocode.js +48 -0
- package/src/providers/registry/kimchi.js +49 -0
- package/src/providers/registry/kimi.js +82 -0
- package/src/providers/registry/kiro.js +132 -0
- package/src/providers/registry/meta.js +34 -0
- package/src/providers/registry/minimax-cn.js +90 -0
- package/src/providers/registry/minimax.js +90 -0
- package/src/providers/registry/mistral.js +36 -0
- package/src/providers/registry/morph.js +31 -0
- package/src/providers/registry/nebius.js +26 -0
- package/src/providers/registry/nvidia.js +55 -0
- package/src/providers/registry/ollama-local.js +18 -0
- package/src/providers/registry/ollama.js +38 -0
- package/src/providers/registry/openai.js +114 -0
- package/src/providers/registry/opencode-go.js +41 -0
- package/src/providers/registry/opencode.js +26 -0
- package/src/providers/registry/openrouter.js +36 -0
- package/src/providers/registry/perplexity.js +56 -0
- package/src/providers/registry/poolside.js +30 -0
- package/src/providers/registry/qoder.js +58 -0
- package/src/providers/registry/sumopod.js +84 -0
- package/src/providers/registry/tencent.js +29 -0
- package/src/providers/registry/together.js +38 -0
- package/src/providers/registry/trae.js +71 -0
- package/src/providers/registry/venice.js +69 -0
- package/src/providers/registry/vercel-ai-gateway.js +37 -0
- package/src/providers/registry/vertex-partner.js +31 -0
- package/src/providers/registry/vertex.js +33 -0
- package/src/providers/registry/windsurf.js +129 -0
- package/src/providers/registry/xai.js +46 -0
- package/src/providers/registry/xiaomi-mimo.js +66 -0
- package/src/providers/registry/xiaomi-tokenplan.js +58 -0
- package/src/providers/registryEntry.js +3 -0
- package/src/providers/schema.js +33 -0
- package/src/providers/shared.js +114 -0
- package/src/providers/thinkingLevels.js +49 -0
- package/src/routes/api/auth/login/route.js +82 -0
- package/src/routes/api/auth/logout/route.js +9 -0
- package/src/routes/api/auth/reset-password/route.js +12 -0
- package/src/routes/api/auth/status/route.js +33 -0
- package/src/routes/api/auth/verify-password/route.js +39 -0
- package/src/routes/api/chat/tools/curl/route.js +100 -0
- package/src/routes/api/chat/tools/router/route.js +215 -0
- package/src/routes/api/chat/tools/search/route.js +362 -0
- package/src/routes/api/chat/tools/shell/route.js +228 -0
- package/src/routes/api/cli-tools/all-statuses/route.js +37 -0
- package/src/routes/api/cli-tools/claude-settings/route.js +261 -0
- package/src/routes/api/cli-tools/codex-settings/route.js +220 -0
- package/src/routes/api/cli-tools/cowork-mcp-registry/route.js +80 -0
- package/src/routes/api/cli-tools/cowork-mcp-tools/route.js +96 -0
- package/src/routes/api/cli-tools/cowork-settings/route.js +380 -0
- package/src/routes/api/cli-tools/grok-build-settings/route.js +152 -0
- package/src/routes/api/cli-tools/hermes-settings/route.js +169 -0
- package/src/routes/api/cli-tools/kilo-settings/route.js +131 -0
- package/src/routes/api/cli-tools/model-mappings/route.js +43 -0
- package/src/routes/api/cli-tools/omp-settings/route.js +266 -0
- package/src/routes/api/cli-tools/opencode-settings/route.js +243 -0
- package/src/routes/api/combos/[id]/route.js +73 -0
- package/src/routes/api/combos/route.js +43 -0
- package/src/routes/api/health/live/route.js +7 -0
- package/src/routes/api/health/ready/route.js +19 -0
- package/src/routes/api/health/route.js +17 -0
- package/src/routes/api/inflight/stream/route.js +24 -0
- package/src/routes/api/init/route.js +3 -0
- package/src/routes/api/keys/[id]/route.js +71 -0
- package/src/routes/api/keys/route.js +41 -0
- package/src/routes/api/logs/route.js +19 -0
- package/src/routes/api/mcp/[plugin]/message/route.js +23 -0
- package/src/routes/api/mcp/[plugin]/sse/route.js +35 -0
- package/src/routes/api/models/availability/route.js +104 -0
- package/src/routes/api/models/custom/route.js +58 -0
- package/src/routes/api/models/disabled/route.js +47 -0
- package/src/routes/api/models/route.js +64 -0
- package/src/routes/api/models/test/ping.js +236 -0
- package/src/routes/api/models/test/route.js +14 -0
- package/src/routes/api/oauth/[provider]/[action]/route.js +491 -0
- package/src/routes/api/oauth/codex/bulk-import/route.js +105 -0
- package/src/routes/api/oauth/codex/import-token/route.js +82 -0
- package/src/routes/api/oauth/cursor/auto-import/route.js +250 -0
- package/src/routes/api/oauth/cursor/import/route.js +86 -0
- package/src/routes/api/oauth/kiro/api-key/route.js +60 -0
- package/src/routes/api/oauth/kiro/auto-import/route.js +120 -0
- package/src/routes/api/oauth/kiro/import/route.js +72 -0
- package/src/routes/api/oauth/kiro/import-cli-proxy/route.js +42 -0
- package/src/routes/api/oauth/kiro/social-authorize/route.js +40 -0
- package/src/routes/api/oauth/kiro/social-exchange/route.js +62 -0
- package/src/routes/api/pricing/route.js +96 -0
- package/src/routes/api/provider-nodes/[id]/route.js +106 -0
- package/src/routes/api/provider-nodes/route.js +110 -0
- package/src/routes/api/provider-nodes/validate/route.js +197 -0
- package/src/routes/api/providers/[id]/models/route.js +716 -0
- package/src/routes/api/providers/[id]/route.js +195 -0
- package/src/routes/api/providers/[id]/test/route.js +27 -0
- package/src/routes/api/providers/[id]/test/testUtils.js +912 -0
- package/src/routes/api/providers/[id]/test-models/route.js +211 -0
- package/src/routes/api/providers/catalog/route.js +58 -0
- package/src/routes/api/providers/client/route.js +133 -0
- package/src/routes/api/providers/counts/route.js +43 -0
- package/src/routes/api/providers/kilo/free-models/route.js +58 -0
- package/src/routes/api/providers/route.js +361 -0
- package/src/routes/api/providers/suggested-models/filters.js +24 -0
- package/src/routes/api/providers/suggested-models/route.js +34 -0
- package/src/routes/api/providers/test-batch/route.js +145 -0
- package/src/routes/api/providers/validate/route.js +633 -0
- package/src/routes/api/proxy-pools/[id]/route.js +129 -0
- package/src/routes/api/proxy-pools/[id]/test/route.js +64 -0
- package/src/routes/api/proxy-pools/cloudflare-deploy/route.js +183 -0
- package/src/routes/api/proxy-pools/route.js +85 -0
- package/src/routes/api/proxy-pools/vercel-deploy/route.js +181 -0
- package/src/routes/api/settings/currency/route.js +29 -0
- package/src/routes/api/settings/database/path/route.js +8 -0
- package/src/routes/api/settings/database/route.js +89 -0
- package/src/routes/api/settings/migrate-9router/route.js +81 -0
- package/src/routes/api/settings/require-login/route.js +14 -0
- package/src/routes/api/settings/route.js +186 -0
- package/src/routes/api/shutdown/route.js +25 -0
- package/src/routes/api/tags/route.js +17 -0
- package/src/routes/api/translator/console-logs/route.js +28 -0
- package/src/routes/api/translator/console-logs/stream/route.js +95 -0
- package/src/routes/api/tunnel/disable/route.js +18 -0
- package/src/routes/api/tunnel/enable/route.js +32 -0
- package/src/routes/api/tunnel/status/route.js +23 -0
- package/src/routes/api/update/route.js +42 -0
- package/src/routes/api/usage/[connectionId]/codex-reset-credits/route.js +169 -0
- package/src/routes/api/usage/[connectionId]/route.js +222 -0
- package/src/routes/api/usage/chart/route.js +27 -0
- package/src/routes/api/usage/history/route.js +94 -0
- package/src/routes/api/usage/logs/route.js +14 -0
- package/src/routes/api/usage/providers/route.js +36 -0
- package/src/routes/api/usage/request-details/route.js +61 -0
- package/src/routes/api/usage/request-logs/route.js +15 -0
- package/src/routes/api/usage/stats/route.js +23 -0
- package/src/routes/api/usage/stream/route.js +78 -0
- package/src/routes/api/v1/api/chat/route.js +36 -0
- package/src/routes/api/v1/chat/completions/route.js +28 -0
- package/src/routes/api/v1/images/edits/route.js +70 -0
- package/src/routes/api/v1/images/generations/route.js +50 -0
- package/src/routes/api/v1/images/imageValidation.js +45 -0
- package/src/routes/api/v1/messages/count_tokens/route.js +87 -0
- package/src/routes/api/v1/messages/route.js +26 -0
- package/src/routes/api/v1/models/route.js +440 -0
- package/src/routes/api/v1/responses/compact/route.js +34 -0
- package/src/routes/api/v1/responses/route.js +26 -0
- package/src/routes/api/v1/route.js +1 -0
- package/src/routes/api/version/route.js +11 -0
- package/src/routes/table.js +1141 -0
- package/src/rtk/applyFilter.js +13 -0
- package/src/rtk/autodetect.js +104 -0
- package/src/rtk/caveman.js +6 -0
- package/src/rtk/cavemanPrompts.js +107 -0
- package/src/rtk/constants.js +51 -0
- package/src/rtk/filters/buildOutput.js +120 -0
- package/src/rtk/filters/dedupLog.js +43 -0
- package/src/rtk/filters/find.js +47 -0
- package/src/rtk/filters/gitDiff.js +90 -0
- package/src/rtk/filters/gitLog.js +88 -0
- package/src/rtk/filters/gitStatus.js +101 -0
- package/src/rtk/filters/grep.js +45 -0
- package/src/rtk/filters/ls.js +73 -0
- package/src/rtk/filters/readNumbered.js +22 -0
- package/src/rtk/filters/searchList.js +49 -0
- package/src/rtk/filters/smartTruncate.js +13 -0
- package/src/rtk/filters/tree.js +28 -0
- package/src/rtk/index.js +143 -0
- package/src/rtk/ponytail.js +6 -0
- package/src/rtk/ponytailPrompt.js +49 -0
- package/src/rtk/systemInject.js +89 -0
- package/src/server.ts +474 -0
- package/src/services/accountFallback.js +241 -0
- package/src/services/accountPool.js +389 -0
- package/src/services/capacityAdapter.js +140 -0
- package/src/services/cliModelMapping.js +84 -0
- package/src/services/clinepassModels.js +60 -0
- package/src/services/codebuddyModels.js +250 -0
- package/src/services/combo.js +482 -0
- package/src/services/copilotModels.js +126 -0
- package/src/services/currencyRate.js +56 -0
- package/src/services/cursorModels.js +197 -0
- package/src/services/grokCliModels.js +130 -0
- package/src/services/kimchiModels.js +175 -0
- package/src/services/kiroModels.js +246 -0
- package/src/services/model.js +83 -0
- package/src/services/oauthCredentialManager.js +153 -0
- package/src/services/projectId.js +240 -0
- package/src/services/provider.js +145 -0
- package/src/services/qoderModels.js +274 -0
- package/src/services/tokenRefresh/dedup.js +70 -0
- package/src/services/tokenRefresh/providers.js +626 -0
- package/src/services/tokenRefresh.js +274 -0
- package/src/services/usage/claude.js +134 -0
- package/src/services/usage/codebuddy-cn.js +212 -0
- package/src/services/usage/codex.js +193 -0
- package/src/services/usage/deepseek.js +103 -0
- package/src/services/usage/github.js +94 -0
- package/src/services/usage/google.js +218 -0
- package/src/services/usage/grok-cli.js +368 -0
- package/src/services/usage/grokCliQuotaFrame.js +170 -0
- package/src/services/usage/kimi.js +185 -0
- package/src/services/usage/kiro.js +172 -0
- package/src/services/usage/minimax.js +215 -0
- package/src/services/usage/misc.js +264 -0
- package/src/services/usage/shared.js +59 -0
- package/src/services/usage.js +56 -0
- package/src/shared/clineAuth.js +37 -0
- package/src/shared/constants/config.js +80 -0
- package/src/shared/constants/coworkPlugins.js +69 -0
- package/src/shared/constants/models.js +41 -0
- package/src/shared/constants/providers.js +128 -0
- package/src/shared/constants/providersDisplay.js +1 -0
- package/src/shared/qoder/constants.js +50 -0
- package/src/shared/qoder/cosy.js +134 -0
- package/src/shared/qoder/encoding.js +36 -0
- package/src/shared/services/initializeApp.js +226 -0
- package/src/shared/services/quotaAutoPing.js +310 -0
- package/src/shared/utils/apiKey.js +65 -0
- package/src/shared/utils/clineAuth.js +1 -0
- package/src/shared/utils/index.js +3 -0
- package/src/shared/utils/machineId.js +60 -0
- package/src/shared/utils/ssrfGuard.js +280 -0
- package/src/sse/handlers/chat.js +426 -0
- package/src/sse/oauthLease.js +32 -0
- package/src/sse/recovery.js +235 -0
- package/src/sse/services/auth.js +489 -0
- package/src/sse/services/backgroundTokenRefresh.js +173 -0
- package/src/sse/services/model.js +129 -0
- package/src/sse/services/requestCancellation.js +4 -0
- package/src/sse/services/tokenRefresh.js +264 -0
- package/src/sse/utils/logger.js +142 -0
- package/src/staticServe.ts +95 -0
- package/src/transformer/streamToJsonConverter.js +87 -0
- package/src/translator/canonical.js +81 -0
- package/src/translator/concerns/chunk.js +9 -0
- package/src/translator/concerns/compatFallback.js +147 -0
- package/src/translator/concerns/finishReason.js +59 -0
- package/src/translator/concerns/image.js +105 -0
- package/src/translator/concerns/json.js +4 -0
- package/src/translator/concerns/kiroConversation.js +428 -0
- package/src/translator/concerns/message.js +5 -0
- package/src/translator/concerns/modality.js +134 -0
- package/src/translator/concerns/paramSupport.js +97 -0
- package/src/translator/concerns/prefetch.js +86 -0
- package/src/translator/concerns/reasoning.js +18 -0
- package/src/translator/concerns/thinking.js +40 -0
- package/src/translator/concerns/thinkingUnified.js +319 -0
- package/src/translator/concerns/toolCall.js +134 -0
- package/src/translator/concerns/usage.js +61 -0
- package/src/translator/detection.js +42 -0
- package/src/translator/formats/claude.js +323 -0
- package/src/translator/formats/gemini.js +366 -0
- package/src/translator/formats/maxTokens.js +19 -0
- package/src/translator/formats/openai.js +116 -0
- package/src/translator/formats/responsesApi.js +117 -0
- package/src/translator/formats.js +28 -0
- package/src/translator/index.js +237 -0
- package/src/translator/request/antigravity-to-openai.js +217 -0
- package/src/translator/request/claude-to-kiro.js +288 -0
- package/src/translator/request/claude-to-openai.js +199 -0
- package/src/translator/request/gemini-to-openai.js +142 -0
- package/src/translator/request/openai-responses.js +373 -0
- package/src/translator/request/openai-to-claude.js +260 -0
- package/src/translator/request/openai-to-commandcode.js +161 -0
- package/src/translator/request/openai-to-cursor.js +175 -0
- package/src/translator/request/openai-to-gemini.js +422 -0
- package/src/translator/request/openai-to-kiro.js +360 -0
- package/src/translator/request/openai-to-ollama.js +152 -0
- package/src/translator/request/openai-to-vertex.js +36 -0
- package/src/translator/response/claude-to-openai.js +185 -0
- package/src/translator/response/commandcode-to-openai.js +164 -0
- package/src/translator/response/cursor-to-openai.js +18 -0
- package/src/translator/response/gemini-to-openai.js +127 -0
- package/src/translator/response/kiro-to-claude.js +230 -0
- package/src/translator/response/kiro-to-openai.js +136 -0
- package/src/translator/response/ollama-to-openai.js +106 -0
- package/src/translator/response/openai-responses.js +534 -0
- package/src/translator/response/openai-to-antigravity.js +107 -0
- package/src/translator/response/openai-to-claude.js +231 -0
- package/src/translator/schema/blocks.js +40 -0
- package/src/translator/schema/defaults.js +3 -0
- package/src/translator/schema/finishReasons.js +22 -0
- package/src/translator/schema/index.js +7 -0
- package/src/translator/schema/roles.js +12 -0
- package/src/transport/requestBoundary.ts +223 -0
- package/src/transport/routerAdmission.ts +380 -0
- package/src/types/provider.ts +59 -0
- package/src/utils/bypassHandler.js +272 -0
- package/src/utils/claudeCloaking.js +10 -0
- package/src/utils/claudeSignature.js +35 -0
- package/src/utils/clientDetector.js +183 -0
- package/src/utils/cursorChecksum.js +104 -0
- package/src/utils/cursorProtobuf.js +801 -0
- package/src/utils/debugLog.js +12 -0
- package/src/utils/error.js +120 -0
- package/src/utils/inflight.js +127 -0
- package/src/utils/kiroSessionReplay.js +139 -0
- package/src/utils/ollamaTransform.js +83 -0
- package/src/utils/proxyFetch.js +308 -0
- package/src/utils/reasoningContentInjector.js +71 -0
- package/src/utils/requestLogger.js +272 -0
- package/src/utils/responsesStreamHelpers.js +46 -0
- package/src/utils/sessionManager.js +205 -0
- package/src/utils/sse.js +13 -0
- package/src/utils/sseConstants.js +21 -0
- package/src/utils/stream.js +511 -0
- package/src/utils/streamHandler.js +256 -0
- package/src/utils/streamHelpers.js +113 -0
- package/src/utils/toolDeduper.js +44 -0
- package/src/utils/usageTracking.js +329 -0
- package/tsconfig.json +45 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import { getAdapter } from "../driver.js";
|
|
3
|
+
import { parseJson, stringifyJson } from "../helpers/jsonCol.js";
|
|
4
|
+
|
|
5
|
+
const OPTIONAL_FIELDS = [
|
|
6
|
+
"displayName", "email", "globalPriority", "defaultModel",
|
|
7
|
+
"accessToken", "refreshToken", "expiresAt", "tokenType",
|
|
8
|
+
"scope", "projectId", "apiKey", "testStatus",
|
|
9
|
+
"lastTested", "lastError", "lastErrorAt", "rateLimitedUntil", "expiresIn", "errorCode",
|
|
10
|
+
"consecutiveUseCount", "idToken", "lastRefreshAt",
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function rowToConn(row) {
|
|
14
|
+
if (!row) return null;
|
|
15
|
+
const extra = parseJson(row.data, {});
|
|
16
|
+
return {
|
|
17
|
+
...extra,
|
|
18
|
+
id: row.id,
|
|
19
|
+
provider: row.provider,
|
|
20
|
+
authType: row.authType,
|
|
21
|
+
name: row.name,
|
|
22
|
+
email: row.email,
|
|
23
|
+
priority: row.priority,
|
|
24
|
+
isActive: row.isActive === 1 || row.isActive === true,
|
|
25
|
+
createdAt: row.createdAt,
|
|
26
|
+
updatedAt: row.updatedAt,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ROUTING_DATA_FIELDS = [
|
|
31
|
+
"lastUsedAt",
|
|
32
|
+
"consecutiveUseCount",
|
|
33
|
+
"deprioritizeUntil",
|
|
34
|
+
"backoffLevel",
|
|
35
|
+
"rateLimitedUntil",
|
|
36
|
+
"lastError",
|
|
37
|
+
"testStatus",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
function rowToRoutingConn(row) {
|
|
41
|
+
if (!row) return null;
|
|
42
|
+
const extra = parseJson(row.data, {}) || {};
|
|
43
|
+
const connection = {
|
|
44
|
+
id: row.id,
|
|
45
|
+
provider: row.provider,
|
|
46
|
+
authType: row.authType,
|
|
47
|
+
name: row.name,
|
|
48
|
+
email: row.email,
|
|
49
|
+
priority: row.priority,
|
|
50
|
+
isActive: row.isActive === 1 || row.isActive === true,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
for (const field of ROUTING_DATA_FIELDS) {
|
|
54
|
+
if (extra[field] !== undefined) connection[field] = extra[field];
|
|
55
|
+
}
|
|
56
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
57
|
+
if (key.startsWith("modelLock_") && value) connection[key] = value;
|
|
58
|
+
}
|
|
59
|
+
return connection;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function connToRow(c) {
|
|
63
|
+
const { id, provider, authType, name, email, priority, isActive, createdAt, updatedAt, ...rest } = c;
|
|
64
|
+
return {
|
|
65
|
+
id,
|
|
66
|
+
provider,
|
|
67
|
+
authType,
|
|
68
|
+
name: name ?? null,
|
|
69
|
+
email: email ?? null,
|
|
70
|
+
priority: priority ?? null,
|
|
71
|
+
isActive: isActive === false ? 0 : 1,
|
|
72
|
+
data: stringifyJson(rest),
|
|
73
|
+
createdAt,
|
|
74
|
+
updatedAt,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function upsert(db, c) {
|
|
79
|
+
const r = connToRow(c);
|
|
80
|
+
db.run(
|
|
81
|
+
`INSERT INTO providerConnections(id, provider, authType, name, email, priority, isActive, data, createdAt, updatedAt)
|
|
82
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
83
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
84
|
+
provider=excluded.provider, authType=excluded.authType, name=excluded.name,
|
|
85
|
+
email=excluded.email, priority=excluded.priority, isActive=excluded.isActive,
|
|
86
|
+
data=excluded.data, updatedAt=excluded.updatedAt`,
|
|
87
|
+
[r.id, r.provider, r.authType, r.name, r.email, r.priority, r.isActive, r.data, r.createdAt, r.updatedAt]
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function deriveConnectionName(data, fallbackName) {
|
|
92
|
+
if (data.provider === "github") {
|
|
93
|
+
return data.providerSpecificData?.githubLogin
|
|
94
|
+
|| data.providerSpecificData?.githubEmail
|
|
95
|
+
|| data.email
|
|
96
|
+
|| data.providerSpecificData?.githubName
|
|
97
|
+
|| fallbackName;
|
|
98
|
+
}
|
|
99
|
+
return fallbackName;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizeConnectionName(value) {
|
|
103
|
+
return typeof value === "string" ? value.trim() : "";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeApiKey(value) {
|
|
107
|
+
return typeof value === "string" ? value.trim() : "";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function conflictError(code, message) {
|
|
111
|
+
const error = new Error(message);
|
|
112
|
+
error.code = code;
|
|
113
|
+
return error;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function nextGeneratedConnectionName(existingRows, preferredName = "") {
|
|
117
|
+
const used = new Set(
|
|
118
|
+
existingRows
|
|
119
|
+
.map((connection) => normalizeConnectionName(connection.name).toLowerCase())
|
|
120
|
+
.filter(Boolean)
|
|
121
|
+
);
|
|
122
|
+
const match = normalizeConnectionName(preferredName).match(
|
|
123
|
+
/^(swayrouter-account|sway-account)(\d*)$/i
|
|
124
|
+
);
|
|
125
|
+
const base = match?.[1] || "swayrouter-account";
|
|
126
|
+
let suffix = match?.[2] ? Number(match[2]) : 0;
|
|
127
|
+
|
|
128
|
+
while (true) {
|
|
129
|
+
const candidate = `${base}${suffix || ""}`;
|
|
130
|
+
if (!used.has(candidate.toLowerCase())) return candidate;
|
|
131
|
+
suffix += 1;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function findDuplicateApiKey(existingRows, apiKey, ignoreId) {
|
|
136
|
+
const normalized = normalizeApiKey(apiKey);
|
|
137
|
+
if (!normalized) return null;
|
|
138
|
+
return existingRows.find(
|
|
139
|
+
(connection) =>
|
|
140
|
+
connection.id !== ignoreId &&
|
|
141
|
+
connection.authType === "apikey" &&
|
|
142
|
+
normalizeApiKey(connection.apiKey) === normalized
|
|
143
|
+
) || null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function findDuplicateConnectionName(existingRows, name, ignoreId) {
|
|
147
|
+
const normalized = normalizeConnectionName(name).toLowerCase();
|
|
148
|
+
if (!normalized) return null;
|
|
149
|
+
return existingRows.find(
|
|
150
|
+
(connection) =>
|
|
151
|
+
connection.id !== ignoreId &&
|
|
152
|
+
connection.authType === "apikey" &&
|
|
153
|
+
normalizeConnectionName(connection.name).toLowerCase() === normalized
|
|
154
|
+
) || null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function getProviderConnections(filter = {}) {
|
|
158
|
+
const db = await getAdapter();
|
|
159
|
+
const where = ["1 = 1"];
|
|
160
|
+
const params = [];
|
|
161
|
+
if (filter.provider) { where.push("provider = ?"); params.push(filter.provider); }
|
|
162
|
+
if (filter.authType) { where.push("authType = ?"); params.push(filter.authType); }
|
|
163
|
+
if (filter.isActive !== undefined) { where.push("isActive = ?"); params.push(filter.isActive ? 1 : 0); }
|
|
164
|
+
|
|
165
|
+
if (filter.ids) {
|
|
166
|
+
const idList = [...filter.ids];
|
|
167
|
+
if (idList.length === 0) return [];
|
|
168
|
+
const placeholders = idList.map(() => "?").join(", ");
|
|
169
|
+
where.push(`id IN (${placeholders})`);
|
|
170
|
+
params.push(...idList);
|
|
171
|
+
}
|
|
172
|
+
const sql = `SELECT * FROM providerConnections WHERE ${where.join(" AND ")}`;
|
|
173
|
+
const rows = db.all(sql, params);
|
|
174
|
+
const list = rows.map(rowToConn);
|
|
175
|
+
list.sort((a, b) => (a.priority || 999) - (b.priority || 999));
|
|
176
|
+
return list;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function getProviderConnectionsForRouting(filter = {}) {
|
|
180
|
+
const db = await getAdapter();
|
|
181
|
+
const where = ["1 = 1"];
|
|
182
|
+
const params = [];
|
|
183
|
+
if (filter.provider) { where.push("provider = ?"); params.push(filter.provider); }
|
|
184
|
+
if (filter.isActive !== undefined) { where.push("isActive = ?"); params.push(filter.isActive ? 1 : 0); }
|
|
185
|
+
if (filter.authType) { where.push("authType = ?"); params.push(filter.authType); }
|
|
186
|
+
|
|
187
|
+
const rows = db.all(
|
|
188
|
+
`SELECT id, provider, authType, name, email, priority, isActive, data
|
|
189
|
+
FROM providerConnections WHERE ${where.join(" AND ")}
|
|
190
|
+
ORDER BY COALESCE(priority, 999), id`,
|
|
191
|
+
params,
|
|
192
|
+
);
|
|
193
|
+
return rows.map(rowToRoutingConn);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export async function getActiveProviderRows() {
|
|
197
|
+
const db = await getAdapter();
|
|
198
|
+
const rows = db.all(
|
|
199
|
+
`SELECT * FROM (
|
|
200
|
+
SELECT *, ROW_NUMBER() OVER (
|
|
201
|
+
PARTITION BY provider
|
|
202
|
+
ORDER BY COALESCE(priority, 999), updatedAt DESC
|
|
203
|
+
) AS rn
|
|
204
|
+
FROM providerConnections
|
|
205
|
+
WHERE isActive = 1
|
|
206
|
+
) WHERE rn = 1`
|
|
207
|
+
);
|
|
208
|
+
return rows.map(rowToConn);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function getProviderConnectionById(id) {
|
|
212
|
+
const db = await getAdapter();
|
|
213
|
+
const row = db.get(`SELECT * FROM providerConnections WHERE id = ?`, [id]);
|
|
214
|
+
return rowToConn(row);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const PRIORITY_GAP = 1024;
|
|
218
|
+
|
|
219
|
+
function gapPriorityBelow(existingRows, prevPriority) {
|
|
220
|
+
let p = (Number(prevPriority) || 0) + PRIORITY_GAP;
|
|
221
|
+
|
|
222
|
+
const taken = new Set();
|
|
223
|
+
for (const c of existingRows) {
|
|
224
|
+
if (c.priority !== null && c.priority !== undefined) taken.add(c.priority);
|
|
225
|
+
}
|
|
226
|
+
let i = 0;
|
|
227
|
+
while (taken.has(p) && i < PRIORITY_GAP) { p += 1; i += 1; }
|
|
228
|
+
return p;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function normalizePriorities(db, providerId) {
|
|
232
|
+
const list = db.all(`SELECT * FROM providerConnections WHERE provider = ?`, [providerId]).map(rowToConn);
|
|
233
|
+
list.sort((a, b) => {
|
|
234
|
+
const pDiff = (a.priority || 0) - (b.priority || 0);
|
|
235
|
+
if (pDiff !== 0) return pDiff;
|
|
236
|
+
return new Date(b.updatedAt || 0) - new Date(a.updatedAt || 0);
|
|
237
|
+
});
|
|
238
|
+
list.forEach((c, i) => {
|
|
239
|
+
db.run(`UPDATE providerConnections SET priority = ? WHERE id = ?`, [i + 1, c.id]);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export async function createProviderConnection(data) {
|
|
244
|
+
const db = await getAdapter();
|
|
245
|
+
const now = new Date().toISOString();
|
|
246
|
+
let result;
|
|
247
|
+
|
|
248
|
+
db.transaction(() => {
|
|
249
|
+
const all = db.all(`SELECT * FROM providerConnections WHERE provider = ?`, [data.provider]).map(rowToConn);
|
|
250
|
+
|
|
251
|
+
const incomingApiKey = normalizeApiKey(data.apiKey);
|
|
252
|
+
if (data.authType === "apikey" && incomingApiKey) {
|
|
253
|
+
if (findDuplicateApiKey(all, incomingApiKey)) {
|
|
254
|
+
throw conflictError(
|
|
255
|
+
"DUPLICATE_API_KEY",
|
|
256
|
+
"This API key is already connected to this provider"
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let existing = null;
|
|
262
|
+
if (data.authType === "oauth" && data.email) {
|
|
263
|
+
const incomingUsername = data.providerSpecificData?.username;
|
|
264
|
+
const incomingWs = data.providerSpecificData?.chatgptAccountId;
|
|
265
|
+
existing = all.find(c => {
|
|
266
|
+
if (c.authType !== "oauth" || c.email !== data.email) return false;
|
|
267
|
+
|
|
268
|
+
if (data.provider === "codex") {
|
|
269
|
+
const existingWs = c.providerSpecificData?.chatgptAccountId;
|
|
270
|
+
return !!incomingWs && !!existingWs && incomingWs === existingWs;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const existingWs = c.providerSpecificData?.chatgptAccountId;
|
|
274
|
+
if (incomingWs && existingWs) return incomingWs === existingWs;
|
|
275
|
+
if (incomingWs && !existingWs) return false;
|
|
276
|
+
if (!incomingWs && existingWs) return false;
|
|
277
|
+
|
|
278
|
+
const existingUsername = c.providerSpecificData?.username;
|
|
279
|
+
if (incomingUsername && existingUsername) {
|
|
280
|
+
return incomingUsername === existingUsername;
|
|
281
|
+
}
|
|
282
|
+
if (incomingUsername || existingUsername) return false;
|
|
283
|
+
return true;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (existing) {
|
|
288
|
+
const merged = { ...existing, ...data, updatedAt: now };
|
|
289
|
+
upsert(db, merged);
|
|
290
|
+
result = merged;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const requestedName = normalizeConnectionName(data.name);
|
|
295
|
+
let connectionName = requestedName || null;
|
|
296
|
+
if (data.authType === "apikey" || data.authType === "api_key") {
|
|
297
|
+
if (data.autoName === true || !requestedName) {
|
|
298
|
+
connectionName = nextGeneratedConnectionName(all, requestedName);
|
|
299
|
+
} else if (findDuplicateConnectionName(all, requestedName)) {
|
|
300
|
+
throw conflictError(
|
|
301
|
+
"DUPLICATE_CONNECTION_NAME",
|
|
302
|
+
"A connection with this account name already exists for this provider"
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (!connectionName && (data.authType === "oauth" || data.authType === "access_token")) {
|
|
307
|
+
connectionName = deriveConnectionName(data, data.email || `Account ${all.length + 1}`);
|
|
308
|
+
}
|
|
309
|
+
let connectionPriority = data.priority;
|
|
310
|
+
if (connectionPriority === undefined || connectionPriority === null) {
|
|
311
|
+
|
|
312
|
+
connectionPriority = gapPriorityBelow(
|
|
313
|
+
all,
|
|
314
|
+
all.reduce((m, c) => {
|
|
315
|
+
const p = Number(c.priority) || 0;
|
|
316
|
+
return p > m ? p : m;
|
|
317
|
+
}, 0)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const conn = {
|
|
322
|
+
id: uuidv4(),
|
|
323
|
+
provider: data.provider,
|
|
324
|
+
authType: data.authType || "oauth",
|
|
325
|
+
name: connectionName,
|
|
326
|
+
priority: connectionPriority,
|
|
327
|
+
isActive: data.isActive !== undefined ? data.isActive : true,
|
|
328
|
+
createdAt: now,
|
|
329
|
+
updatedAt: now,
|
|
330
|
+
};
|
|
331
|
+
for (const f of OPTIONAL_FIELDS) {
|
|
332
|
+
if (data[f] !== undefined && data[f] !== null) {
|
|
333
|
+
conn[f] = f === "apiKey" ? incomingApiKey : data[f];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (data.providerSpecificData && Object.keys(data.providerSpecificData).length > 0) {
|
|
337
|
+
conn.providerSpecificData = data.providerSpecificData;
|
|
338
|
+
}
|
|
339
|
+
if (data.email !== undefined) conn.email = data.email;
|
|
340
|
+
|
|
341
|
+
upsert(db, conn);
|
|
342
|
+
result = conn;
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export async function updateProviderConnection(id, data) {
|
|
349
|
+
const db = await getAdapter();
|
|
350
|
+
let result;
|
|
351
|
+
db.transaction(() => {
|
|
352
|
+
const row = db.get(`SELECT * FROM providerConnections WHERE id = ?`, [id]);
|
|
353
|
+
if (!row) { result = null; return; }
|
|
354
|
+
const existing = rowToConn(row);
|
|
355
|
+
const providerRows = db
|
|
356
|
+
.all(`SELECT * FROM providerConnections WHERE provider = ?`, [existing.provider])
|
|
357
|
+
.map(rowToConn);
|
|
358
|
+
const normalizedUpdate = { ...data };
|
|
359
|
+
|
|
360
|
+
if (existing.authType === "apikey") {
|
|
361
|
+
if (data.apiKey !== undefined) {
|
|
362
|
+
normalizedUpdate.apiKey = normalizeApiKey(data.apiKey);
|
|
363
|
+
if (findDuplicateApiKey(providerRows, normalizedUpdate.apiKey, id)) {
|
|
364
|
+
throw conflictError(
|
|
365
|
+
"DUPLICATE_API_KEY",
|
|
366
|
+
"This API key is already connected to this provider"
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (data.name !== undefined) {
|
|
371
|
+
normalizedUpdate.name = normalizeConnectionName(data.name);
|
|
372
|
+
if (findDuplicateConnectionName(providerRows, normalizedUpdate.name, id)) {
|
|
373
|
+
throw conflictError(
|
|
374
|
+
"DUPLICATE_CONNECTION_NAME",
|
|
375
|
+
"A connection with this account name already exists for this provider"
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const merged = { ...existing, ...normalizedUpdate, updatedAt: new Date().toISOString() };
|
|
382
|
+
upsert(db, merged);
|
|
383
|
+
if (data.priority !== undefined) normalizePriorities(db, existing.provider);
|
|
384
|
+
result = merged;
|
|
385
|
+
});
|
|
386
|
+
return result;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export async function deleteProviderConnection(id) {
|
|
390
|
+
const db = await getAdapter();
|
|
391
|
+
let ok = false;
|
|
392
|
+
db.transaction(() => {
|
|
393
|
+
const row = db.get(`SELECT provider FROM providerConnections WHERE id = ?`, [id]);
|
|
394
|
+
if (!row) return;
|
|
395
|
+
db.run(`DELETE FROM providerConnections WHERE id = ?`, [id]);
|
|
396
|
+
ok = true;
|
|
397
|
+
});
|
|
398
|
+
return ok;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export async function deleteProviderConnectionsByProvider(providerId) {
|
|
402
|
+
const db = await getAdapter();
|
|
403
|
+
const before = db.get(`SELECT COUNT(*) AS n FROM providerConnections WHERE provider = ?`, [providerId]);
|
|
404
|
+
db.run(`DELETE FROM providerConnections WHERE provider = ?`, [providerId]);
|
|
405
|
+
return before?.n || 0;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export async function reorderProviderConnections(providerId) {
|
|
409
|
+
const db = await getAdapter();
|
|
410
|
+
db.transaction(() => normalizePriorities(db, providerId));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export async function getProviderConnectionsPaged(filter = {}) {
|
|
414
|
+
const db = await getAdapter();
|
|
415
|
+
const where = [];
|
|
416
|
+
const params = [];
|
|
417
|
+
|
|
418
|
+
if (filter.provider) { where.push("provider = ?"); params.push(filter.provider); }
|
|
419
|
+
if (filter.providerPrefixes?.length) {
|
|
420
|
+
const prefixes = filter.providerPrefixes.filter((prefix) => typeof prefix === "string" && prefix.length > 0);
|
|
421
|
+
if (prefixes.length) {
|
|
422
|
+
where.push(`(${prefixes.map(() => "provider LIKE ?").join(" OR ")})`);
|
|
423
|
+
params.push(...prefixes.map((prefix) => `${prefix}%`));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (filter.authType) { where.push("authType = ?"); params.push(filter.authType); }
|
|
427
|
+
if (filter.isActive !== undefined) { where.push("isActive = ?"); params.push(filter.isActive ? 1 : 0); }
|
|
428
|
+
if (filter.search) {
|
|
429
|
+
const q = `%${filter.search}%`;
|
|
430
|
+
where.push("(name LIKE ? OR email LIKE ? OR id LIKE ?)");
|
|
431
|
+
params.push(q, q, q);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const whereClause = where.length ? ` WHERE ${where.join(" AND ")}` : "";
|
|
435
|
+
|
|
436
|
+
const countRow = db.get(`SELECT COUNT(*) AS total FROM providerConnections${whereClause}`, params);
|
|
437
|
+
const total = countRow?.total || 0;
|
|
438
|
+
|
|
439
|
+
const page = Math.max(1, Number(filter.page) || 1);
|
|
440
|
+
const pageSize = Math.max(1, Math.min(500, Number(filter.pageSize) || 25));
|
|
441
|
+
const offset = (page - 1) * pageSize;
|
|
442
|
+
|
|
443
|
+
const cap = Number.isFinite(Number(filter.limit)) && Number(filter.limit) > 0
|
|
444
|
+
? Math.min(500, Math.floor(Number(filter.limit)))
|
|
445
|
+
: null;
|
|
446
|
+
const cappedSize = cap === null ? pageSize : Math.max(0, Math.min(pageSize, cap - offset));
|
|
447
|
+
const pageSizeApplied = cappedSize > 0 ? cappedSize : 0;
|
|
448
|
+
|
|
449
|
+
const rows = pageSizeApplied > 0 ? db.all(
|
|
450
|
+
`SELECT * FROM providerConnections${whereClause} ORDER BY COALESCE(priority, 999), id LIMIT ? OFFSET ?`,
|
|
451
|
+
[...params, pageSizeApplied, offset]
|
|
452
|
+
) : [];
|
|
453
|
+
const connections = rows.map(rowToConn);
|
|
454
|
+
|
|
455
|
+
return { connections, total };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export async function countProviderConnections() {
|
|
459
|
+
const db = await getAdapter();
|
|
460
|
+
const rows = db.all(
|
|
461
|
+
`SELECT provider,
|
|
462
|
+
COUNT(*) AS total,
|
|
463
|
+
SUM(CASE WHEN isActive = 1 THEN 1 ELSE 0 END) AS active,
|
|
464
|
+
SUM(CASE WHEN isActive = 0 THEN 1 ELSE 0 END) AS inactive
|
|
465
|
+
FROM providerConnections
|
|
466
|
+
GROUP BY provider`
|
|
467
|
+
);
|
|
468
|
+
return rows.map(r => ({
|
|
469
|
+
provider: r.provider,
|
|
470
|
+
total: r.total,
|
|
471
|
+
active: r.active,
|
|
472
|
+
inactive: r.inactive,
|
|
473
|
+
}));
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export async function countConnectionsByProxyPool() {
|
|
477
|
+
const db = await getAdapter();
|
|
478
|
+
const rows = db.all(`
|
|
479
|
+
SELECT COALESCE(
|
|
480
|
+
json_extract(data, '$.providerSpecificData.proxyPoolId'),
|
|
481
|
+
json_extract(data, '$.proxyPoolId')
|
|
482
|
+
) AS proxyPoolId, COUNT(*) AS total
|
|
483
|
+
FROM providerConnections
|
|
484
|
+
WHERE COALESCE(
|
|
485
|
+
json_extract(data, '$.providerSpecificData.proxyPoolId'),
|
|
486
|
+
json_extract(data, '$.proxyPoolId')
|
|
487
|
+
) IS NOT NULL
|
|
488
|
+
GROUP BY proxyPoolId
|
|
489
|
+
`);
|
|
490
|
+
return new Map(rows.map((row) => [String(row.proxyPoolId), Number(row.total) || 0]));
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export async function cleanupProviderConnections() {
|
|
494
|
+
const db = await getAdapter();
|
|
495
|
+
const fieldsToCheck = [
|
|
496
|
+
"displayName", "email", "globalPriority", "defaultModel",
|
|
497
|
+
"accessToken", "refreshToken", "expiresAt", "tokenType",
|
|
498
|
+
"scope", "projectId", "apiKey", "testStatus",
|
|
499
|
+
"lastTested", "lastError", "lastErrorAt", "rateLimitedUntil", "expiresIn",
|
|
500
|
+
"consecutiveUseCount",
|
|
501
|
+
];
|
|
502
|
+
let cleaned = 0;
|
|
503
|
+
db.transaction(() => {
|
|
504
|
+
const rows = db.all(`SELECT * FROM providerConnections`);
|
|
505
|
+
for (const row of rows) {
|
|
506
|
+
const conn = rowToConn(row);
|
|
507
|
+
let dirty = false;
|
|
508
|
+
for (const f of fieldsToCheck) {
|
|
509
|
+
if (conn[f] === null || conn[f] === undefined) {
|
|
510
|
+
if (f in conn) { delete conn[f]; cleaned++; dirty = true; }
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (conn.providerSpecificData && Object.keys(conn.providerSpecificData).length === 0) {
|
|
514
|
+
delete conn.providerSpecificData;
|
|
515
|
+
cleaned++;
|
|
516
|
+
dirty = true;
|
|
517
|
+
}
|
|
518
|
+
if (dirty) upsert(db, conn);
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
return cleaned;
|
|
522
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getAdapter } from "../driver.js";
|
|
2
|
+
import { parseJson, stringifyJson } from "../helpers/jsonCol.js";
|
|
3
|
+
import { getConfigCache, bumpConfigCacheVersion } from "../../cache/ttlCache.js";
|
|
4
|
+
|
|
5
|
+
const SCOPE = "disabledModels";
|
|
6
|
+
|
|
7
|
+
const disabledCache = getConfigCache("disabledModels", { ttlMs: 5000 });
|
|
8
|
+
|
|
9
|
+
async function readAllDisabled() {
|
|
10
|
+
const db = await getAdapter();
|
|
11
|
+
const rows = db.all(`SELECT key, value FROM kv WHERE scope = ?`, [SCOPE]);
|
|
12
|
+
const out = {};
|
|
13
|
+
for (const r of rows) out[r.key] = parseJson(r.value, []);
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function getDisabledModels() {
|
|
18
|
+
return await disabledCache.get("all", readAllDisabled);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function getDisabledByProvider(providerAlias) {
|
|
22
|
+
const all = await getDisabledModels();
|
|
23
|
+
return all[providerAlias] ?? [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function disableModels(providerAlias, ids) {
|
|
27
|
+
if (!providerAlias || !Array.isArray(ids)) return;
|
|
28
|
+
const db = await getAdapter();
|
|
29
|
+
db.transaction(() => {
|
|
30
|
+
const row = db.get(`SELECT value FROM kv WHERE scope = ? AND key = ?`, [SCOPE, providerAlias]);
|
|
31
|
+
const current = row ? (parseJson(row.value, []) || []) : [];
|
|
32
|
+
const merged = [...new Set([...current, ...ids])];
|
|
33
|
+
db.run(
|
|
34
|
+
`INSERT INTO kv(scope, key, value) VALUES(?, ?, ?) ON CONFLICT(scope, key) DO UPDATE SET value = excluded.value`,
|
|
35
|
+
[SCOPE, providerAlias, stringifyJson(merged)]
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
await bumpConfigCacheVersion().catch(() => {});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function enableModels(providerAlias, ids) {
|
|
42
|
+
if (!providerAlias) return;
|
|
43
|
+
const db = await getAdapter();
|
|
44
|
+
db.transaction(() => {
|
|
45
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
46
|
+
db.run(`DELETE FROM kv WHERE scope = ? AND key = ?`, [SCOPE, providerAlias]);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const row = db.get(`SELECT value FROM kv WHERE scope = ? AND key = ?`, [SCOPE, providerAlias]);
|
|
50
|
+
const current = row ? (parseJson(row.value, []) || []) : [];
|
|
51
|
+
const removeSet = new Set(ids);
|
|
52
|
+
const next = current.filter((id) => !removeSet.has(id));
|
|
53
|
+
if (next.length === 0) {
|
|
54
|
+
db.run(`DELETE FROM kv WHERE scope = ? AND key = ?`, [SCOPE, providerAlias]);
|
|
55
|
+
} else {
|
|
56
|
+
db.run(
|
|
57
|
+
`INSERT INTO kv(scope, key, value) VALUES(?, ?, ?) ON CONFLICT(scope, key) DO UPDATE SET value = excluded.value`,
|
|
58
|
+
[SCOPE, providerAlias, stringifyJson(next)]
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
await bumpConfigCacheVersion().catch(() => {});
|
|
63
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import { getAdapter } from "../driver.js";
|
|
3
|
+
import { parseJson, stringifyJson } from "../helpers/jsonCol.js";
|
|
4
|
+
|
|
5
|
+
function rowToNode(row) {
|
|
6
|
+
if (!row) return null;
|
|
7
|
+
const extra = parseJson(row.data, {});
|
|
8
|
+
return {
|
|
9
|
+
...extra,
|
|
10
|
+
id: row.id,
|
|
11
|
+
type: row.type,
|
|
12
|
+
name: row.name,
|
|
13
|
+
createdAt: row.createdAt,
|
|
14
|
+
updatedAt: row.updatedAt,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function nodeToRow(n) {
|
|
19
|
+
const { id, type, name, createdAt, updatedAt, ...rest } = n;
|
|
20
|
+
return {
|
|
21
|
+
id,
|
|
22
|
+
type: type ?? null,
|
|
23
|
+
name: name ?? null,
|
|
24
|
+
data: stringifyJson(rest),
|
|
25
|
+
createdAt,
|
|
26
|
+
updatedAt,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function upsert(db, n) {
|
|
31
|
+
const r = nodeToRow(n);
|
|
32
|
+
db.run(
|
|
33
|
+
`INSERT INTO providerNodes(id, type, name, data, createdAt, updatedAt)
|
|
34
|
+
VALUES(?, ?, ?, ?, ?, ?)
|
|
35
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
36
|
+
type=excluded.type, name=excluded.name, data=excluded.data, updatedAt=excluded.updatedAt`,
|
|
37
|
+
[r.id, r.type, r.name, r.data, r.createdAt, r.updatedAt]
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeNodeIdentity(value) {
|
|
42
|
+
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function duplicateNodeError(field, value) {
|
|
46
|
+
const label = field === "prefix" ? "prefix" : "name";
|
|
47
|
+
const error = new Error(`A custom provider with this ${label} already exists`);
|
|
48
|
+
error.code = "DUPLICATE_PROVIDER_NODE";
|
|
49
|
+
error.field = field;
|
|
50
|
+
error.value = value;
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function assertUniqueNode(db, node, ignoreId) {
|
|
55
|
+
const name = normalizeNodeIdentity(node.name);
|
|
56
|
+
const prefix = normalizeNodeIdentity(node.prefix);
|
|
57
|
+
const rows = db.all(`SELECT * FROM providerNodes`).map(rowToNode);
|
|
58
|
+
for (const existing of rows) {
|
|
59
|
+
if (existing.id === ignoreId) continue;
|
|
60
|
+
if (name && normalizeNodeIdentity(existing.name) === name) {
|
|
61
|
+
throw duplicateNodeError("name", node.name);
|
|
62
|
+
}
|
|
63
|
+
if (prefix && normalizeNodeIdentity(existing.prefix) === prefix) {
|
|
64
|
+
throw duplicateNodeError("prefix", node.prefix);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function getProviderNodes(filter = {}) {
|
|
70
|
+
const db = await getAdapter();
|
|
71
|
+
const where = [];
|
|
72
|
+
const params = [];
|
|
73
|
+
if (filter.type) { where.push("type = ?"); params.push(filter.type); }
|
|
74
|
+
const sql = `SELECT * FROM providerNodes${where.length ? ` WHERE ${where.join(" AND ")}` : ""}`;
|
|
75
|
+
return db.all(sql, params).map(rowToNode);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function getProviderNodeById(id) {
|
|
79
|
+
const db = await getAdapter();
|
|
80
|
+
return rowToNode(db.get(`SELECT * FROM providerNodes WHERE id = ?`, [id]));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function createProviderNode(data) {
|
|
84
|
+
const db = await getAdapter();
|
|
85
|
+
const now = new Date().toISOString();
|
|
86
|
+
const node = {
|
|
87
|
+
id: data.id || uuidv4(),
|
|
88
|
+
type: data.type,
|
|
89
|
+
name: data.name,
|
|
90
|
+
prefix: data.prefix,
|
|
91
|
+
apiType: data.apiType,
|
|
92
|
+
baseUrl: data.baseUrl,
|
|
93
|
+
createdAt: now,
|
|
94
|
+
updatedAt: now,
|
|
95
|
+
};
|
|
96
|
+
db.transaction(() => {
|
|
97
|
+
assertUniqueNode(db, node);
|
|
98
|
+
upsert(db, node);
|
|
99
|
+
});
|
|
100
|
+
return node;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function updateProviderNode(id, data) {
|
|
104
|
+
const db = await getAdapter();
|
|
105
|
+
let result = null;
|
|
106
|
+
db.transaction(() => {
|
|
107
|
+
const row = db.get(`SELECT * FROM providerNodes WHERE id = ?`, [id]);
|
|
108
|
+
if (!row) return;
|
|
109
|
+
const merged = { ...rowToNode(row), ...data, updatedAt: new Date().toISOString() };
|
|
110
|
+
assertUniqueNode(db, merged, id);
|
|
111
|
+
upsert(db, merged);
|
|
112
|
+
result = merged;
|
|
113
|
+
});
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function deleteProviderNode(id) {
|
|
118
|
+
const db = await getAdapter();
|
|
119
|
+
let removed = null;
|
|
120
|
+
db.transaction(() => {
|
|
121
|
+
const row = db.get(`SELECT * FROM providerNodes WHERE id = ?`, [id]);
|
|
122
|
+
if (!row) return;
|
|
123
|
+
removed = rowToNode(row);
|
|
124
|
+
db.run(`DELETE FROM providerNodes WHERE id = ?`, [id]);
|
|
125
|
+
});
|
|
126
|
+
return removed;
|
|
127
|
+
}
|