wyxrouter 0.4.71
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/CHANGELOG.md +222 -0
- package/LICENSE +21 -0
- package/README.md +96 -0
- package/README.zh-CN.md +1311 -0
- package/assets/pixel-router-2d.png +0 -0
- package/cli/LICENSE +42 -0
- package/cli/README.md +125 -0
- package/cli/app/package.json +58 -0
- package/cli/cli.js +806 -0
- package/cli/package.json +48 -0
- package/i18n/README.ja-JP.md +1209 -0
- package/i18n/README.ru.md +1311 -0
- package/i18n/README.vi.md +1310 -0
- package/i18n/README.zh-CN.md +1306 -0
- package/images/9router.png +0 -0
- package/jsconfig.json +12 -0
- package/next.config.mjs +71 -0
- package/open-sse/config/appConstants.js +203 -0
- package/open-sse/config/codexInstructions.js +119 -0
- package/open-sse/config/constants.js +4 -0
- package/open-sse/config/defaultThinkingSignature.js +12 -0
- package/open-sse/config/errorConfig.js +85 -0
- package/open-sse/config/googleTtsLanguages.js +62 -0
- package/open-sse/config/kiroConstants.js +322 -0
- package/open-sse/config/models.js +13 -0
- package/open-sse/config/ollamaModels.js +19 -0
- package/open-sse/config/providerModels.js +944 -0
- package/open-sse/config/providers.js +458 -0
- package/open-sse/config/runtimeConfig.js +72 -0
- package/open-sse/config/ttsModels.js +129 -0
- package/open-sse/executors/antigravity.js +504 -0
- package/open-sse/executors/azure.js +57 -0
- package/open-sse/executors/base.js +185 -0
- package/open-sse/executors/codex.js +469 -0
- package/open-sse/executors/commandcode.js +88 -0
- package/open-sse/executors/cursor.js +795 -0
- package/open-sse/executors/default.js +497 -0
- package/open-sse/executors/gemini-cli.js +89 -0
- package/open-sse/executors/github.js +379 -0
- package/open-sse/executors/grok-web.js +345 -0
- package/open-sse/executors/iflow.js +108 -0
- package/open-sse/executors/index.js +75 -0
- package/open-sse/executors/kiro.js +508 -0
- package/open-sse/executors/ollama-local.js +14 -0
- package/open-sse/executors/opencode-go.js +41 -0
- package/open-sse/executors/opencode.js +32 -0
- package/open-sse/executors/perplexity-web.js +507 -0
- package/open-sse/executors/qoder.js +450 -0
- package/open-sse/executors/qwen.js +129 -0
- package/open-sse/executors/vertex.js +131 -0
- package/open-sse/executors/xiaomi-tokenplan.js +19 -0
- package/open-sse/handlers/chatCore/nonStreamingHandler.js +230 -0
- package/open-sse/handlers/chatCore/requestDetail.js +102 -0
- package/open-sse/handlers/chatCore/sseToJsonHandler.js +231 -0
- package/open-sse/handlers/chatCore/streamingHandler.js +103 -0
- package/open-sse/handlers/chatCore.js +287 -0
- package/open-sse/handlers/embeddingProviders/_base.js +4 -0
- package/open-sse/handlers/embeddingProviders/gemini.js +54 -0
- package/open-sse/handlers/embeddingProviders/index.js +23 -0
- package/open-sse/handlers/embeddingProviders/openai.js +39 -0
- package/open-sse/handlers/embeddingProviders/openaiCompatNode.js +13 -0
- package/open-sse/handlers/embeddingsCore.js +126 -0
- package/open-sse/handlers/fetch/index.js +237 -0
- package/open-sse/handlers/imageGenerationCore.js +189 -0
- package/open-sse/handlers/imageProviders/_base.js +31 -0
- package/open-sse/handlers/imageProviders/blackForestLabs.js +43 -0
- package/open-sse/handlers/imageProviders/cloudflareAi.js +178 -0
- package/open-sse/handlers/imageProviders/codex.js +198 -0
- package/open-sse/handlers/imageProviders/comfyui.js +8 -0
- package/open-sse/handlers/imageProviders/falAi.js +41 -0
- package/open-sse/handlers/imageProviders/gemini.js +25 -0
- package/open-sse/handlers/imageProviders/huggingface.js +22 -0
- package/open-sse/handlers/imageProviders/index.js +40 -0
- package/open-sse/handlers/imageProviders/nanobanana.js +58 -0
- package/open-sse/handlers/imageProviders/openai.js +40 -0
- package/open-sse/handlers/imageProviders/runwayml.js +47 -0
- package/open-sse/handlers/imageProviders/sdwebui.js +17 -0
- package/open-sse/handlers/imageProviders/stabilityAi.js +34 -0
- package/open-sse/handlers/responsesHandler.js +103 -0
- package/open-sse/handlers/search/callers.js +371 -0
- package/open-sse/handlers/search/chatSearch.js +409 -0
- package/open-sse/handlers/search/index.js +201 -0
- package/open-sse/handlers/search/normalizers.js +223 -0
- package/open-sse/handlers/sttCore.js +194 -0
- package/open-sse/handlers/ttsCore.js +74 -0
- package/open-sse/handlers/ttsProviders/_base.js +39 -0
- package/open-sse/handlers/ttsProviders/edgeTts.js +89 -0
- package/open-sse/handlers/ttsProviders/elevenlabs.js +48 -0
- package/open-sse/handlers/ttsProviders/gemini.js +117 -0
- package/open-sse/handlers/ttsProviders/genericFormats.js +169 -0
- package/open-sse/handlers/ttsProviders/googleTts.js +54 -0
- package/open-sse/handlers/ttsProviders/index.js +50 -0
- package/open-sse/handlers/ttsProviders/localDevice.js +87 -0
- package/open-sse/handlers/ttsProviders/minimax.js +59 -0
- package/open-sse/handlers/ttsProviders/openai.js +30 -0
- package/open-sse/handlers/ttsProviders/openrouter.js +70 -0
- package/open-sse/index.js +82 -0
- package/open-sse/rtk/applyFilter.js +15 -0
- package/open-sse/rtk/autodetect.js +111 -0
- package/open-sse/rtk/caveman.js +100 -0
- package/open-sse/rtk/cavemanPrompts.js +78 -0
- package/open-sse/rtk/constants.js +55 -0
- package/open-sse/rtk/filters/buildOutput.js +127 -0
- package/open-sse/rtk/filters/dedupLog.js +44 -0
- package/open-sse/rtk/filters/find.js +49 -0
- package/open-sse/rtk/filters/gitDiff.js +92 -0
- package/open-sse/rtk/filters/gitStatus.js +117 -0
- package/open-sse/rtk/filters/grep.js +48 -0
- package/open-sse/rtk/filters/ls.js +79 -0
- package/open-sse/rtk/filters/readNumbered.js +27 -0
- package/open-sse/rtk/filters/searchList.js +52 -0
- package/open-sse/rtk/filters/smartTruncate.js +15 -0
- package/open-sse/rtk/filters/tree.js +32 -0
- package/open-sse/rtk/index.js +155 -0
- package/open-sse/rtk/registry.js +38 -0
- package/open-sse/services/accountFallback.js +238 -0
- package/open-sse/services/combo.js +198 -0
- package/open-sse/services/compact.js +71 -0
- package/open-sse/services/kiroModels.js +332 -0
- package/open-sse/services/model.js +261 -0
- package/open-sse/services/oauthCredentialManager.js +151 -0
- package/open-sse/services/projectId.js +306 -0
- package/open-sse/services/provider.js +356 -0
- package/open-sse/services/qoderModels.js +214 -0
- package/open-sse/services/tokenRefresh.js +939 -0
- package/open-sse/services/usage.js +1496 -0
- package/open-sse/transformer/responsesTransformer.js +439 -0
- package/open-sse/transformer/streamToJsonConverter.js +103 -0
- package/open-sse/translator/formats.js +36 -0
- package/open-sse/translator/helpers/claudeHelper.js +216 -0
- package/open-sse/translator/helpers/geminiHelper.js +372 -0
- package/open-sse/translator/helpers/imageHelper.js +34 -0
- package/open-sse/translator/helpers/maxTokensHelper.js +27 -0
- package/open-sse/translator/helpers/openaiHelper.js +130 -0
- package/open-sse/translator/helpers/responsesApiHelper.js +139 -0
- package/open-sse/translator/helpers/toolCallHelper.js +148 -0
- package/open-sse/translator/index.js +251 -0
- package/open-sse/translator/request/antigravity-to-openai.js +229 -0
- package/open-sse/translator/request/claude-to-openai.js +232 -0
- package/open-sse/translator/request/gemini-to-openai.js +147 -0
- package/open-sse/translator/request/openai-responses.js +318 -0
- package/open-sse/translator/request/openai-to-claude.js +401 -0
- package/open-sse/translator/request/openai-to-commandcode.js +170 -0
- package/open-sse/translator/request/openai-to-cursor.js +183 -0
- package/open-sse/translator/request/openai-to-gemini.js +470 -0
- package/open-sse/translator/request/openai-to-kiro.js +629 -0
- package/open-sse/translator/request/openai-to-kiro.old.js +278 -0
- package/open-sse/translator/request/openai-to-ollama.js +192 -0
- package/open-sse/translator/request/openai-to-vertex.js +42 -0
- package/open-sse/translator/response/claude-to-openai.js +206 -0
- package/open-sse/translator/response/commandcode-to-openai.js +197 -0
- package/open-sse/translator/response/cursor-to-openai.js +30 -0
- package/open-sse/translator/response/gemini-to-openai.js +245 -0
- package/open-sse/translator/response/kiro-to-openai.js +195 -0
- package/open-sse/translator/response/ollama-to-openai.js +152 -0
- package/open-sse/translator/response/openai-responses.js +590 -0
- package/open-sse/translator/response/openai-to-antigravity.js +122 -0
- package/open-sse/translator/response/openai-to-claude.js +266 -0
- package/open-sse/utils/bypassHandler.js +298 -0
- package/open-sse/utils/claudeCloaking.js +155 -0
- package/open-sse/utils/claudeHeaderCache.js +70 -0
- package/open-sse/utils/clientDetector.js +63 -0
- package/open-sse/utils/cursorChecksum.js +149 -0
- package/open-sse/utils/cursorProtobuf.js +904 -0
- package/open-sse/utils/debugLog.js +14 -0
- package/open-sse/utils/error.js +147 -0
- package/open-sse/utils/ollamaTransform.js +85 -0
- package/open-sse/utils/proxyFetch.js +368 -0
- package/open-sse/utils/reasoningContentInjector.js +79 -0
- package/open-sse/utils/requestLogger.js +260 -0
- package/open-sse/utils/responsesStreamHelpers.js +49 -0
- package/open-sse/utils/sessionManager.js +82 -0
- package/open-sse/utils/stream.js +462 -0
- package/open-sse/utils/streamHandler.js +250 -0
- package/open-sse/utils/streamHelpers.js +122 -0
- package/open-sse/utils/toolDeduper.js +49 -0
- package/open-sse/utils/usageTracking.js +347 -0
- package/package.json +100 -0
- package/postcss.config.mjs +12 -0
- package/public/favicon.svg +11 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/i18n/literals/ar.json +195 -0
- package/public/i18n/literals/bn.json +195 -0
- package/public/i18n/literals/cs.json +195 -0
- package/public/i18n/literals/da.json +195 -0
- package/public/i18n/literals/de.json +195 -0
- package/public/i18n/literals/el.json +195 -0
- package/public/i18n/literals/es.json +195 -0
- package/public/i18n/literals/fi.json +195 -0
- package/public/i18n/literals/fr.json +195 -0
- package/public/i18n/literals/he.json +195 -0
- package/public/i18n/literals/hi.json +195 -0
- package/public/i18n/literals/hu.json +195 -0
- package/public/i18n/literals/id.json +195 -0
- package/public/i18n/literals/it.json +195 -0
- package/public/i18n/literals/ja.json +195 -0
- package/public/i18n/literals/ko.json +195 -0
- package/public/i18n/literals/nl.json +195 -0
- package/public/i18n/literals/no.json +195 -0
- package/public/i18n/literals/pl.json +195 -0
- package/public/i18n/literals/pt-BR.json +195 -0
- package/public/i18n/literals/pt-PT.json +195 -0
- package/public/i18n/literals/ro.json +195 -0
- package/public/i18n/literals/ru.json +195 -0
- package/public/i18n/literals/sv.json +195 -0
- package/public/i18n/literals/th.json +195 -0
- package/public/i18n/literals/tl.json +195 -0
- package/public/i18n/literals/tr.json +195 -0
- package/public/i18n/literals/uk.json +195 -0
- package/public/i18n/literals/ur.json +195 -0
- package/public/i18n/literals/vi.json +195 -0
- package/public/i18n/literals/zh-CN.json +772 -0
- package/public/i18n/literals/zh-TW.json +195 -0
- package/public/icons/discord.svg +4 -0
- package/public/icons/icon-192.svg +4 -0
- package/public/icons/icon-512.svg +4 -0
- package/public/next.svg +1 -0
- package/public/providers/alicode-intl.png +0 -0
- package/public/providers/alicode.png +0 -0
- package/public/providers/amp.png +0 -0
- package/public/providers/anthropic-m.png +0 -0
- package/public/providers/anthropic.png +0 -0
- package/public/providers/antigravity.png +0 -0
- package/public/providers/assemblyai.png +0 -0
- package/public/providers/aws-polly.png +0 -0
- package/public/providers/azure.png +0 -0
- package/public/providers/black-forest-labs.png +0 -0
- package/public/providers/blackbox.png +0 -0
- package/public/providers/brave-search.png +0 -0
- package/public/providers/byteplus.png +0 -0
- package/public/providers/cartesia.png +0 -0
- package/public/providers/cerebras.png +0 -0
- package/public/providers/chutes.png +0 -0
- package/public/providers/claude.png +0 -0
- package/public/providers/cline.png +0 -0
- package/public/providers/cloudflare-ai.png +0 -0
- package/public/providers/codebuddy.svg +37 -0
- package/public/providers/codex.png +0 -0
- package/public/providers/cohere.png +0 -0
- package/public/providers/comfyui.png +0 -0
- package/public/providers/commandcode.png +0 -0
- package/public/providers/continue.png +0 -0
- package/public/providers/copilot.png +0 -0
- package/public/providers/coqui.png +0 -0
- package/public/providers/cursor.png +0 -0
- package/public/providers/deepgram.png +0 -0
- package/public/providers/deepseek-tui.png +0 -0
- package/public/providers/deepseek.png +0 -0
- package/public/providers/droid.png +0 -0
- package/public/providers/edge-tts.png +0 -0
- package/public/providers/elevenlabs.png +0 -0
- package/public/providers/exa.png +0 -0
- package/public/providers/fal-ai.png +0 -0
- package/public/providers/firecrawl.png +0 -0
- package/public/providers/fireworks.png +0 -0
- package/public/providers/gemini-cli.png +0 -0
- package/public/providers/gemini.png +0 -0
- package/public/providers/github.png +0 -0
- package/public/providers/glm-cn.png +0 -0
- package/public/providers/glm.png +0 -0
- package/public/providers/google-pse.png +0 -0
- package/public/providers/google-tts.png +0 -0
- package/public/providers/grok-web.png +0 -0
- package/public/providers/groq.png +0 -0
- package/public/providers/hermes.png +0 -0
- package/public/providers/huggingface.png +0 -0
- package/public/providers/hyperbolic.png +0 -0
- package/public/providers/iflow.png +0 -0
- package/public/providers/inworld.png +0 -0
- package/public/providers/jcode.png +0 -0
- package/public/providers/jina-ai.png +0 -0
- package/public/providers/jina-reader.png +0 -0
- package/public/providers/kilocode.png +0 -0
- package/public/providers/kimi-coding.png +0 -0
- package/public/providers/kimi.png +0 -0
- package/public/providers/kiro.png +0 -0
- package/public/providers/linkup.png +0 -0
- package/public/providers/local-device.png +0 -0
- package/public/providers/minimax-cn.png +0 -0
- package/public/providers/minimax.png +0 -0
- package/public/providers/mistral.png +0 -0
- package/public/providers/nanobanana.png +0 -0
- package/public/providers/nebius.png +0 -0
- package/public/providers/nvidia.png +0 -0
- package/public/providers/oai-cc.png +0 -0
- package/public/providers/oai-r.png +0 -0
- package/public/providers/ollama-local.png +0 -0
- package/public/providers/ollama.png +0 -0
- package/public/providers/openai.png +0 -0
- package/public/providers/openclaw.png +0 -0
- package/public/providers/opencode-go.png +0 -0
- package/public/providers/opencode.png +0 -0
- package/public/providers/openrouter.png +0 -0
- package/public/providers/perplexity-web.png +0 -0
- package/public/providers/perplexity.png +0 -0
- package/public/providers/playht.png +0 -0
- package/public/providers/qoder.png +0 -0
- package/public/providers/qwen.png +0 -0
- package/public/providers/recraft.png +0 -0
- package/public/providers/roo.png +0 -0
- package/public/providers/runwayml.png +0 -0
- package/public/providers/sdwebui.png +0 -0
- package/public/providers/searchapi.png +0 -0
- package/public/providers/searxng.png +0 -0
- package/public/providers/serper.png +0 -0
- package/public/providers/siliconflow.png +0 -0
- package/public/providers/stability-ai.png +0 -0
- package/public/providers/tavily.png +0 -0
- package/public/providers/together.png +0 -0
- package/public/providers/topaz.png +0 -0
- package/public/providers/tortoise.png +0 -0
- package/public/providers/vertex-partner.png +0 -0
- package/public/providers/vertex.png +0 -0
- package/public/providers/volcengine-ark.png +0 -0
- package/public/providers/voyage-ai.png +0 -0
- package/public/providers/xai.png +0 -0
- package/public/providers/xiaomi-mimo.png +0 -0
- package/public/providers/xiaomi-tokenplan.png +0 -0
- package/public/providers/youcom.png +0 -0
- package/public/sw.js +22 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/scripts/compact-request-details.mjs +71 -0
- package/scripts/import-codex-gptjson.mjs +342 -0
- package/scripts/start-standalone.mjs +25 -0
- package/scripts/translate-readme.js +201 -0
- package/src/app/(dashboard)/dashboard/automation/page.js +294 -0
- package/src/app/(dashboard)/dashboard/basic-chat/BasicChatPageClient.js +967 -0
- package/src/app/(dashboard)/dashboard/basic-chat/page.js +5 -0
- package/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.js +66 -0
- package/src/app/(dashboard)/dashboard/cli-tools/[toolId]/ToolDetailClient.js +173 -0
- package/src/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js +11 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/AntigravityToolCard.js +481 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/ApiKeySelect.js +66 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/BaseUrlSelect.js +174 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/ClaudeToolCard.js +390 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/ClineToolCard.js +301 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js +458 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/CopilotToolCard.js +323 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/CoworkToolCard.js +640 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/DeepSeekTuiToolCard.js +338 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/DefaultToolCard.js +271 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/DroidToolCard.js +410 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/EndpointPresetControl.js +128 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/HermesToolCard.js +317 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/JcodeToolCard.js +380 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/KiloToolCard.js +275 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/MitmLinkCard.js +40 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/MitmServerCard.js +329 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/MitmToolCard.js +318 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/OpenClawToolCard.js +388 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/OpenCodeToolCard.js +500 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/ToolSummaryCard.js +39 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/cliEndpointMatch.js +13 -0
- package/src/app/(dashboard)/dashboard/cli-tools/components/index.js +19 -0
- package/src/app/(dashboard)/dashboard/cli-tools/page.js +7 -0
- package/src/app/(dashboard)/dashboard/combos/page.js +612 -0
- package/src/app/(dashboard)/dashboard/console-log/ConsoleLogClient.js +91 -0
- package/src/app/(dashboard)/dashboard/console-log/page.js +8 -0
- package/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js +1555 -0
- package/src/app/(dashboard)/dashboard/endpoint/page.js +7 -0
- package/src/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page.js +1903 -0
- package/src/app/(dashboard)/dashboard/media-providers/[kind]/page.js +289 -0
- package/src/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js +410 -0
- package/src/app/(dashboard)/dashboard/media-providers/web/page.js +209 -0
- package/src/app/(dashboard)/dashboard/mitm/MitmPageClient.js +117 -0
- package/src/app/(dashboard)/dashboard/mitm/page.js +5 -0
- package/src/app/(dashboard)/dashboard/page.js +7 -0
- package/src/app/(dashboard)/dashboard/profile/page.js +1140 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/AddApiKeyModal.js +389 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/AddCustomModelModal.js +125 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/CompatibleModelsSection.js +250 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/ConnectionRow.js +299 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/CooldownTimer.js +42 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/EditCompatibleNodeModal.js +161 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/ModelRow.js +95 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/PassthroughModelsSection.js +183 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/page.js +2053 -0
- package/src/app/(dashboard)/dashboard/providers/[id]/page.new.js +1724 -0
- package/src/app/(dashboard)/dashboard/providers/components/ConnectionsCard.js +497 -0
- package/src/app/(dashboard)/dashboard/providers/components/ModelAvailabilityBadge.js +185 -0
- package/src/app/(dashboard)/dashboard/providers/components/ModelsCard.js +294 -0
- package/src/app/(dashboard)/dashboard/providers/new/page.js +220 -0
- package/src/app/(dashboard)/dashboard/providers/page.js +1365 -0
- package/src/app/(dashboard)/dashboard/proxy-pools/page.js +1092 -0
- package/src/app/(dashboard)/dashboard/quota/page.js +11 -0
- package/src/app/(dashboard)/dashboard/skills/page.js +112 -0
- package/src/app/(dashboard)/dashboard/translator/page.js +303 -0
- package/src/app/(dashboard)/dashboard/usage/components/OverviewCards.js +35 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/ProviderLimitCard.js +185 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/QuotaProgressBar.js +127 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/QuotaTable.js +259 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.js +1394 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.js +244 -0
- package/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js +327 -0
- package/src/app/(dashboard)/dashboard/usage/components/RequestDetailsTab.js +433 -0
- package/src/app/(dashboard)/dashboard/usage/components/UsageChart.js +141 -0
- package/src/app/(dashboard)/dashboard/usage/components/UsageTable.js +247 -0
- package/src/app/(dashboard)/dashboard/usage/page.js +75 -0
- package/src/app/(dashboard)/layout.js +6 -0
- package/src/app/api/auth/login/route.js +76 -0
- package/src/app/api/auth/logout/route.js +12 -0
- package/src/app/api/auth/oidc/callback/route.js +87 -0
- package/src/app/api/auth/oidc/start/route.js +52 -0
- package/src/app/api/auth/oidc/test/route.js +84 -0
- package/src/app/api/auth/status/route.js +45 -0
- package/src/app/api/cli-tools/all-statuses/route.js +46 -0
- package/src/app/api/cli-tools/antigravity-mitm/alias/route.js +53 -0
- package/src/app/api/cli-tools/antigravity-mitm/route.js +202 -0
- package/src/app/api/cli-tools/claude-settings/route.js +203 -0
- package/src/app/api/cli-tools/cline-settings/route.js +133 -0
- package/src/app/api/cli-tools/codex-gateway/accounts/route.js +16 -0
- package/src/app/api/cli-tools/codex-settings/route.js +239 -0
- package/src/app/api/cli-tools/copilot-settings/route.js +148 -0
- package/src/app/api/cli-tools/cowork-mcp-registry/route.js +77 -0
- package/src/app/api/cli-tools/cowork-mcp-tools/route.js +95 -0
- package/src/app/api/cli-tools/cowork-settings/route.js +412 -0
- package/src/app/api/cli-tools/deepseek-tui-settings/route.js +164 -0
- package/src/app/api/cli-tools/droid-settings/route.js +213 -0
- package/src/app/api/cli-tools/hermes-settings/route.js +175 -0
- package/src/app/api/cli-tools/jcode-settings/route.js +216 -0
- package/src/app/api/cli-tools/kilo-settings/route.js +131 -0
- package/src/app/api/cli-tools/openclaw-settings/route.js +292 -0
- package/src/app/api/cli-tools/opencode-settings/route.js +259 -0
- package/src/app/api/combos/[id]/route.js +81 -0
- package/src/app/api/combos/route.js +48 -0
- package/src/app/api/health/route.js +15 -0
- package/src/app/api/init/route.js +4 -0
- package/src/app/api/keys/[id]/route.js +58 -0
- package/src/app/api/keys/route.js +42 -0
- package/src/app/api/locale/route.js +30 -0
- package/src/app/api/mcp/[plugin]/message/route.js +21 -0
- package/src/app/api/mcp/[plugin]/sse/route.js +37 -0
- package/src/app/api/media-providers/tts/deepgram/voices/route.js +65 -0
- package/src/app/api/media-providers/tts/elevenlabs/voices/route.js +71 -0
- package/src/app/api/media-providers/tts/inworld/voices/route.js +61 -0
- package/src/app/api/media-providers/tts/minimax/voices/route.js +113 -0
- package/src/app/api/media-providers/tts/voices/route.js +99 -0
- package/src/app/api/models/alias/route.js +53 -0
- package/src/app/api/models/availability/route.js +103 -0
- package/src/app/api/models/custom/route.js +48 -0
- package/src/app/api/models/disabled/route.js +50 -0
- package/src/app/api/models/route.js +64 -0
- package/src/app/api/models/test/ping.js +191 -0
- package/src/app/api/models/test/route.js +14 -0
- package/src/app/api/oauth/[provider]/[action]/route.js +343 -0
- package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/cancel/route.js +19 -0
- package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/manual/[workerId]/route.js +30 -0
- package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/route.js +23 -0
- package/src/app/api/oauth/codebuddy/bulk-import/latest/route.js +25 -0
- package/src/app/api/oauth/codebuddy/bulk-import/route.js +49 -0
- package/src/app/api/oauth/codebuddy/quota-cookie/route.js +133 -0
- package/src/app/api/oauth/codex/import-token/route.js +96 -0
- package/src/app/api/oauth/cursor/auto-import/route.js +258 -0
- package/src/app/api/oauth/cursor/import/route.js +100 -0
- package/src/app/api/oauth/gitlab/pat/route.js +62 -0
- package/src/app/api/oauth/iflow/cookie/route.js +137 -0
- package/src/app/api/oauth/kiro/auto-import/route.js +85 -0
- package/src/app/api/oauth/kiro/bulk-import/[jobId]/cancel/route.js +18 -0
- package/src/app/api/oauth/kiro/bulk-import/[jobId]/manual/[workerId]/route.js +29 -0
- package/src/app/api/oauth/kiro/bulk-import/[jobId]/route.js +22 -0
- package/src/app/api/oauth/kiro/bulk-import/latest/route.js +25 -0
- package/src/app/api/oauth/kiro/bulk-import/route.js +49 -0
- package/src/app/api/oauth/kiro/import/route.js +110 -0
- package/src/app/api/oauth/kiro/social-authorize/route.js +27 -0
- package/src/app/api/oauth/kiro/social-exchange/route.js +41 -0
- package/src/app/api/pricing/route.js +134 -0
- package/src/app/api/provider-nodes/[id]/route.js +101 -0
- package/src/app/api/provider-nodes/route.js +104 -0
- package/src/app/api/provider-nodes/validate/route.js +201 -0
- package/src/app/api/providers/[id]/models/route.js +526 -0
- package/src/app/api/providers/[id]/route.js +189 -0
- package/src/app/api/providers/[id]/test/route.js +23 -0
- package/src/app/api/providers/[id]/test/testUtils.js +714 -0
- package/src/app/api/providers/[id]/test-models/route.js +66 -0
- package/src/app/api/providers/client/route.js +126 -0
- package/src/app/api/providers/kilo/free-models/route.js +55 -0
- package/src/app/api/providers/route.js +206 -0
- package/src/app/api/providers/suggested-models/filters.js +20 -0
- package/src/app/api/providers/suggested-models/route.js +32 -0
- package/src/app/api/providers/test-batch/route.js +131 -0
- package/src/app/api/providers/validate/route.js +637 -0
- package/src/app/api/proxy-pools/[id]/route.js +123 -0
- package/src/app/api/proxy-pools/[id]/test/route.js +70 -0
- package/src/app/api/proxy-pools/cloudflare-deploy/route.js +145 -0
- package/src/app/api/proxy-pools/deno-deploy/route.js +175 -0
- package/src/app/api/proxy-pools/route.js +93 -0
- package/src/app/api/proxy-pools/vercel-deploy/route.js +142 -0
- package/src/app/api/settings/database/route.js +36 -0
- package/src/app/api/settings/proxy-test/route.js +23 -0
- package/src/app/api/settings/require-login/route.js +15 -0
- package/src/app/api/settings/route.js +100 -0
- package/src/app/api/shutdown/route.js +24 -0
- package/src/app/api/tags/route.js +18 -0
- package/src/app/api/translator/console-logs/route.js +24 -0
- package/src/app/api/translator/console-logs/stream/route.js +79 -0
- package/src/app/api/translator/load/route.js +45 -0
- package/src/app/api/translator/save/route.js +44 -0
- package/src/app/api/translator/send/route.js +94 -0
- package/src/app/api/translator/translate/route.js +90 -0
- package/src/app/api/tunnel/disable/route.js +12 -0
- package/src/app/api/tunnel/enable/route.js +16 -0
- package/src/app/api/tunnel/status/route.js +13 -0
- package/src/app/api/tunnel/tailscale-check/route.js +50 -0
- package/src/app/api/tunnel/tailscale-disable/route.js +12 -0
- package/src/app/api/tunnel/tailscale-enable/route.js +12 -0
- package/src/app/api/tunnel/tailscale-install/route.js +72 -0
- package/src/app/api/usage/[connectionId]/route.js +188 -0
- package/src/app/api/usage/chart/route.js +21 -0
- package/src/app/api/usage/history/route.js +12 -0
- package/src/app/api/usage/logs/route.js +12 -0
- package/src/app/api/usage/providers/route.js +42 -0
- package/src/app/api/usage/request-details/route.js +57 -0
- package/src/app/api/usage/request-logs/route.js +13 -0
- package/src/app/api/usage/stats/route.js +23 -0
- package/src/app/api/usage/stream/route.js +79 -0
- package/src/app/api/v1/api/chat/route.js +37 -0
- package/src/app/api/v1/audio/speech/route.js +16 -0
- package/src/app/api/v1/audio/transcriptions/route.js +19 -0
- package/src/app/api/v1/audio/voices/route.js +68 -0
- package/src/app/api/v1/chat/completions/route.js +35 -0
- package/src/app/api/v1/embeddings/route.js +21 -0
- package/src/app/api/v1/images/generations/route.js +16 -0
- package/src/app/api/v1/messages/count_tokens/route.js +52 -0
- package/src/app/api/v1/messages/route.js +36 -0
- package/src/app/api/v1/models/[kind]/route.js +55 -0
- package/src/app/api/v1/models/info/route.js +110 -0
- package/src/app/api/v1/models/route.js +451 -0
- package/src/app/api/v1/responses/compact/route.js +37 -0
- package/src/app/api/v1/responses/route.js +30 -0
- package/src/app/api/v1/route.js +1 -0
- package/src/app/api/v1/search/route.js +21 -0
- package/src/app/api/v1/web/fetch/route.js +21 -0
- package/src/app/api/v1beta/models/[...path]/route.js +328 -0
- package/src/app/api/v1beta/models/route.js +44 -0
- package/src/app/api/version/route.js +45 -0
- package/src/app/api/version/shutdown/route.js +15 -0
- package/src/app/api/version/update/route.js +21 -0
- package/src/app/callback/page.js +148 -0
- package/src/app/dashboard/settings/pricing/page.js +173 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +496 -0
- package/src/app/landing/components/AnimatedBackground.js +57 -0
- package/src/app/landing/components/Features.js +133 -0
- package/src/app/landing/components/FlowAnimation.js +175 -0
- package/src/app/landing/components/Footer.js +61 -0
- package/src/app/landing/components/GetStarted.js +97 -0
- package/src/app/landing/components/HeroSection.js +47 -0
- package/src/app/landing/components/HowItWorks.js +66 -0
- package/src/app/landing/components/Navigation.js +72 -0
- package/src/app/landing/page.js +106 -0
- package/src/app/layout.js +49 -0
- package/src/app/login/page.js +197 -0
- package/src/app/manifest.js +30 -0
- package/src/app/page.js +5 -0
- package/src/dashboardGuard.js +242 -0
- package/src/i18n/RuntimeI18nProvider.js +27 -0
- package/src/i18n/config.js +146 -0
- package/src/i18n/runtime.js +162 -0
- package/src/lib/appUpdater.js +200 -0
- package/src/lib/auth/dashboardSession.js +68 -0
- package/src/lib/auth/loginLimiter.js +52 -0
- package/src/lib/auth/oidc.js +234 -0
- package/src/lib/consoleLogBuffer.js +79 -0
- package/src/lib/dataDir.js +29 -0
- package/src/lib/db/adapters/betterSqliteAdapter.js +55 -0
- package/src/lib/db/adapters/bunSqliteAdapter.js +63 -0
- package/src/lib/db/adapters/nodeSqliteAdapter.js +84 -0
- package/src/lib/db/adapters/sqljsAdapter.js +115 -0
- package/src/lib/db/backup.js +35 -0
- package/src/lib/db/driver.js +85 -0
- package/src/lib/db/helpers/jsonCol.js +9 -0
- package/src/lib/db/helpers/kvStore.js +39 -0
- package/src/lib/db/helpers/metaStore.js +22 -0
- package/src/lib/db/index.js +171 -0
- package/src/lib/db/migrate.js +286 -0
- package/src/lib/db/migrations/001-initial.js +14 -0
- package/src/lib/db/migrations/index.js +10 -0
- package/src/lib/db/paths.js +18 -0
- package/src/lib/db/repos/aliasRepo.js +62 -0
- package/src/lib/db/repos/apiKeysRepo.js +75 -0
- package/src/lib/db/repos/combosRepo.js +73 -0
- package/src/lib/db/repos/connectionsRepo.js +226 -0
- package/src/lib/db/repos/disabledModelsRepo.js +56 -0
- package/src/lib/db/repos/nodesRepo.js +95 -0
- package/src/lib/db/repos/pricingRepo.js +108 -0
- package/src/lib/db/repos/proxyPoolsRepo.js +103 -0
- package/src/lib/db/repos/requestDetailsRepo.js +259 -0
- package/src/lib/db/repos/settingsRepo.js +104 -0
- package/src/lib/db/repos/usageRepo.js +731 -0
- package/src/lib/db/schema.js +157 -0
- package/src/lib/db/version.js +21 -0
- package/src/lib/disabledModelsDb.js +4 -0
- package/src/lib/localDb.js +21 -0
- package/src/lib/mcp/stdioSseBridge.js +198 -0
- package/src/lib/mitmAliasCache.js +46 -0
- package/src/lib/network/connectionProxy.js +160 -0
- package/src/lib/network/initOutboundProxy.js +25 -0
- package/src/lib/network/outboundProxy.js +68 -0
- package/src/lib/network/proxyTest.js +91 -0
- package/src/lib/oauth/constants/oauth.js +284 -0
- package/src/lib/oauth/constants/xai.js +61 -0
- package/src/lib/oauth/providers.js +1506 -0
- package/src/lib/oauth/services/antigravity.js +321 -0
- package/src/lib/oauth/services/claude.js +136 -0
- package/src/lib/oauth/services/codebuddyBulkImportManager.js +454 -0
- package/src/lib/oauth/services/codex.js +144 -0
- package/src/lib/oauth/services/cursor.js +179 -0
- package/src/lib/oauth/services/gemini.js +240 -0
- package/src/lib/oauth/services/github.js +225 -0
- package/src/lib/oauth/services/iflow.js +202 -0
- package/src/lib/oauth/services/index.js +17 -0
- package/src/lib/oauth/services/kiro.js +334 -0
- package/src/lib/oauth/services/kiroBulkImportManager.js +778 -0
- package/src/lib/oauth/services/kiroConnections.js +93 -0
- package/src/lib/oauth/services/kiroGoogleAutomation.js +1136 -0
- package/src/lib/oauth/services/oauth.js +157 -0
- package/src/lib/oauth/services/openai.js +123 -0
- package/src/lib/oauth/services/qoder.js +216 -0
- package/src/lib/oauth/services/qwen.js +170 -0
- package/src/lib/oauth/services/xai.js +238 -0
- package/src/lib/oauth/utils/banner.js +63 -0
- package/src/lib/oauth/utils/pkce.js +39 -0
- package/src/lib/oauth/utils/server.js +415 -0
- package/src/lib/oauth/utils/ui.js +48 -0
- package/src/lib/providerNormalization.js +45 -0
- package/src/lib/qoder/constants.js +64 -0
- package/src/lib/qoder/cosy.js +175 -0
- package/src/lib/qoder/encoding.js +55 -0
- package/src/lib/requestDetailsDb.js +4 -0
- package/src/lib/tunnel/cloudflare/cloudflared.js +449 -0
- package/src/lib/tunnel/cloudflare/config.js +9 -0
- package/src/lib/tunnel/cloudflare/healthCheck.js +29 -0
- package/src/lib/tunnel/cloudflare/manager.js +151 -0
- package/src/lib/tunnel/cloudflare/pid.js +23 -0
- package/src/lib/tunnel/index.js +48 -0
- package/src/lib/tunnel/shared/dnsResolver.js +17 -0
- package/src/lib/tunnel/shared/internetCheck.js +26 -0
- package/src/lib/tunnel/shared/state.js +41 -0
- package/src/lib/tunnel/shared/watchdogConfig.js +8 -0
- package/src/lib/tunnel/tailscale/config.js +7 -0
- package/src/lib/tunnel/tailscale/healthCheck.js +29 -0
- package/src/lib/tunnel/tailscale/manager.js +129 -0
- package/src/lib/tunnel/tailscale/tailscale.js +790 -0
- package/src/lib/updater/updater.js +235 -0
- package/src/lib/usage/fetcher.js +208 -0
- package/src/lib/usageDb.js +7 -0
- package/src/mitm/antigravityIdeVersion.js +50 -0
- package/src/mitm/cert/generate.js +32 -0
- package/src/mitm/cert/install.js +269 -0
- package/src/mitm/cert/rootCA.js +173 -0
- package/src/mitm/config.js +87 -0
- package/src/mitm/dbReader.js +22 -0
- package/src/mitm/dns/dnsConfig.js +266 -0
- package/src/mitm/handlers/antigravity.js +33 -0
- package/src/mitm/handlers/base.js +226 -0
- package/src/mitm/handlers/copilot.js +35 -0
- package/src/mitm/handlers/cursor.js +15 -0
- package/src/mitm/handlers/kiro.js +526 -0
- package/src/mitm/logger.js +106 -0
- package/src/mitm/manager.js +851 -0
- package/src/mitm/paths.js +32 -0
- package/src/mitm/server.js +435 -0
- package/src/mitm/winElevated.js +81 -0
- package/src/models/index.js +38 -0
- package/src/proxy.js +5 -0
- package/src/shared/components/AddCustomEmbeddingModal.js +183 -0
- package/src/shared/components/Avatar.js +88 -0
- package/src/shared/components/Badge.js +54 -0
- package/src/shared/components/BulkAccountAutomationModal.js +508 -0
- package/src/shared/components/Button.js +56 -0
- package/src/shared/components/Card.js +116 -0
- package/src/shared/components/ChangelogModal.js +97 -0
- package/src/shared/components/CodeBuddyQuotaCookieModal.js +109 -0
- package/src/shared/components/ComboFormModal.js +176 -0
- package/src/shared/components/CursorAuthModal.js +212 -0
- package/src/shared/components/DonateModal.js +136 -0
- package/src/shared/components/Drawer.js +82 -0
- package/src/shared/components/EditConnectionModal.js +286 -0
- package/src/shared/components/Footer.js +132 -0
- package/src/shared/components/GitLabAuthModal.js +194 -0
- package/src/shared/components/Header.js +380 -0
- package/src/shared/components/HeaderLanguage.js +46 -0
- package/src/shared/components/HeaderMenu.js +126 -0
- package/src/shared/components/IFlowCookieModal.js +132 -0
- package/src/shared/components/Input.js +65 -0
- package/src/shared/components/KiroAuthModal.js +1171 -0
- package/src/shared/components/KiroOAuthWrapper.js +149 -0
- package/src/shared/components/KiroSocialOAuthModal.js +205 -0
- package/src/shared/components/LanguageSwitcher.js +190 -0
- package/src/shared/components/Loading.js +75 -0
- package/src/shared/components/ManualConfigModal.js +44 -0
- package/src/shared/components/McpMarketplaceModal.js +255 -0
- package/src/shared/components/Modal.js +146 -0
- package/src/shared/components/ModelSelectModal.js +537 -0
- package/src/shared/components/NineRemoteButton.js +23 -0
- package/src/shared/components/NineRemotePromoModal.js +99 -0
- package/src/shared/components/NoAuthProxyCard.js +86 -0
- package/src/shared/components/OAuthModal.js +682 -0
- package/src/shared/components/Pagination.js +150 -0
- package/src/shared/components/PricingModal.js +208 -0
- package/src/shared/components/ProviderIcon.js +63 -0
- package/src/shared/components/ProviderInfoCard.js +82 -0
- package/src/shared/components/RequestLogger.js +121 -0
- package/src/shared/components/SegmentedControl.js +48 -0
- package/src/shared/components/Select.js +67 -0
- package/src/shared/components/Sidebar.js +441 -0
- package/src/shared/components/ThemeProvider.js +15 -0
- package/src/shared/components/ThemeToggle.js +42 -0
- package/src/shared/components/Toggle.js +69 -0
- package/src/shared/components/Tooltip.js +25 -0
- package/src/shared/components/UsageStats.js +505 -0
- package/src/shared/components/index.js +46 -0
- package/src/shared/components/layouts/AuthLayout.js +29 -0
- package/src/shared/components/layouts/DashboardLayout.js +104 -0
- package/src/shared/components/layouts/index.js +4 -0
- package/src/shared/constants/cliTools.js +397 -0
- package/src/shared/constants/colors.js +77 -0
- package/src/shared/constants/config.js +99 -0
- package/src/shared/constants/coworkPlugins.js +75 -0
- package/src/shared/constants/index.js +4 -0
- package/src/shared/constants/locales.js +36 -0
- package/src/shared/constants/mitmToolHosts.js +12 -0
- package/src/shared/constants/models.js +38 -0
- package/src/shared/constants/pricing.js +303 -0
- package/src/shared/constants/providers.js +289 -0
- package/src/shared/constants/skills.js +78 -0
- package/src/shared/constants/ttsProviders.js +138 -0
- package/src/shared/hooks/index.js +2 -0
- package/src/shared/hooks/useCopyToClipboard.js +43 -0
- package/src/shared/hooks/useTheme.js +60 -0
- package/src/shared/services/bootstrap.js +12 -0
- package/src/shared/services/initializeApp.js +268 -0
- package/src/shared/utils/api.js +93 -0
- package/src/shared/utils/apiKey.js +98 -0
- package/src/shared/utils/clineAuth.js +37 -0
- package/src/shared/utils/cn.js +11 -0
- package/src/shared/utils/connectionStatus.js +162 -0
- package/src/shared/utils/index.js +40 -0
- package/src/shared/utils/machine.js +6 -0
- package/src/shared/utils/machineId.js +66 -0
- package/src/shared/utils/providerModelsFetcher.js +30 -0
- package/src/sse/handlers/chat.js +261 -0
- package/src/sse/handlers/embeddings.js +141 -0
- package/src/sse/handlers/fetch.js +213 -0
- package/src/sse/handlers/imageGeneration.js +142 -0
- package/src/sse/handlers/search.js +206 -0
- package/src/sse/handlers/stt.js +88 -0
- package/src/sse/handlers/tts.js +114 -0
- package/src/sse/services/auth.js +346 -0
- package/src/sse/services/codexGateway.js +215 -0
- package/src/sse/services/model.js +99 -0
- package/src/sse/services/tokenRefresh.js +319 -0
- package/src/sse/utils/logger.js +75 -0
- package/src/store/headerSearchStore.js +19 -0
- package/src/store/index.js +6 -0
- package/src/store/notificationStore.js +45 -0
- package/src/store/providerStore.js +55 -0
- package/src/store/settingsStore.js +51 -0
- package/src/store/themeStore.js +54 -0
- package/src/store/userStore.js +20 -0
- package/start.sh +4 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { PROVIDERS } from "../config/providers.js";
|
|
2
|
+
import { buildClineHeaders } from "../../src/shared/utils/clineAuth.js";
|
|
3
|
+
|
|
4
|
+
const OPENAI_COMPATIBLE_PREFIX = "openai-compatible-";
|
|
5
|
+
const OPENAI_COMPATIBLE_DEFAULTS = {
|
|
6
|
+
baseUrl: "https://api.openai.com/v1",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const ANTHROPIC_COMPATIBLE_PREFIX = "anthropic-compatible-";
|
|
10
|
+
const ANTHROPIC_COMPATIBLE_DEFAULTS = {
|
|
11
|
+
baseUrl: "https://api.anthropic.com/v1",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function isOpenAICompatible(provider) {
|
|
15
|
+
return typeof provider === "string" && provider.startsWith(OPENAI_COMPATIBLE_PREFIX);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isAnthropicCompatible(provider) {
|
|
19
|
+
return typeof provider === "string" && provider.startsWith(ANTHROPIC_COMPATIBLE_PREFIX);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getOpenAICompatibleType(provider) {
|
|
23
|
+
if (!isOpenAICompatible(provider)) return "chat";
|
|
24
|
+
return provider.includes("responses") ? "responses" : "chat";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildOpenAICompatibleUrl(baseUrl, apiType) {
|
|
28
|
+
const normalized = baseUrl.replace(/\/$/, "");
|
|
29
|
+
const path = apiType === "responses" ? "/responses" : "/chat/completions";
|
|
30
|
+
return `${normalized}${path}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function buildAnthropicCompatibleUrl(baseUrl) {
|
|
34
|
+
const normalized = baseUrl.replace(/\/$/, "");
|
|
35
|
+
return `${normalized}/messages`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildQwenBaseUrl(resourceUrl, fallbackBaseUrl) {
|
|
39
|
+
const fallback = (fallbackBaseUrl || "").replace(/\/chat\/completions$/, "");
|
|
40
|
+
const raw = typeof resourceUrl === "string" ? resourceUrl.trim() : "";
|
|
41
|
+
if (!raw) return fallback;
|
|
42
|
+
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
43
|
+
return raw.replace(/\/$/, "");
|
|
44
|
+
}
|
|
45
|
+
return `https://${raw.replace(/\/$/, "")}/v1`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Detect request format from body structure
|
|
49
|
+
export function detectFormat(body) {
|
|
50
|
+
// OpenAI Responses API: has input (array or string) instead of messages[]
|
|
51
|
+
// The Responses API accepts both input as array and input as a plain string
|
|
52
|
+
if (body.input && (Array.isArray(body.input) || typeof body.input === "string") && !body.messages) {
|
|
53
|
+
return "openai-responses";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Antigravity format: Gemini wrapped in body.request
|
|
57
|
+
if (body.request?.contents && body.userAgent === "antigravity") {
|
|
58
|
+
return "antigravity";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Gemini format: has contents array
|
|
62
|
+
if (body.contents && Array.isArray(body.contents)) {
|
|
63
|
+
return "gemini";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// OpenAI-specific indicators (check BEFORE Claude)
|
|
67
|
+
// These fields are OpenAI-specific and never appear in Claude format
|
|
68
|
+
if (
|
|
69
|
+
body.stream_options || // OpenAI streaming options
|
|
70
|
+
body.response_format || // JSON mode, etc.
|
|
71
|
+
body.logprobs !== undefined || // Log probabilities
|
|
72
|
+
body.top_logprobs !== undefined ||
|
|
73
|
+
body.n !== undefined || // Number of completions
|
|
74
|
+
body.presence_penalty !== undefined || // Penalties
|
|
75
|
+
body.frequency_penalty !== undefined ||
|
|
76
|
+
body.logit_bias || // Token biasing
|
|
77
|
+
body.user // User identifier
|
|
78
|
+
) {
|
|
79
|
+
return "openai";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Claude format: messages with content as array of objects with type
|
|
83
|
+
// Claude requires content to be array with specific structure
|
|
84
|
+
if (body.messages && Array.isArray(body.messages)) {
|
|
85
|
+
const firstMsg = body.messages[0];
|
|
86
|
+
|
|
87
|
+
// If content is array, check if it follows Claude structure
|
|
88
|
+
if (firstMsg?.content && Array.isArray(firstMsg.content)) {
|
|
89
|
+
const firstContent = firstMsg.content[0];
|
|
90
|
+
|
|
91
|
+
// Claude format has specific types: text, image, tool_use, tool_result
|
|
92
|
+
// OpenAI multimodal has: text, image_url (note the difference)
|
|
93
|
+
if (firstContent?.type === "text" && !body.model?.includes("/")) {
|
|
94
|
+
// Could be Claude or OpenAI multimodal
|
|
95
|
+
// Check for Claude-specific fields
|
|
96
|
+
if (body.system || body.anthropic_version) {
|
|
97
|
+
return "claude";
|
|
98
|
+
}
|
|
99
|
+
// Check if image format is Claude (source.type) vs OpenAI (image_url.url)
|
|
100
|
+
const hasClaudeImage = firstMsg.content.some(c =>
|
|
101
|
+
c.type === "image" && c.source?.type === "base64"
|
|
102
|
+
);
|
|
103
|
+
const hasOpenAIImage = firstMsg.content.some(c =>
|
|
104
|
+
c.type === "image_url" && c.image_url?.url
|
|
105
|
+
);
|
|
106
|
+
if (hasClaudeImage) return "claude";
|
|
107
|
+
if (hasOpenAIImage) return "openai";
|
|
108
|
+
|
|
109
|
+
// If still unclear, check for tool format
|
|
110
|
+
const hasClaudeTool = firstMsg.content.some(c =>
|
|
111
|
+
c.type === "tool_use" || c.type === "tool_result"
|
|
112
|
+
);
|
|
113
|
+
if (hasClaudeTool) return "claude";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If content is string, it's likely OpenAI (Claude also supports this)
|
|
118
|
+
// Check for other Claude-specific indicators
|
|
119
|
+
if (body.system !== undefined || body.anthropic_version) {
|
|
120
|
+
return "claude";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Default to OpenAI format
|
|
125
|
+
return "openai";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Get provider config
|
|
129
|
+
export function getProviderConfig(provider) {
|
|
130
|
+
if (isOpenAICompatible(provider)) {
|
|
131
|
+
const apiType = getOpenAICompatibleType(provider);
|
|
132
|
+
return {
|
|
133
|
+
...PROVIDERS.openai,
|
|
134
|
+
format: apiType === "responses" ? "openai-responses" : "openai",
|
|
135
|
+
baseUrl: OPENAI_COMPATIBLE_DEFAULTS.baseUrl,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (isAnthropicCompatible(provider)) {
|
|
139
|
+
return {
|
|
140
|
+
...PROVIDERS.anthropic, // Use Anthropic defaults (header: x-api-key)
|
|
141
|
+
format: "claude",
|
|
142
|
+
baseUrl: ANTHROPIC_COMPATIBLE_DEFAULTS.baseUrl,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return PROVIDERS[provider] || PROVIDERS.openai;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Get number of fallback URLs for provider (for retry logic)
|
|
149
|
+
export function getProviderFallbackCount(provider) {
|
|
150
|
+
const config = getProviderConfig(provider);
|
|
151
|
+
return config.baseUrls?.length || 1;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Build provider URL
|
|
155
|
+
export function buildProviderUrl(provider, model, stream = true, options = {}) {
|
|
156
|
+
if (isOpenAICompatible(provider)) {
|
|
157
|
+
const apiType = getOpenAICompatibleType(provider);
|
|
158
|
+
const baseUrl = options?.baseUrl || OPENAI_COMPATIBLE_DEFAULTS.baseUrl;
|
|
159
|
+
return buildOpenAICompatibleUrl(baseUrl, apiType);
|
|
160
|
+
}
|
|
161
|
+
if (isAnthropicCompatible(provider)) {
|
|
162
|
+
const baseUrl = options?.baseUrl || ANTHROPIC_COMPATIBLE_DEFAULTS.baseUrl;
|
|
163
|
+
return buildAnthropicCompatibleUrl(baseUrl);
|
|
164
|
+
}
|
|
165
|
+
const config = getProviderConfig(provider);
|
|
166
|
+
|
|
167
|
+
switch (provider) {
|
|
168
|
+
case "claude":
|
|
169
|
+
return `${config.baseUrl}?beta=true`;
|
|
170
|
+
|
|
171
|
+
case "gemini": {
|
|
172
|
+
const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
|
|
173
|
+
return `${config.baseUrl}/${model}:${action}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
case "gemini-cli": {
|
|
177
|
+
const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
|
|
178
|
+
return `${config.baseUrl}:${action}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
case "antigravity": {
|
|
182
|
+
// Use baseUrlIndex from options or default to 0
|
|
183
|
+
const urlIndex = options?.baseUrlIndex || 0;
|
|
184
|
+
const baseUrl = config.baseUrls[urlIndex] || config.baseUrls[0];
|
|
185
|
+
const path = stream ? "/v1internal:streamGenerateContent?alt=sse" : "/v1internal:generateContent";
|
|
186
|
+
return `${baseUrl}${path}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case "codex":
|
|
190
|
+
return config.baseUrl;
|
|
191
|
+
|
|
192
|
+
case "qwen": {
|
|
193
|
+
const baseUrl = buildQwenBaseUrl(options?.qwenResourceUrl, config.baseUrl);
|
|
194
|
+
return `${baseUrl}/chat/completions`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
case "github":
|
|
198
|
+
return config.baseUrl;
|
|
199
|
+
|
|
200
|
+
case "glm":
|
|
201
|
+
case "kimi":
|
|
202
|
+
case "minimax":
|
|
203
|
+
// Claude-compatible providers
|
|
204
|
+
return `${config.baseUrl}?beta=true`;
|
|
205
|
+
|
|
206
|
+
default:
|
|
207
|
+
return config.baseUrl;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Build provider headers
|
|
212
|
+
export function buildProviderHeaders(provider, credentials, stream = true, body = null) {
|
|
213
|
+
const config = getProviderConfig(provider);
|
|
214
|
+
const headers = {
|
|
215
|
+
"Content-Type": "application/json",
|
|
216
|
+
...config.headers
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Add auth header
|
|
220
|
+
// Specific override for Anthropic Compatible
|
|
221
|
+
if (isAnthropicCompatible(provider)) {
|
|
222
|
+
if (credentials.apiKey) {
|
|
223
|
+
headers["x-api-key"] = credentials.apiKey;
|
|
224
|
+
// Do NOT send Authorization header when apiKey is present for Anthropic Compatible
|
|
225
|
+
// as it causes issues with some providers (e.g. opencode.ai)
|
|
226
|
+
} else if (credentials.accessToken) {
|
|
227
|
+
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
228
|
+
}
|
|
229
|
+
// Add default Anthropic version if not present (some proxies require it)
|
|
230
|
+
if (!headers["anthropic-version"]) {
|
|
231
|
+
headers["anthropic-version"] = "2023-06-01";
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
switch (provider) {
|
|
235
|
+
case "gemini":
|
|
236
|
+
if (credentials.apiKey) {
|
|
237
|
+
headers["x-goog-api-key"] = credentials.apiKey;
|
|
238
|
+
} else if (credentials.accessToken) {
|
|
239
|
+
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
|
|
243
|
+
case "antigravity":
|
|
244
|
+
case "gemini-cli":
|
|
245
|
+
// Antigravity and Gemini CLI use OAuth access token
|
|
246
|
+
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
247
|
+
break;
|
|
248
|
+
|
|
249
|
+
case "claude":
|
|
250
|
+
// Claude uses x-api-key header for API key, or Authorization for OAuth
|
|
251
|
+
if (credentials.apiKey) {
|
|
252
|
+
headers["x-api-key"] = credentials.apiKey;
|
|
253
|
+
} else if (credentials.accessToken) {
|
|
254
|
+
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
|
|
258
|
+
case "github": {
|
|
259
|
+
// GitHub Copilot requires special headers to mimic VSCode
|
|
260
|
+
// Prioritize copilotToken from providerSpecificData, fallback to accessToken
|
|
261
|
+
const githubToken = credentials.copilotToken || credentials.accessToken;
|
|
262
|
+
// Add headers in exact same order as test endpoint
|
|
263
|
+
headers["Authorization"] = `Bearer ${githubToken}`;
|
|
264
|
+
headers["Content-Type"] = "application/json";
|
|
265
|
+
headers["copilot-integration-id"] = "vscode-chat";
|
|
266
|
+
headers["editor-version"] = "vscode/1.107.1";
|
|
267
|
+
headers["editor-plugin-version"] = "copilot-chat/0.26.7";
|
|
268
|
+
headers["user-agent"] = "GitHubCopilotChat/0.26.7";
|
|
269
|
+
headers["openai-intent"] = "conversation-panel";
|
|
270
|
+
headers["x-github-api-version"] = "2025-04-01";
|
|
271
|
+
// Generate a UUID for x-request-id (Cloudflare Workers compatible)
|
|
272
|
+
headers["x-request-id"] = crypto.randomUUID ? crypto.randomUUID() :
|
|
273
|
+
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
274
|
+
const r = Math.random() * 16 | 0;
|
|
275
|
+
const v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
276
|
+
return v.toString(16);
|
|
277
|
+
});
|
|
278
|
+
headers["x-vscode-user-agent-library-version"] = "electron-fetch";
|
|
279
|
+
headers["X-Initiator"] = "user";
|
|
280
|
+
headers["Accept"] = "application/json";
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
case "codex":
|
|
285
|
+
case "qwen":
|
|
286
|
+
case "openai":
|
|
287
|
+
case "openrouter":
|
|
288
|
+
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
|
|
289
|
+
break;
|
|
290
|
+
|
|
291
|
+
case "cline":
|
|
292
|
+
Object.assign(headers, buildClineHeaders(credentials.apiKey || credentials.accessToken));
|
|
293
|
+
break;
|
|
294
|
+
|
|
295
|
+
case "glm":
|
|
296
|
+
case "kimi":
|
|
297
|
+
case "minimax":
|
|
298
|
+
// Claude-compatible API providers use x-api-key
|
|
299
|
+
headers["x-api-key"] = credentials.apiKey;
|
|
300
|
+
break;
|
|
301
|
+
|
|
302
|
+
case "vertex":
|
|
303
|
+
case "vertex-partner":
|
|
304
|
+
// Vertex uses async token minting — headers are set by VertexExecutor._buildHeadersAsync()
|
|
305
|
+
// Do NOT set Authorization here; it would leak the raw SA JSON as Bearer token
|
|
306
|
+
break;
|
|
307
|
+
|
|
308
|
+
default:
|
|
309
|
+
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Stream accept header
|
|
315
|
+
if (stream) {
|
|
316
|
+
headers["Accept"] = "text/event-stream";
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return headers;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Get target format for provider
|
|
323
|
+
export function getTargetFormat(provider) {
|
|
324
|
+
if (isOpenAICompatible(provider)) {
|
|
325
|
+
return getOpenAICompatibleType(provider) === "responses" ? "openai-responses" : "openai";
|
|
326
|
+
}
|
|
327
|
+
if (isAnthropicCompatible(provider)) {
|
|
328
|
+
return "claude";
|
|
329
|
+
}
|
|
330
|
+
const config = getProviderConfig(provider);
|
|
331
|
+
return config.format || "openai";
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Check if last message is from user
|
|
335
|
+
export function isLastMessageFromUser(body) {
|
|
336
|
+
const messages = body.messages || body.contents;
|
|
337
|
+
if (!messages?.length) return true;
|
|
338
|
+
const lastMsg = messages[messages.length - 1];
|
|
339
|
+
return lastMsg?.role === "user";
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Check if request has thinking config
|
|
343
|
+
export function hasThinkingConfig(body) {
|
|
344
|
+
return !!(body.reasoning_effort || body.thinking?.type === "enabled");
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Normalize thinking config based on last message role
|
|
348
|
+
// - If lastMessage is not user → remove thinking config
|
|
349
|
+
// - If lastMessage is user AND has thinking config → keep it (force enable)
|
|
350
|
+
export function normalizeThinkingConfig(body) {
|
|
351
|
+
if (!isLastMessageFromUser(body)) {
|
|
352
|
+
delete body.reasoning_effort;
|
|
353
|
+
delete body.thinking;
|
|
354
|
+
}
|
|
355
|
+
return body;
|
|
356
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qoder model catalog fetcher.
|
|
3
|
+
*
|
|
4
|
+
* Calls /algo/api/v2/model/list (COSY-signed) on the inference host to get
|
|
5
|
+
* the live catalog for an authenticated Qoder account, then caches the
|
|
6
|
+
* per-model `model_config` blocks by key. Chat requests later look up the
|
|
7
|
+
* exact server-published metadata for the model they want — Qoder's chat
|
|
8
|
+
* endpoint silently downgrades to a different model when the wrong
|
|
9
|
+
* model_config is sent.
|
|
10
|
+
*
|
|
11
|
+
* On any error the live cache stays empty and chatExecuteCall surfaces the
|
|
12
|
+
* problem to the user as "model config not yet fetched, retry shortly".
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { createHash } from "crypto";
|
|
16
|
+
|
|
17
|
+
import { proxyAwareFetch } from "../utils/proxyFetch.js";
|
|
18
|
+
import { buildCosyHeaders } from "@/lib/qoder/cosy.js";
|
|
19
|
+
import {
|
|
20
|
+
QODER_MODEL_LIST_URL,
|
|
21
|
+
} from "@/lib/qoder/constants.js";
|
|
22
|
+
|
|
23
|
+
const FETCH_TIMEOUT_MS = 15_000;
|
|
24
|
+
const CACHE_TTL_MS = 60 * 60 * 1000; // 1h, same as the Kiro catalog
|
|
25
|
+
|
|
26
|
+
/** @type {Map<string, { expiresAt: number, models: any[], rawConfigs: Map<string, object>, fetched: boolean }>} */
|
|
27
|
+
const catalogCache = new Map();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* In-flight fetch promises keyed by cacheKey. Concurrent first-time
|
|
31
|
+
* callers (parallel chat windows) all observe the same Promise so we
|
|
32
|
+
* fan-out exactly one upstream request per credential per miss.
|
|
33
|
+
* @type {Map<string, Promise<{ expiresAt: number, models: any[], rawConfigs: Map<string, object>, fetched: boolean } | null>>}
|
|
34
|
+
*/
|
|
35
|
+
const inflight = new Map();
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Stable cache key per credential (so different login sessions for the same
|
|
39
|
+
* account share an entry).
|
|
40
|
+
*/
|
|
41
|
+
function cacheKey(credentials) {
|
|
42
|
+
const psd = credentials?.providerSpecificData || {};
|
|
43
|
+
const seed = psd.userId || credentials?.refreshToken || credentials?.accessToken || "anonymous";
|
|
44
|
+
return createHash("sha256").update(`qoder:${seed}`).digest("hex");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Strip credential -> COSY creds for buildCosyHeaders.
|
|
49
|
+
*/
|
|
50
|
+
function cosyCredsFromConnection(credentials) {
|
|
51
|
+
const psd = credentials?.providerSpecificData || {};
|
|
52
|
+
return {
|
|
53
|
+
userId: psd.userId,
|
|
54
|
+
authToken: credentials.accessToken,
|
|
55
|
+
name: credentials.displayName || "",
|
|
56
|
+
email: credentials.email || "",
|
|
57
|
+
machineId: psd.machineId || "",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Fetch the live model list for this credential. Returns:
|
|
63
|
+
* { models: [{ id, name, contextLength, isVL, isReasoning, ... }, ...],
|
|
64
|
+
* rawConfigs: Map<modelKey, modelConfigObject> }
|
|
65
|
+
* or `null` on any error.
|
|
66
|
+
*/
|
|
67
|
+
async function fetchQoderCatalogRaw(credentials, signal, proxyOptions = null) {
|
|
68
|
+
const creds = cosyCredsFromConnection(credentials);
|
|
69
|
+
if (!creds.userId || !creds.authToken) return null;
|
|
70
|
+
|
|
71
|
+
const headers = {
|
|
72
|
+
Accept: "application/json",
|
|
73
|
+
"Accept-Encoding": "identity",
|
|
74
|
+
...buildCosyHeaders(Buffer.alloc(0), QODER_MODEL_LIST_URL, creds),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const controller = new AbortController();
|
|
78
|
+
let timer = null;
|
|
79
|
+
let abortListener = null;
|
|
80
|
+
let response;
|
|
81
|
+
try {
|
|
82
|
+
timer = setTimeout(() => controller.abort("timeout"), FETCH_TIMEOUT_MS);
|
|
83
|
+
if (signal && typeof signal.addEventListener === "function") {
|
|
84
|
+
// If the parent signal already aborted before we got here, the
|
|
85
|
+
// 'abort' event has already fired and addEventListener won't
|
|
86
|
+
// re-trigger it. Propagate the cancellation immediately.
|
|
87
|
+
if (signal.aborted) {
|
|
88
|
+
controller.abort(signal.reason);
|
|
89
|
+
} else {
|
|
90
|
+
abortListener = () => controller.abort(signal.reason);
|
|
91
|
+
signal.addEventListener("abort", abortListener);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
response = await proxyAwareFetch(
|
|
95
|
+
QODER_MODEL_LIST_URL,
|
|
96
|
+
{
|
|
97
|
+
method: "GET",
|
|
98
|
+
headers,
|
|
99
|
+
signal: controller.signal,
|
|
100
|
+
},
|
|
101
|
+
proxyOptions,
|
|
102
|
+
);
|
|
103
|
+
} finally {
|
|
104
|
+
if (timer) clearTimeout(timer);
|
|
105
|
+
if (signal && abortListener) signal.removeEventListener("abort", abortListener);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!response.ok) return null;
|
|
109
|
+
|
|
110
|
+
const body = await response.json().catch(() => null);
|
|
111
|
+
if (!body || !Array.isArray(body.chat)) return null;
|
|
112
|
+
|
|
113
|
+
const models = [];
|
|
114
|
+
const rawConfigs = new Map();
|
|
115
|
+
for (const entry of body.chat) {
|
|
116
|
+
if (!entry || typeof entry !== "object") continue;
|
|
117
|
+
const key = entry.key;
|
|
118
|
+
if (!key) continue;
|
|
119
|
+
|
|
120
|
+
// Always cache the config — chat needs model_config even for UI-hidden
|
|
121
|
+
// models (enable:false). Upstream still accepts chat for these keys.
|
|
122
|
+
rawConfigs.set(key, entry);
|
|
123
|
+
if (entry.enable === false) continue;
|
|
124
|
+
|
|
125
|
+
const display = entry.display_name || key;
|
|
126
|
+
const ctx = Number(entry.max_input_tokens) || 131_072;
|
|
127
|
+
models.push({
|
|
128
|
+
id: key,
|
|
129
|
+
name: `${display}`,
|
|
130
|
+
contextLength: ctx,
|
|
131
|
+
isVL: !!entry.is_vl,
|
|
132
|
+
isReasoning: !!entry.is_reasoning,
|
|
133
|
+
maxOutputTokens: Number(entry.max_output_tokens) || 0,
|
|
134
|
+
description: entry.description || "",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { models, rawConfigs };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get the cached model_config block for a given model key, fetching the
|
|
143
|
+
* catalog first if needed. Returns null when the catalog can't be fetched
|
|
144
|
+
* (so callers can fall back to the static registry).
|
|
145
|
+
*/
|
|
146
|
+
export async function getQoderModelConfig(credentials, modelKey, options = {}) {
|
|
147
|
+
const cached = await resolveQoderModels(credentials, options);
|
|
148
|
+
if (!cached) return null;
|
|
149
|
+
const config = cached.rawConfigs.get(modelKey);
|
|
150
|
+
if (!config) return null;
|
|
151
|
+
// Defensive copy — chat code may mutate `key` to align with the alias path.
|
|
152
|
+
return { ...config, key: modelKey };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Resolve the live model catalog + raw configs for a credential. Caches
|
|
157
|
+
* results for CACHE_TTL_MS so repeated chat requests don't re-fetch, and
|
|
158
|
+
* deduplicates concurrent misses so parallel chat windows fan-out exactly
|
|
159
|
+
* one upstream request per credential.
|
|
160
|
+
*/
|
|
161
|
+
export async function resolveQoderModels(credentials, options = {}) {
|
|
162
|
+
if (!credentials?.accessToken) return null;
|
|
163
|
+
const psd = credentials.providerSpecificData || {};
|
|
164
|
+
if (!psd.userId) return null;
|
|
165
|
+
|
|
166
|
+
const key = cacheKey(credentials);
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
if (!options.forceRefresh) {
|
|
169
|
+
const cached = catalogCache.get(key);
|
|
170
|
+
if (cached && cached.expiresAt > now) {
|
|
171
|
+
return cached;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Coalesce concurrent misses on the same credential into one upstream call.
|
|
176
|
+
// forceRefresh callers still get their own fetch (they wanted fresh data).
|
|
177
|
+
const existing = inflight.get(key);
|
|
178
|
+
if (existing && !options.forceRefresh) {
|
|
179
|
+
return existing;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const fetchPromise = (async () => {
|
|
183
|
+
const fetched = await fetchQoderCatalogRaw(credentials, options.signal, options.proxyOptions);
|
|
184
|
+
if (!fetched) return null;
|
|
185
|
+
const entry = {
|
|
186
|
+
expiresAt: Date.now() + CACHE_TTL_MS,
|
|
187
|
+
models: fetched.models,
|
|
188
|
+
rawConfigs: fetched.rawConfigs,
|
|
189
|
+
fetched: true,
|
|
190
|
+
};
|
|
191
|
+
catalogCache.set(key, entry);
|
|
192
|
+
return entry;
|
|
193
|
+
})();
|
|
194
|
+
|
|
195
|
+
inflight.set(key, fetchPromise);
|
|
196
|
+
try {
|
|
197
|
+
return await fetchPromise;
|
|
198
|
+
} finally {
|
|
199
|
+
// Clear only if this is still the in-flight entry — a forceRefresh
|
|
200
|
+
// call that started later may have replaced it.
|
|
201
|
+
if (inflight.get(key) === fetchPromise) {
|
|
202
|
+
inflight.delete(key);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function invalidateQoderCatalog(credentials) {
|
|
208
|
+
if (!credentials) return;
|
|
209
|
+
catalogCache.delete(cacheKey(credentials));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function clearQoderCatalog() {
|
|
213
|
+
catalogCache.clear();
|
|
214
|
+
}
|