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,185 @@
|
|
|
1
|
+
import { HTTP_STATUS, RETRY_CONFIG, DEFAULT_RETRY_CONFIG, resolveRetryEntry, FETCH_CONNECT_TIMEOUT_MS } from "../config/runtimeConfig.js";
|
|
2
|
+
import { shouldRefreshCredentials } from "../services/oauthCredentialManager.js";
|
|
3
|
+
import { proxyAwareFetch } from "../utils/proxyFetch.js";
|
|
4
|
+
import { dbg } from "../utils/debugLog.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* BaseExecutor - Base class for provider executors
|
|
8
|
+
*/
|
|
9
|
+
export class BaseExecutor {
|
|
10
|
+
constructor(provider, config) {
|
|
11
|
+
this.provider = provider;
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.noAuth = config?.noAuth || false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getProvider() {
|
|
17
|
+
return this.provider;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getBaseUrls() {
|
|
21
|
+
return this.config.baseUrls || (this.config.baseUrl ? [this.config.baseUrl] : []);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getFallbackCount() {
|
|
25
|
+
return this.getBaseUrls().length || 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
buildUrl(model, stream, urlIndex = 0, credentials = null) {
|
|
29
|
+
if (this.provider?.startsWith?.("openai-compatible-")) {
|
|
30
|
+
const baseUrl = credentials?.providerSpecificData?.baseUrl || "https://api.openai.com/v1";
|
|
31
|
+
const normalized = baseUrl.replace(/\/$/, "");
|
|
32
|
+
const path = this.provider.includes("responses") ? "/responses" : "/chat/completions";
|
|
33
|
+
return `${normalized}${path}`;
|
|
34
|
+
}
|
|
35
|
+
if (this.provider?.startsWith?.("anthropic-compatible-")) {
|
|
36
|
+
const baseUrl = credentials?.providerSpecificData?.baseUrl || "https://api.anthropic.com/v1";
|
|
37
|
+
const normalized = baseUrl.replace(/\/$/, "");
|
|
38
|
+
return `${normalized}/messages`;
|
|
39
|
+
}
|
|
40
|
+
const baseUrls = this.getBaseUrls();
|
|
41
|
+
return baseUrls[urlIndex] || baseUrls[0] || this.config.baseUrl;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
buildHeaders(credentials, stream = true) {
|
|
45
|
+
const headers = {
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
...this.config.headers
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (this.provider?.startsWith?.("anthropic-compatible-")) {
|
|
51
|
+
// Anthropic-compatible providers use x-api-key header
|
|
52
|
+
if (credentials.apiKey) {
|
|
53
|
+
headers["x-api-key"] = credentials.apiKey;
|
|
54
|
+
} else if (credentials.accessToken) {
|
|
55
|
+
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
56
|
+
}
|
|
57
|
+
if (!headers["anthropic-version"]) {
|
|
58
|
+
headers["anthropic-version"] = "2023-06-01";
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// Standard Bearer token auth for other providers
|
|
62
|
+
if (credentials.accessToken) {
|
|
63
|
+
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
64
|
+
} else if (credentials.apiKey) {
|
|
65
|
+
headers["Authorization"] = `Bearer ${credentials.apiKey}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (stream) {
|
|
70
|
+
headers["Accept"] = "text/event-stream";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return headers;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Override in subclass for provider-specific transformations
|
|
77
|
+
transformRequest(model, body, stream, credentials) {
|
|
78
|
+
return body;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Override in subclass for providers that need encoded/binary request bodies.
|
|
82
|
+
prepareRequestBody(transformedBody, headers) {
|
|
83
|
+
return JSON.stringify(transformedBody);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
shouldRetry(status, urlIndex) {
|
|
87
|
+
return status === HTTP_STATUS.RATE_LIMITED && urlIndex + 1 < this.getFallbackCount();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Override in subclass for provider-specific refresh
|
|
91
|
+
async refreshCredentials(credentials, log, proxyOptions = null) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
needsRefresh(credentials) {
|
|
96
|
+
return shouldRefreshCredentials(this.provider, credentials);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
parseError(response, bodyText) {
|
|
100
|
+
return { status: response.status, message: bodyText || `HTTP ${response.status}` };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async execute({ model, body, stream, credentials, signal, log, proxyOptions = null }) {
|
|
104
|
+
const fallbackCount = this.getFallbackCount();
|
|
105
|
+
let lastError = null;
|
|
106
|
+
let lastStatus = 0;
|
|
107
|
+
const retryAttemptsByUrl = {};
|
|
108
|
+
|
|
109
|
+
// Merge default retry config with provider-specific config
|
|
110
|
+
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.config.retry };
|
|
111
|
+
|
|
112
|
+
// Schedule retry via retryConfig[statusKey]. Returns true when caller should `urlIndex--; continue`
|
|
113
|
+
const tryRetry = async (urlIndex, statusKey, reason) => {
|
|
114
|
+
const { attempts, delayMs } = resolveRetryEntry(retryConfig[statusKey]);
|
|
115
|
+
if (attempts <= 0 || retryAttemptsByUrl[urlIndex] >= attempts) return false;
|
|
116
|
+
retryAttemptsByUrl[urlIndex]++;
|
|
117
|
+
log?.debug?.("RETRY", `${reason} retry ${retryAttemptsByUrl[urlIndex]}/${attempts} after ${delayMs / 1000}s`);
|
|
118
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
119
|
+
return true;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
|
|
123
|
+
const url = this.buildUrl(model, stream, urlIndex, credentials);
|
|
124
|
+
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
|
125
|
+
const headers = this.buildHeaders(credentials, stream);
|
|
126
|
+
|
|
127
|
+
if (!retryAttemptsByUrl[urlIndex]) retryAttemptsByUrl[urlIndex] = 0;
|
|
128
|
+
|
|
129
|
+
// Abort if upstream doesn't return response headers within connection timeout
|
|
130
|
+
const connectCtrl = new AbortController();
|
|
131
|
+
const timeoutMs = this.config?.timeoutMs || FETCH_CONNECT_TIMEOUT_MS;
|
|
132
|
+
const connectTimer = setTimeout(() => connectCtrl.abort(new Error("fetch connect timeout")), timeoutMs);
|
|
133
|
+
const mergedSignal = signal ? AbortSignal.any([signal, connectCtrl.signal]) : connectCtrl.signal;
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const requestBody = this.prepareRequestBody(transformedBody, headers);
|
|
137
|
+
const requestBodySize = typeof requestBody === "string"
|
|
138
|
+
? requestBody.length
|
|
139
|
+
: requestBody?.byteLength ?? requestBody?.length ?? "?";
|
|
140
|
+
const fetchT0 = Date.now();
|
|
141
|
+
dbg("FETCH", `${this.provider.toUpperCase()} → ${url} | body=${requestBodySize}B | connectTimeout=${timeoutMs}ms`);
|
|
142
|
+
const response = await proxyAwareFetch(url, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers,
|
|
145
|
+
body: requestBody,
|
|
146
|
+
signal: mergedSignal
|
|
147
|
+
}, proxyOptions);
|
|
148
|
+
clearTimeout(connectTimer);
|
|
149
|
+
const ct = response.headers?.get?.("content-type") || "";
|
|
150
|
+
const cl = response.headers?.get?.("content-length") || "?";
|
|
151
|
+
dbg("FETCH", `${this.provider.toUpperCase()} ← ${response.status} | ttft=${Date.now() - fetchT0}ms | ct=${ct} | cl=${cl}`);
|
|
152
|
+
|
|
153
|
+
if (await tryRetry(urlIndex, response.status, `status ${response.status}`)) { urlIndex--; continue; }
|
|
154
|
+
|
|
155
|
+
if (this.shouldRetry(response.status, urlIndex)) {
|
|
156
|
+
log?.debug?.("RETRY", `${response.status} on ${url}, trying fallback ${urlIndex + 1}`);
|
|
157
|
+
lastStatus = response.status;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { response, url, headers, transformedBody };
|
|
162
|
+
} catch (error) {
|
|
163
|
+
clearTimeout(connectTimer);
|
|
164
|
+
lastError = error;
|
|
165
|
+
const isConnectTimeout = connectCtrl.signal.aborted && error.name === "AbortError";
|
|
166
|
+
dbg("FETCH", `${this.provider.toUpperCase()} ✖ ${error.name}: ${error.message}${isConnectTimeout ? " (connect timeout)" : ""}`);
|
|
167
|
+
// Connect timeout is internal — convert to retryable network error, don't propagate AbortError
|
|
168
|
+
if (error.name === "AbortError" && !isConnectTimeout) throw error;
|
|
169
|
+
|
|
170
|
+
// Map network/fetch exceptions to 502 retry config
|
|
171
|
+
if (await tryRetry(urlIndex, HTTP_STATUS.BAD_GATEWAY, `network "${error.message}"`)) { urlIndex--; continue; }
|
|
172
|
+
|
|
173
|
+
if (urlIndex + 1 < fallbackCount) {
|
|
174
|
+
log?.debug?.("RETRY", `Error on ${url}, trying fallback ${urlIndex + 1}`);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
throw lastError || new Error(`All ${fallbackCount} URLs failed with status ${lastStatus}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export default BaseExecutor;
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { BaseExecutor } from "./base.js";
|
|
3
|
+
import { CODEX_DEFAULT_INSTRUCTIONS } from "../config/codexInstructions.js";
|
|
4
|
+
import { PROVIDERS } from "../config/providers.js";
|
|
5
|
+
import {
|
|
6
|
+
refreshProviderCredentials,
|
|
7
|
+
shouldRefreshCredentials,
|
|
8
|
+
} from "../services/oauthCredentialManager.js";
|
|
9
|
+
import { normalizeResponsesInput } from "../translator/helpers/responsesApiHelper.js";
|
|
10
|
+
import { fetchImageAsBase64 } from "../translator/helpers/imageHelper.js";
|
|
11
|
+
import { getModelUpstreamId } from "../config/providerModels.js";
|
|
12
|
+
import { getConsistentMachineId } from "../../src/shared/utils/machineId.js";
|
|
13
|
+
import { DEFAULT_RETRY_CONFIG, resolveRetryEntry } from "../config/runtimeConfig.js";
|
|
14
|
+
import { dbg } from "../utils/debugLog.js";
|
|
15
|
+
|
|
16
|
+
// SSE error patterns inside 200-OK body that should trigger retry as if 503
|
|
17
|
+
const CODEX_SSE_OVERLOADED_PATTERNS = ["server_is_overloaded", "service_unavailable_error"];
|
|
18
|
+
const CODEX_SSE_PEEK_BYTES = 4096;
|
|
19
|
+
|
|
20
|
+
// In-memory map: hash(machineId + first assistant content) → { sessionId, lastUsed }
|
|
21
|
+
const SESSION_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
22
|
+
const assistantSessionMap = new Map();
|
|
23
|
+
|
|
24
|
+
// Server-generated item id prefixes that Codex /responses cannot resolve when store=false
|
|
25
|
+
const SERVER_ID_PATTERN = /^(rs|fc|resp|msg)_/;
|
|
26
|
+
|
|
27
|
+
// Hosted tool types that Codex/OpenAI Responses executes server-side
|
|
28
|
+
const CODEX_HOSTED_TOOL_TYPES = new Set([
|
|
29
|
+
"image_generation", "web_search", "web_search_preview", "file_search",
|
|
30
|
+
"computer", "computer_use_preview", "code_interpreter", "mcp", "local_shell"
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
// Allowlist of fields accepted by Codex Responses API — anything else is stripped
|
|
34
|
+
const RESPONSES_API_ALLOWLIST = new Set([
|
|
35
|
+
"model", "input", "instructions", "tools", "tool_choice", "stream", "store",
|
|
36
|
+
"reasoning", "service_tier", "include", "prompt_cache_key", "client_metadata"
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
// Convert role=system → role=developer in body.input (keeps content in cacheable prefix)
|
|
40
|
+
function convertSystemToDeveloperRole(body) {
|
|
41
|
+
if (!Array.isArray(body.input)) return;
|
|
42
|
+
for (const item of body.input) {
|
|
43
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
44
|
+
const isSystemMsg = item.role === "system" && (!item.type || item.type === "message");
|
|
45
|
+
if (isSystemMsg) item.role = "developer";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Strip server-generated item IDs (rs_/fc_/resp_/msg_) from input — avoids 404 with store=false
|
|
50
|
+
function stripStoredItemReferences(body) {
|
|
51
|
+
if (!Array.isArray(body.input)) return;
|
|
52
|
+
body.input = body.input.filter((item) => {
|
|
53
|
+
if (typeof item === "string" && SERVER_ID_PATTERN.test(item)) return false;
|
|
54
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
55
|
+
if (item.type === "item_reference") return false;
|
|
56
|
+
if (typeof item.id === "string" && SERVER_ID_PATTERN.test(item.id)) delete item.id;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Flatten Chat-Completions tool shape into Responses flat format + filter unsupported tools
|
|
63
|
+
function normalizeCodexTools(body) {
|
|
64
|
+
if (!Array.isArray(body.tools)) return;
|
|
65
|
+
const validNames = new Set();
|
|
66
|
+
body.tools = body.tools.filter((tool) => {
|
|
67
|
+
if (!tool || typeof tool !== "object" || Array.isArray(tool)) return false;
|
|
68
|
+
const type = typeof tool.type === "string" ? tool.type : "";
|
|
69
|
+
if (type === "namespace") {
|
|
70
|
+
if (Array.isArray(tool.tools)) {
|
|
71
|
+
for (const st of tool.tools) {
|
|
72
|
+
const n = typeof st?.name === "string" ? st.name.trim().slice(0, 128) : "";
|
|
73
|
+
if (n) validNames.add(n);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (type !== "function") {
|
|
79
|
+
if (!type || tool.function || typeof tool.name === "string") return false;
|
|
80
|
+
return CODEX_HOSTED_TOOL_TYPES.has(type);
|
|
81
|
+
}
|
|
82
|
+
const fn = tool.function && typeof tool.function === "object" && !Array.isArray(tool.function) ? tool.function : null;
|
|
83
|
+
const rawName = typeof tool.name === "string" ? tool.name : (typeof fn?.name === "string" ? fn.name : "");
|
|
84
|
+
const name = rawName.trim();
|
|
85
|
+
if (!name) return false;
|
|
86
|
+
const description = typeof tool.description === "string" ? tool.description : (typeof fn?.description === "string" ? fn.description : "");
|
|
87
|
+
const parameters = (tool.parameters && typeof tool.parameters === "object" && !Array.isArray(tool.parameters))
|
|
88
|
+
? tool.parameters
|
|
89
|
+
: (fn?.parameters && typeof fn.parameters === "object" && !Array.isArray(fn.parameters) ? fn.parameters : { type: "object", properties: {} });
|
|
90
|
+
for (const k of Object.keys(tool)) delete tool[k];
|
|
91
|
+
tool.type = "function";
|
|
92
|
+
tool.name = name.slice(0, 128);
|
|
93
|
+
if (description) tool.description = description;
|
|
94
|
+
tool.parameters = parameters;
|
|
95
|
+
validNames.add(name);
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
// Drop tool_choice if it references an unknown function name
|
|
99
|
+
if (body.tool_choice && typeof body.tool_choice === "object" && !Array.isArray(body.tool_choice)) {
|
|
100
|
+
if (body.tool_choice.type === "function") {
|
|
101
|
+
const n = typeof body.tool_choice.name === "string" ? body.tool_choice.name.trim() : "";
|
|
102
|
+
if (!n || !validNames.has(n)) delete body.tool_choice;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Cache machine ID at module level (resolved once)
|
|
108
|
+
let cachedMachineId = null;
|
|
109
|
+
getConsistentMachineId().then(id => { cachedMachineId = id; });
|
|
110
|
+
|
|
111
|
+
function hashContent(text) {
|
|
112
|
+
return createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function generateSessionId() {
|
|
116
|
+
return `sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Extract text content from an input item
|
|
120
|
+
function extractItemText(item) {
|
|
121
|
+
if (!item) return "";
|
|
122
|
+
if (typeof item.content === "string") return item.content;
|
|
123
|
+
if (Array.isArray(item.content)) {
|
|
124
|
+
return item.content.map(c => c.text || c.output || "").filter(Boolean).join("");
|
|
125
|
+
}
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Normalize a session id candidate (trim, length cap)
|
|
130
|
+
function normalizeSessionId(value) {
|
|
131
|
+
if (typeof value !== "string") return null;
|
|
132
|
+
const v = value.trim();
|
|
133
|
+
if (!v || v.length > 256) return null;
|
|
134
|
+
return v;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Resolve prompt-cache session id with priority: body → assistant-text-hash → workspaceId → machineId
|
|
138
|
+
function resolveCacheSessionId(body, credentials, machineId) {
|
|
139
|
+
// 1. Client-provided session/conversation id (highest priority — stable per conversation)
|
|
140
|
+
const fromBody =
|
|
141
|
+
normalizeSessionId(body?.prompt_cache_key) ||
|
|
142
|
+
normalizeSessionId(body?.session_id) ||
|
|
143
|
+
normalizeSessionId(body?.conversation_id);
|
|
144
|
+
if (fromBody) return fromBody;
|
|
145
|
+
|
|
146
|
+
// 2. Hash accumulated assistant text (≥50 chars) — sticky session across turns
|
|
147
|
+
if (Array.isArray(body?.input) && body.input.length > 0) {
|
|
148
|
+
let text = "";
|
|
149
|
+
const MIN_LEN = 50;
|
|
150
|
+
const CAP_LEN = 200;
|
|
151
|
+
for (const item of body.input) {
|
|
152
|
+
if (item?.role !== "assistant") continue;
|
|
153
|
+
const t = extractItemText(item);
|
|
154
|
+
if (!t) continue;
|
|
155
|
+
text += t;
|
|
156
|
+
if (text.length >= CAP_LEN) break;
|
|
157
|
+
}
|
|
158
|
+
if (text.length >= MIN_LEN) {
|
|
159
|
+
const hash = hashContent((machineId || "") + text.slice(0, CAP_LEN));
|
|
160
|
+
const entry = assistantSessionMap.get(hash);
|
|
161
|
+
if (entry) {
|
|
162
|
+
entry.lastUsed = Date.now();
|
|
163
|
+
return entry.sessionId;
|
|
164
|
+
}
|
|
165
|
+
const sessionId = generateSessionId();
|
|
166
|
+
assistantSessionMap.set(hash, { sessionId, lastUsed: Date.now() });
|
|
167
|
+
return sessionId;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 3. Account-wide fallback (workspaceId from connection)
|
|
172
|
+
const workspaceId = normalizeSessionId(credentials?.providerSpecificData?.workspaceId);
|
|
173
|
+
if (workspaceId) return workspaceId;
|
|
174
|
+
|
|
175
|
+
// 4. Last resort — stable per-machine id
|
|
176
|
+
return machineId ? `sess_${hashContent(machineId)}` : generateSessionId();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Cleanup expired entries periodically
|
|
180
|
+
setInterval(() => {
|
|
181
|
+
const now = Date.now();
|
|
182
|
+
for (const [key, entry] of assistantSessionMap) {
|
|
183
|
+
if (now - entry.lastUsed > SESSION_TTL_MS) assistantSessionMap.delete(key);
|
|
184
|
+
}
|
|
185
|
+
}, 10 * 60 * 1000);
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Codex Executor - handles OpenAI Codex API (Responses API format)
|
|
189
|
+
* Automatically injects default instructions if missing
|
|
190
|
+
*/
|
|
191
|
+
export class CodexExecutor extends BaseExecutor {
|
|
192
|
+
constructor() {
|
|
193
|
+
super("codex", PROVIDERS.codex);
|
|
194
|
+
this._currentSessionId = null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Override headers to add codex-specific identity headers.
|
|
199
|
+
* transformRequest runs BEFORE buildHeaders, sets this._currentSessionId.
|
|
200
|
+
*/
|
|
201
|
+
buildHeaders(credentials, stream = true) {
|
|
202
|
+
const headers = super.buildHeaders(credentials, stream);
|
|
203
|
+
headers["session_id"] = this._currentSessionId || credentials?.connectionId || "default";
|
|
204
|
+
// Identify client type to Codex backend (matches official codex CLI)
|
|
205
|
+
if (!headers["originator"]) headers["originator"] = "codex_cli_rs";
|
|
206
|
+
// Workspace binding header — improves account scope + cache affinity
|
|
207
|
+
const workspaceId = credentials?.providerSpecificData?.workspaceId;
|
|
208
|
+
if (typeof workspaceId === "string" && workspaceId && !headers["chatgpt-account-id"]) {
|
|
209
|
+
headers["chatgpt-account-id"] = workspaceId;
|
|
210
|
+
}
|
|
211
|
+
return headers;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
buildUrl(model, stream, urlIndex = 0, credentials = null) {
|
|
215
|
+
const base = super.buildUrl(model, stream, urlIndex, credentials);
|
|
216
|
+
return this._isCompact ? `${base}/compact` : base;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async refreshCredentials(credentials, log) {
|
|
220
|
+
if (!credentials?.refreshToken) return null;
|
|
221
|
+
return refreshProviderCredentials("codex", credentials, log);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
needsRefresh(credentials) {
|
|
225
|
+
return shouldRefreshCredentials("codex", credentials);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Prefetch remote image URLs and inline them as base64 data URIs.
|
|
230
|
+
* Runs before execute() because Codex backend cannot fetch remote images.
|
|
231
|
+
* Mutates body.input in place.
|
|
232
|
+
*/
|
|
233
|
+
async prefetchImages(body) {
|
|
234
|
+
if (!Array.isArray(body?.input)) return;
|
|
235
|
+
for (const item of body.input) {
|
|
236
|
+
if (!Array.isArray(item.content)) continue;
|
|
237
|
+
const pending = item.content.map(async (c) => {
|
|
238
|
+
if (c.type !== "image_url") return c;
|
|
239
|
+
const url = typeof c.image_url === "string" ? c.image_url : c.image_url?.url;
|
|
240
|
+
const detail = c.image_url?.detail || "auto";
|
|
241
|
+
if (!url) return c;
|
|
242
|
+
if (url.startsWith("data:")) return { type: "input_image", image_url: url, detail };
|
|
243
|
+
const fetched = await fetchImageAsBase64(url, { timeoutMs: 15000 });
|
|
244
|
+
return { type: "input_image", image_url: fetched?.url || url, detail };
|
|
245
|
+
});
|
|
246
|
+
item.content = await Promise.all(pending);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async execute(args) {
|
|
251
|
+
const imgCount = Array.isArray(args.body?.input) ? args.body.input.reduce((n, it) => n + (Array.isArray(it.content) ? it.content.filter(c => c.type === "image_url").length : 0), 0) : 0;
|
|
252
|
+
const inputLen = Array.isArray(args.body?.input) ? args.body.input.length : 0;
|
|
253
|
+
dbg("CODEX", `execute start | inputItems=${inputLen} | images=${imgCount} | sessionId=${this._currentSessionId || "pending"}`);
|
|
254
|
+
if (imgCount > 0) {
|
|
255
|
+
const t0 = Date.now();
|
|
256
|
+
await this.prefetchImages(args.body);
|
|
257
|
+
dbg("CODEX", `prefetchImages done | ${Date.now() - t0}ms`);
|
|
258
|
+
} else {
|
|
259
|
+
await this.prefetchImages(args.body);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Retry loop for SSE-level overloaded errors (200 OK body contains event: error)
|
|
263
|
+
// Reuses 503 retry config — same semantic: upstream temporarily unavailable
|
|
264
|
+
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.config.retry };
|
|
265
|
+
const { attempts, delayMs } = resolveRetryEntry(retryConfig[503]);
|
|
266
|
+
let attempt = 0;
|
|
267
|
+
while (true) {
|
|
268
|
+
const result = await super.execute(args);
|
|
269
|
+
const peek = await this._peekSseOverloaded(result.response);
|
|
270
|
+
if (!peek.matched) {
|
|
271
|
+
// Replace body with re-assembled stream (prefix bytes already read + rest)
|
|
272
|
+
if (peek.replacementBody) {
|
|
273
|
+
result.response = new Response(peek.replacementBody, {
|
|
274
|
+
status: result.response.status,
|
|
275
|
+
statusText: result.response.statusText,
|
|
276
|
+
headers: result.response.headers,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
if (attempt >= attempts) {
|
|
282
|
+
args.log?.warn?.("RETRY", `CODEX | SSE overloaded "${peek.matched}" — retries exhausted (${attempt}/${attempts})`);
|
|
283
|
+
// Out of retries → return with replacement body so client gets the error
|
|
284
|
+
if (peek.replacementBody) {
|
|
285
|
+
result.response = new Response(peek.replacementBody, {
|
|
286
|
+
status: result.response.status,
|
|
287
|
+
statusText: result.response.statusText,
|
|
288
|
+
headers: result.response.headers,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
attempt++;
|
|
294
|
+
args.log?.debug?.("RETRY", `CODEX | SSE "${peek.matched}" retry ${attempt}/${attempts} after ${delayMs / 1000}s`);
|
|
295
|
+
dbg("CODEX", `SSE overloaded "${peek.matched}" → retry ${attempt}/${attempts} in ${delayMs}ms`);
|
|
296
|
+
try { await result.response.body?.cancel?.(); } catch { /* noop */ }
|
|
297
|
+
await new Promise(r => setTimeout(r, delayMs));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Peek first N bytes of SSE body to detect upstream "overloaded" errors.
|
|
302
|
+
// Returns { matched: string|null, replacementBody: ReadableStream|null }.
|
|
303
|
+
// Caller MUST use replacementBody (original body has been read).
|
|
304
|
+
async _peekSseOverloaded(response) {
|
|
305
|
+
if (!response || !response.ok || !response.body) return { matched: null, replacementBody: null };
|
|
306
|
+
const reader = response.body.getReader();
|
|
307
|
+
const decoder = new TextDecoder();
|
|
308
|
+
const chunks = [];
|
|
309
|
+
let text = "";
|
|
310
|
+
let matched = null;
|
|
311
|
+
try {
|
|
312
|
+
while (text.length < CODEX_SSE_PEEK_BYTES) {
|
|
313
|
+
const { done, value } = await reader.read();
|
|
314
|
+
if (done) break;
|
|
315
|
+
chunks.push(value);
|
|
316
|
+
text += decoder.decode(value, { stream: true });
|
|
317
|
+
const hit = CODEX_SSE_OVERLOADED_PATTERNS.find(p => text.includes(p));
|
|
318
|
+
if (hit) { matched = hit; break; }
|
|
319
|
+
}
|
|
320
|
+
} catch (e) {
|
|
321
|
+
dbg("CODEX", `peek read error: ${e.message}`);
|
|
322
|
+
}
|
|
323
|
+
reader.releaseLock();
|
|
324
|
+
|
|
325
|
+
// Re-assemble stream: prefix chunks + remaining upstream body
|
|
326
|
+
const upstream = response.body;
|
|
327
|
+
let upstreamReader = null;
|
|
328
|
+
const replacementBody = new ReadableStream({
|
|
329
|
+
start(controller) {
|
|
330
|
+
for (const c of chunks) controller.enqueue(c);
|
|
331
|
+
upstreamReader = upstream.getReader();
|
|
332
|
+
},
|
|
333
|
+
async pull(controller) {
|
|
334
|
+
try {
|
|
335
|
+
const { done, value } = await upstreamReader.read();
|
|
336
|
+
if (done) { controller.close(); return; }
|
|
337
|
+
controller.enqueue(value);
|
|
338
|
+
} catch (e) { controller.error(e); }
|
|
339
|
+
},
|
|
340
|
+
cancel(reason) {
|
|
341
|
+
try { upstreamReader?.cancel(reason); } catch { /* noop */ }
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
return { matched, replacementBody };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Parse Codex usage_limit_reached to extract precise resetsAtMs; fallback to default otherwise
|
|
348
|
+
parseError(response, bodyText) {
|
|
349
|
+
if (response.status === 429 && bodyText) {
|
|
350
|
+
try {
|
|
351
|
+
const json = JSON.parse(bodyText);
|
|
352
|
+
const err = json?.error;
|
|
353
|
+
if (err?.type === "usage_limit_reached") {
|
|
354
|
+
const now = Date.now();
|
|
355
|
+
let resetsAtMs = null;
|
|
356
|
+
if (typeof err.resets_at === "number" && err.resets_at > 0) {
|
|
357
|
+
const ms = err.resets_at * 1000;
|
|
358
|
+
if (ms > now) resetsAtMs = ms;
|
|
359
|
+
}
|
|
360
|
+
if (!resetsAtMs && typeof err.resets_in_seconds === "number" && err.resets_in_seconds > 0) {
|
|
361
|
+
resetsAtMs = now + err.resets_in_seconds * 1000;
|
|
362
|
+
}
|
|
363
|
+
if (resetsAtMs) {
|
|
364
|
+
return { status: 429, message: err.message || bodyText, resetsAtMs };
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} catch { /* fall through to default */ }
|
|
368
|
+
}
|
|
369
|
+
return super.parseError(response, bodyText);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Transform request before sending - inject default instructions if missing.
|
|
374
|
+
* Image fetching is handled separately in prefetchImages() so this stays sync.
|
|
375
|
+
*/
|
|
376
|
+
transformRequest(model, body, stream, credentials) {
|
|
377
|
+
this._isCompact = !!body._compact;
|
|
378
|
+
delete body._compact;
|
|
379
|
+
// Resolve conversation-stable session_id (priority: body → assistant-text → workspace → machine)
|
|
380
|
+
this._currentSessionId = resolveCacheSessionId(body, credentials, cachedMachineId);
|
|
381
|
+
// Convert string input to array format (Codex API requires input as array)
|
|
382
|
+
const normalized = normalizeResponsesInput(body.input);
|
|
383
|
+
if (normalized) body.input = normalized;
|
|
384
|
+
|
|
385
|
+
// Ensure input is present and non-empty (Codex API rejects empty input)
|
|
386
|
+
if (!body.input || (Array.isArray(body.input) && body.input.length === 0)) {
|
|
387
|
+
body.input = [{ type: "message", role: "user", content: [{ type: "input_text", text: "..." }] }];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Keep system prompts in body.input as role=developer so they stay in the cacheable prefix
|
|
391
|
+
convertSystemToDeveloperRole(body);
|
|
392
|
+
// Strip server-generated item IDs (rs_/fc_/resp_/msg_) — Codex /responses can't resolve when store=false
|
|
393
|
+
stripStoredItemReferences(body);
|
|
394
|
+
// Flatten function tools + drop unsupported types
|
|
395
|
+
normalizeCodexTools(body);
|
|
396
|
+
|
|
397
|
+
// Ensure streaming is enabled (Codex API requires it)
|
|
398
|
+
body.stream = true;
|
|
399
|
+
|
|
400
|
+
// If no instructions provided, inject default Codex instructions
|
|
401
|
+
if (!body.instructions || body.instructions.trim() === "") {
|
|
402
|
+
body.instructions = CODEX_DEFAULT_INSTRUCTIONS;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Ensure store is false (Codex requirement)
|
|
406
|
+
body.store = false;
|
|
407
|
+
|
|
408
|
+
// Inject prompt_cache_key for stable Codex prompt caching
|
|
409
|
+
if (!body.prompt_cache_key && this._currentSessionId) {
|
|
410
|
+
body.prompt_cache_key = this._currentSessionId;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Map virtual Codex review models to the upstream Codex model before suffix parsing.
|
|
414
|
+
body.model = getModelUpstreamId("cx", body.model || model);
|
|
415
|
+
|
|
416
|
+
// Extract thinking level from model name suffix
|
|
417
|
+
// e.g., gpt-5.3-codex-high → high, gpt-5.3-codex → medium (default)
|
|
418
|
+
const effortLevels = ['none', 'low', 'medium', 'high', 'xhigh'];
|
|
419
|
+
let modelEffort = null;
|
|
420
|
+
for (const level of effortLevels) {
|
|
421
|
+
if (body.model.endsWith(`-${level}`)) {
|
|
422
|
+
modelEffort = level;
|
|
423
|
+
// Strip suffix from model name for actual API call
|
|
424
|
+
body.model = body.model.replace(`-${level}`, '');
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Priority: explicit reasoning.effort > reasoning_effort param > model suffix > default (medium)
|
|
430
|
+
if (!body.reasoning) {
|
|
431
|
+
const effort = body.reasoning_effort || modelEffort || 'low';
|
|
432
|
+
body.reasoning = { effort, summary: "auto" };
|
|
433
|
+
} else if (!body.reasoning.summary) {
|
|
434
|
+
body.reasoning.summary = "auto";
|
|
435
|
+
}
|
|
436
|
+
delete body.reasoning_effort;
|
|
437
|
+
|
|
438
|
+
// Include reasoning encrypted content (required by Codex backend for reasoning models)
|
|
439
|
+
if (body.reasoning && body.reasoning.effort && body.reasoning.effort !== 'none') {
|
|
440
|
+
body.include = ["reasoning.encrypted_content"];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Remove unsupported parameters for Codex API
|
|
444
|
+
delete body.temperature;
|
|
445
|
+
delete body.top_p;
|
|
446
|
+
delete body.frequency_penalty;
|
|
447
|
+
delete body.presence_penalty;
|
|
448
|
+
delete body.logprobs;
|
|
449
|
+
delete body.top_logprobs;
|
|
450
|
+
delete body.n;
|
|
451
|
+
delete body.seed;
|
|
452
|
+
delete body.max_tokens;
|
|
453
|
+
delete body.max_completion_tokens;
|
|
454
|
+
delete body.max_output_tokens; // Responses API clients send this but Codex rejects it
|
|
455
|
+
delete body.user; // Cursor sends this but Codex doesn't support it
|
|
456
|
+
delete body.prompt_cache_retention; // Cursor sends this but Codex doesn't support it
|
|
457
|
+
delete body.metadata; // Cursor sends this but Codex doesn't support it
|
|
458
|
+
delete body.stream_options; // Cursor sends this but Codex doesn't support it
|
|
459
|
+
delete body.safety_identifier; // Droid CLI sends this but Codex doesn't support it
|
|
460
|
+
delete body.previous_response_id; // store=false → backend can't resolve previous resp; avoid 404
|
|
461
|
+
|
|
462
|
+
// Final allowlist filter — strip any unknown field that could trigger upstream "routing_unsupported"
|
|
463
|
+
for (const k of Object.keys(body)) {
|
|
464
|
+
if (!RESPONSES_API_ALLOWLIST.has(k)) delete body[k];
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return body;
|
|
468
|
+
}
|
|
469
|
+
}
|