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,526 @@
|
|
|
1
|
+
const { err } = require("../logger");
|
|
2
|
+
const { IS_DEV } = require("../config");
|
|
3
|
+
const { fetchRouter, pipeTransformedEventStream } = require("./base");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
// Debug trace log — written to data/logs/mitm/kiro-debug.log (dev only)
|
|
8
|
+
const DEBUG_LOG = path.join(__dirname, "../../../data/logs/mitm/kiro-debug.log");
|
|
9
|
+
function dbg(msg) {
|
|
10
|
+
if (!IS_DEV) return;
|
|
11
|
+
try {
|
|
12
|
+
fs.appendFileSync(DEBUG_LOG, `${new Date().toISOString()} ${msg}\n`);
|
|
13
|
+
} catch {}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ─── CRC32 (standard, polynomial 0xEDB88320 — same as AWS EventStream) ───────
|
|
17
|
+
const CRC32_TABLE = (() => {
|
|
18
|
+
const t = new Uint32Array(256);
|
|
19
|
+
for (let n = 0; n < 256; n++) {
|
|
20
|
+
let c = n;
|
|
21
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
22
|
+
t[n] = c;
|
|
23
|
+
}
|
|
24
|
+
return t;
|
|
25
|
+
})();
|
|
26
|
+
|
|
27
|
+
function crc32(buf) {
|
|
28
|
+
let crc = 0xffffffff;
|
|
29
|
+
for (let i = 0; i < buf.length; i++) {
|
|
30
|
+
crc = (crc >>> 8) ^ CRC32_TABLE[(crc ^ buf[i]) & 0xff];
|
|
31
|
+
}
|
|
32
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initialize state for the Kiro response translator
|
|
37
|
+
*/
|
|
38
|
+
function initKiroState(modelId) {
|
|
39
|
+
return {
|
|
40
|
+
modelId: modelId || null, // Model name from first chunk
|
|
41
|
+
toolCallInit: {}, // { [index]: { id, name } } — tracks seen tools
|
|
42
|
+
hasToolCalls: false, // Whether this response uses tool calls
|
|
43
|
+
finishSent: false, // Whether termination has been emitted
|
|
44
|
+
usage: null, // Accumulated usage from usage-only chunks
|
|
45
|
+
inThink: false, // Whether inside a <thinking> block
|
|
46
|
+
thinkBuf: "" // Buffer for partial thinking content
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Extract thinking blocks from text content.
|
|
52
|
+
* Handles both <thinking>...</thinking> and <think>...</think> tags,
|
|
53
|
+
* including partial tags split across SSE chunks.
|
|
54
|
+
*/
|
|
55
|
+
function extractThinking(text, state) {
|
|
56
|
+
if (!text) return { thinking: null, text: null };
|
|
57
|
+
|
|
58
|
+
let working = text;
|
|
59
|
+
|
|
60
|
+
// Prepend buffered partial thinking from previous chunk
|
|
61
|
+
if (state.inThink && state.thinkBuf) {
|
|
62
|
+
working = state.thinkBuf + working;
|
|
63
|
+
state.thinkBuf = "";
|
|
64
|
+
state.inThink = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Match <thinking> or <think> opening tags
|
|
68
|
+
const startRe = /<thinking>|<think>/i;
|
|
69
|
+
const startMatch = working.match(startRe);
|
|
70
|
+
|
|
71
|
+
if (!startMatch) {
|
|
72
|
+
return { thinking: null, text: working };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const tag = startMatch[0].toLowerCase();
|
|
76
|
+
const closeTag = tag === "<think>" ? "</think>" : "</thinking>";
|
|
77
|
+
const startIdx = startMatch.index;
|
|
78
|
+
const endIdx = working.indexOf(closeTag, startIdx + tag.length);
|
|
79
|
+
|
|
80
|
+
if (endIdx === -1) {
|
|
81
|
+
// Opening tag without closing — buffer for next chunk
|
|
82
|
+
state.inThink = true;
|
|
83
|
+
state.thinkBuf = working.slice(startIdx);
|
|
84
|
+
const before = working.slice(0, startIdx).trim();
|
|
85
|
+
return { thinking: null, text: before || null };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Complete block found
|
|
89
|
+
const thinking = working.slice(startIdx + tag.length, endIdx);
|
|
90
|
+
const before = working.slice(0, startIdx).trim();
|
|
91
|
+
const after = working.slice(endIdx + closeTag.length).trim();
|
|
92
|
+
const rest = [before, after].filter(Boolean).join("");
|
|
93
|
+
|
|
94
|
+
// Recursively process for more blocks
|
|
95
|
+
const recurse = rest
|
|
96
|
+
? extractThinking(rest, { inThink: false, thinkBuf: "" })
|
|
97
|
+
: { thinking: null, text: null };
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
thinking: thinking || null,
|
|
101
|
+
text: recurse.text || null
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── AWS EventStream frame builder ────────────────────────────────────────────
|
|
106
|
+
/**
|
|
107
|
+
* Encode a single string header into the AWS EventStream binary format.
|
|
108
|
+
* Header wire format: [nameLen 1B][name][type=7 1B][valueLen 2B][value]
|
|
109
|
+
*/
|
|
110
|
+
function encodeHeader(name, value) {
|
|
111
|
+
const nameBuf = Buffer.from(name, "utf8");
|
|
112
|
+
const valueBuf = Buffer.from(value, "utf8");
|
|
113
|
+
const buf = Buffer.alloc(1 + nameBuf.length + 1 + 2 + valueBuf.length);
|
|
114
|
+
let o = 0;
|
|
115
|
+
buf[o++] = nameBuf.length;
|
|
116
|
+
nameBuf.copy(buf, o); o += nameBuf.length;
|
|
117
|
+
buf[o++] = 7; // string type
|
|
118
|
+
buf.writeUInt16BE(valueBuf.length, o); o += 2;
|
|
119
|
+
valueBuf.copy(buf, o);
|
|
120
|
+
return buf;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Build a single AWS EventStream binary frame with all Smithy-required headers.
|
|
125
|
+
*
|
|
126
|
+
* Frame layout (big-endian):
|
|
127
|
+
* [totalLen 4B][headersLen 4B][preludeCRC 4B]
|
|
128
|
+
* [headers ...][payload JSON ...][messageCRC 4B]
|
|
129
|
+
*
|
|
130
|
+
* The SmithyMessageDecoderStream layer requires three system headers on every frame:
|
|
131
|
+
* :message-type = "event" (or "exception" / "error")
|
|
132
|
+
* :event-type = e.g. "assistantResponseEvent"
|
|
133
|
+
* :content-type = "application/json"
|
|
134
|
+
*/
|
|
135
|
+
function buildEventStreamFrame(eventType, payload) {
|
|
136
|
+
const payloadBuf = Buffer.from(
|
|
137
|
+
typeof payload === "string" ? payload : JSON.stringify(payload),
|
|
138
|
+
"utf8"
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// All three Smithy system headers are required
|
|
142
|
+
const headersBuf = Buffer.concat([
|
|
143
|
+
encodeHeader(":message-type", "event"),
|
|
144
|
+
encodeHeader(":event-type", eventType),
|
|
145
|
+
encodeHeader(":content-type", "application/json"),
|
|
146
|
+
]);
|
|
147
|
+
const headersLen = headersBuf.length;
|
|
148
|
+
|
|
149
|
+
const totalLen = 4 + 4 + 4 + headersLen + payloadBuf.length + 4;
|
|
150
|
+
const frame = Buffer.alloc(totalLen);
|
|
151
|
+
|
|
152
|
+
frame.writeUInt32BE(totalLen, 0);
|
|
153
|
+
frame.writeUInt32BE(headersLen, 4);
|
|
154
|
+
frame.writeUInt32BE(crc32(frame.slice(0, 8)), 8); // prelude CRC
|
|
155
|
+
headersBuf.copy(frame, 12);
|
|
156
|
+
payloadBuf.copy(frame, 12 + headersLen);
|
|
157
|
+
frame.writeUInt32BE(crc32(frame.slice(0, totalLen - 4)), totalLen - 4); // message CRC
|
|
158
|
+
|
|
159
|
+
return frame;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ─── CodeWhisperer → OpenAI conversion ───────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Safely stringify a tool-call input value.
|
|
166
|
+
* OpenAI expects `function.arguments` to be a JSON string, never an object.
|
|
167
|
+
* If 9router's Anthropic→OpenAI conversion passes the input as a pre-parsed object,
|
|
168
|
+
* this prevents the "" + object → "[object Object]" corruption.
|
|
169
|
+
*/
|
|
170
|
+
function safeArgsString(value) {
|
|
171
|
+
if (typeof value === "string") return value;
|
|
172
|
+
if (value == null) return "{}";
|
|
173
|
+
try { return JSON.stringify(value); } catch { return "{}"; }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Convert a CodeWhisperer userInputMessage to one or more OpenAI messages.
|
|
178
|
+
*
|
|
179
|
+
* A user turn can contain:
|
|
180
|
+
* - plain text content → { role:"user", content }
|
|
181
|
+
* - toolResults only → one { role:"tool", tool_call_id, content } per result
|
|
182
|
+
* - both → tool messages first, then the user text message
|
|
183
|
+
*/
|
|
184
|
+
function convertUserInputMessage(uim) {
|
|
185
|
+
const out = [];
|
|
186
|
+
const toolResults = uim.userInputMessageContext?.toolResults || [];
|
|
187
|
+
|
|
188
|
+
// Emit one "tool" message per tool result (OpenAI multi-tool format)
|
|
189
|
+
for (const tr of toolResults) {
|
|
190
|
+
const text = (tr.content || []).map(c => c.text || "").join("\n");
|
|
191
|
+
out.push({
|
|
192
|
+
role: "tool",
|
|
193
|
+
tool_call_id: tr.toolUseId || "",
|
|
194
|
+
content: text,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Emit user text only if it exists alongside OR when there are no tool results
|
|
199
|
+
const text = (uim.content || "").trim();
|
|
200
|
+
if (text || toolResults.length === 0) {
|
|
201
|
+
out.push({ role: "user", content: text });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Convert a CodeWhisperer assistantResponseMessage to an OpenAI assistant message.
|
|
209
|
+
*
|
|
210
|
+
* The assistant turn can contain:
|
|
211
|
+
* - plain text content
|
|
212
|
+
* - toolUses (tool calls) → tool_calls[] on the assistant message
|
|
213
|
+
*/
|
|
214
|
+
function convertAssistantResponseMessage(arm) {
|
|
215
|
+
const toolUses = arm.toolUses || [];
|
|
216
|
+
|
|
217
|
+
if (toolUses.length > 0) {
|
|
218
|
+
return {
|
|
219
|
+
role: "assistant",
|
|
220
|
+
content: arm.content || null,
|
|
221
|
+
tool_calls: toolUses.map(tu => ({
|
|
222
|
+
id: tu.toolUseId || `call_${Date.now()}`,
|
|
223
|
+
type: "function",
|
|
224
|
+
function: {
|
|
225
|
+
name: tu.name || "",
|
|
226
|
+
arguments: safeArgsString(tu.input),
|
|
227
|
+
},
|
|
228
|
+
})),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return { role: "assistant", content: arm.content || "" };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Convert AWS CodeWhisperer conversationState to an OpenAI messages array.
|
|
237
|
+
*
|
|
238
|
+
* Full multi-turn shape:
|
|
239
|
+
* history: [
|
|
240
|
+
* { userInputMessage: { content, userInputMessageContext?: { toolResults?, tools? } } },
|
|
241
|
+
* { assistantResponseMessage: { content, toolUses?: [...] } },
|
|
242
|
+
* ...
|
|
243
|
+
* ]
|
|
244
|
+
* currentMessage: { userInputMessage: { content, userInputMessageContext?: { toolResults? } } }
|
|
245
|
+
*/
|
|
246
|
+
function codeWhispererToMessages(body) {
|
|
247
|
+
const cs = body.conversationState || {};
|
|
248
|
+
const history = cs.history || [];
|
|
249
|
+
const currentMsg = cs.currentMessage;
|
|
250
|
+
const messages = [];
|
|
251
|
+
|
|
252
|
+
for (const item of history) {
|
|
253
|
+
if (item.userInputMessage) {
|
|
254
|
+
messages.push(...convertUserInputMessage(item.userInputMessage));
|
|
255
|
+
} else if (item.assistantResponseMessage) {
|
|
256
|
+
messages.push(convertAssistantResponseMessage(item.assistantResponseMessage));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Append the current (latest) turn
|
|
261
|
+
if (currentMsg?.userInputMessage) {
|
|
262
|
+
messages.push(...convertUserInputMessage(currentMsg.userInputMessage));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return messages;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Extract tool definitions from a CodeWhisperer request and convert to OpenAI format.
|
|
270
|
+
*
|
|
271
|
+
* CodeWhisperer tools live in:
|
|
272
|
+
* conversationState.currentMessage.userInputMessage.userInputMessageContext.tools
|
|
273
|
+
* OR (in multi-turn) the first history item's userInputMessageContext.tools
|
|
274
|
+
*
|
|
275
|
+
* CodeWhisperer tool shape:
|
|
276
|
+
* { toolSpecification: { name, description, inputSchema: { json: <schema> } } }
|
|
277
|
+
*
|
|
278
|
+
* OpenAI tool shape:
|
|
279
|
+
* { type: "function", function: { name, description, parameters: <schema> } }
|
|
280
|
+
*/
|
|
281
|
+
function extractTools(body) {
|
|
282
|
+
const cs = body.conversationState || {};
|
|
283
|
+
|
|
284
|
+
// Tools are typically on the currentMessage; may also appear on the first history item
|
|
285
|
+
const fromCurrent = cs.currentMessage?.userInputMessage?.userInputMessageContext?.tools || [];
|
|
286
|
+
const fromHistory = cs.history?.find(h => h.userInputMessage?.userInputMessageContext?.tools)
|
|
287
|
+
?.userInputMessage?.userInputMessageContext?.tools || [];
|
|
288
|
+
const cwTools = fromCurrent.length > 0 ? fromCurrent : fromHistory;
|
|
289
|
+
|
|
290
|
+
if (!cwTools.length) return [];
|
|
291
|
+
|
|
292
|
+
return cwTools.map(item => {
|
|
293
|
+
const spec = item.toolSpecification || item;
|
|
294
|
+
return {
|
|
295
|
+
type: "function",
|
|
296
|
+
function: {
|
|
297
|
+
name: spec.name || "",
|
|
298
|
+
description: spec.description || `Tool: ${spec.name || "unknown"}`,
|
|
299
|
+
parameters: spec.inputSchema?.json || { type: "object", properties: {}, required: [] },
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ─── OpenAI SSE → EventStream binary conversion ───────────────────────────────
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Convert an OpenAI SSE chunk to AWS EventStream binary frame(s)
|
|
309
|
+
* This replaces pipeOpenAIasEventStream and works with pipeTransformedEventStream
|
|
310
|
+
*
|
|
311
|
+
* @param {object|null} chunk - Parsed OpenAI chat.completion.chunk, or null for flush
|
|
312
|
+
* @param {object} state - Mutable state object
|
|
313
|
+
* @returns {Uint8Array|Uint8Array[]|null} Binary EventStream frame(s) or null to skip
|
|
314
|
+
*/
|
|
315
|
+
function convertOpenAIToKiro(chunk, state) {
|
|
316
|
+
// Flush: ensure clean stream termination
|
|
317
|
+
if (!chunk) {
|
|
318
|
+
if (state.finishSent) return null;
|
|
319
|
+
// Flush any remaining buffered thinking
|
|
320
|
+
if (state.inThink && state.thinkBuf) {
|
|
321
|
+
state.inThink = false;
|
|
322
|
+
const thinking = state.thinkBuf;
|
|
323
|
+
state.thinkBuf = "";
|
|
324
|
+
return buildEventStreamFrame("reasoningContentEvent", {
|
|
325
|
+
content: thinking,
|
|
326
|
+
modelId: state.modelId || "kiro-unknown"
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
return buildEventStreamFrame("messageStopEvent", {});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const frames = [];
|
|
333
|
+
const choice = chunk.choices?.[0];
|
|
334
|
+
const delta = choice?.delta || {};
|
|
335
|
+
|
|
336
|
+
// Capture modelId from first chunk (real API includes it in every content frame)
|
|
337
|
+
if (!state.modelId && chunk.model) {
|
|
338
|
+
state.modelId = chunk.model;
|
|
339
|
+
}
|
|
340
|
+
const modelId = state.modelId || "unknown";
|
|
341
|
+
|
|
342
|
+
// Handle usage (may arrive standalone or with other chunks)
|
|
343
|
+
if (chunk.usage) {
|
|
344
|
+
state.usage = chunk.usage;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Handle tool calls — stream incrementally, matching real API format
|
|
348
|
+
if (delta.tool_calls) {
|
|
349
|
+
state.hasToolCalls = true;
|
|
350
|
+
for (const tc of delta.tool_calls) {
|
|
351
|
+
const idx = tc.index ?? 0;
|
|
352
|
+
|
|
353
|
+
if (tc.id && tc.function?.name && !state.toolCallInit[idx]) {
|
|
354
|
+
// First appearance: emit frame with name + id, no input
|
|
355
|
+
state.toolCallInit[idx] = { id: tc.id, name: tc.function.name };
|
|
356
|
+
dbg(`toolUseEvent init: ${tc.function.name} (${tc.id})`);
|
|
357
|
+
frames.push(buildEventStreamFrame("toolUseEvent", {
|
|
358
|
+
name: tc.function.name,
|
|
359
|
+
toolUseId: tc.id
|
|
360
|
+
}));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Emit incremental input fragment
|
|
364
|
+
if (tc.function?.arguments) {
|
|
365
|
+
const init = state.toolCallInit[idx];
|
|
366
|
+
dbg(`toolUseEvent fragment: ${tc.function.arguments.slice(0, 100)}`);
|
|
367
|
+
frames.push(buildEventStreamFrame("toolUseEvent", {
|
|
368
|
+
input: tc.function.arguments,
|
|
369
|
+
name: init?.name || tc.function?.name || "",
|
|
370
|
+
toolUseId: init?.id || tc.id || ""
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Handle explicit reasoning_content (type-specific thinking channel)
|
|
377
|
+
if (delta.reasoning_content) {
|
|
378
|
+
frames.push(buildEventStreamFrame("reasoningContentEvent", {
|
|
379
|
+
content: delta.reasoning_content,
|
|
380
|
+
modelId
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Handle text content — extract thinking blocks, emit rest as assistantResponseEvent
|
|
385
|
+
if (delta.content) {
|
|
386
|
+
const { thinking, text } = extractThinking(delta.content, state);
|
|
387
|
+
|
|
388
|
+
if (thinking) {
|
|
389
|
+
frames.push(buildEventStreamFrame("reasoningContentEvent", {
|
|
390
|
+
content: thinking,
|
|
391
|
+
modelId
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (text) {
|
|
396
|
+
frames.push(buildEventStreamFrame("assistantResponseEvent", {
|
|
397
|
+
content: text,
|
|
398
|
+
modelId
|
|
399
|
+
}));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Handle finish_reason
|
|
404
|
+
if (choice?.finish_reason) {
|
|
405
|
+
const finishFrames = emitFinish(state);
|
|
406
|
+
if (finishFrames) {
|
|
407
|
+
frames.push(...(Array.isArray(finishFrames) ? finishFrames : [finishFrames]));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (frames.length === 0) return null;
|
|
412
|
+
return frames.length === 1 ? frames[0] : frames;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Emit termination frames. For tool-call responses, emits stop:true per tool.
|
|
417
|
+
* For text-only responses, emits messageStopEvent.
|
|
418
|
+
*/
|
|
419
|
+
function emitFinish(state) {
|
|
420
|
+
const frames = [];
|
|
421
|
+
|
|
422
|
+
if (state.hasToolCalls) {
|
|
423
|
+
// Tool-call response: emit stop:true for each tool
|
|
424
|
+
for (const idx of Object.keys(state.toolCallInit).sort()) {
|
|
425
|
+
const tc = state.toolCallInit[idx];
|
|
426
|
+
frames.push(buildEventStreamFrame("toolUseEvent", {
|
|
427
|
+
name: tc.name,
|
|
428
|
+
stop: true,
|
|
429
|
+
toolUseId: tc.id
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
// Text-only response: emit messageStopEvent
|
|
434
|
+
frames.push(buildEventStreamFrame("messageStopEvent", {}));
|
|
435
|
+
}
|
|
436
|
+
state.finishSent = true;
|
|
437
|
+
|
|
438
|
+
// Emit usage if available
|
|
439
|
+
if (state.usage) {
|
|
440
|
+
frames.push(buildEventStreamFrame("usageEvent", {
|
|
441
|
+
inputTokens: state.usage.prompt_tokens || 0,
|
|
442
|
+
outputTokens: state.usage.completion_tokens || 0
|
|
443
|
+
}));
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
state.toolCallInit = {};
|
|
447
|
+
return frames.length > 0 ? frames : null;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ─── MITM intercept entry point ───────────────────────────────────────────────
|
|
451
|
+
/**
|
|
452
|
+
* Intercept Kiro IDE CodeWhisperer request and convert to EventStream response:
|
|
453
|
+
* 1. Parse CodeWhisperer JSON body (reject binary EventStream formats)
|
|
454
|
+
* 2. Convert CodeWhisperer format to OpenAI messages[] format
|
|
455
|
+
* 3. Forward to 9router /v1/chat/completions (OpenAI SSE)
|
|
456
|
+
* 4. Convert OpenAI SSE response → AWS EventStream binary frames
|
|
457
|
+
* 5. Stream EventStream frames back to Kiro IDE
|
|
458
|
+
*
|
|
459
|
+
* @param {http.IncomingMessage} req - HTTP request from Kiro IDE
|
|
460
|
+
* @param {http.ServerResponse} res - HTTP response to Kiro IDE
|
|
461
|
+
* @param {Buffer} bodyBuffer - Request body buffer
|
|
462
|
+
* @param {string} mappedModel - Model name after MITM alias mapping
|
|
463
|
+
*/
|
|
464
|
+
async function intercept(req, res, bodyBuffer, mappedModel) {
|
|
465
|
+
try {
|
|
466
|
+
// Detect and handle binary data (e.g., continuation requests with EventStream frames)
|
|
467
|
+
if (isBinaryEventStream(bodyBuffer)) {
|
|
468
|
+
// Binary EventStream requests are typically continuation/streaming frames
|
|
469
|
+
// that don't contain model info - pass them through directly to avoid JSON.parse crash
|
|
470
|
+
throw new Error(`Binary EventStream format detected (${bodyBuffer.length}B) - request should use passthrough instead of intercept`);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const body = JSON.parse(bodyBuffer.toString());
|
|
474
|
+
|
|
475
|
+
// 1 + 2: CodeWhisperer → OpenAI messages + tools
|
|
476
|
+
const messages = codeWhispererToMessages(body);
|
|
477
|
+
if (messages.length === 0) {
|
|
478
|
+
throw new Error("codeWhispererToMessages produced 0 messages — check request body");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const tools = extractTools(body);
|
|
482
|
+
|
|
483
|
+
const openaiBody = {
|
|
484
|
+
model: mappedModel,
|
|
485
|
+
messages,
|
|
486
|
+
stream: true,
|
|
487
|
+
// Forward tools so Claude uses structured tool_calls instead of XML text fallback
|
|
488
|
+
...(tools.length > 0 && { tools, tool_choice: "auto" }),
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// 3: Forward to 9router
|
|
492
|
+
const routerRes = await fetchRouter(openaiBody, "/v1/chat/completions", req.headers);
|
|
493
|
+
|
|
494
|
+
// 4 + 5: Re-encode response as AWS EventStream binary using standard pipeline
|
|
495
|
+
const state = initKiroState(mappedModel);
|
|
496
|
+
|
|
497
|
+
await pipeTransformedEventStream(routerRes, res, convertOpenAIToKiro, state);
|
|
498
|
+
} catch (error) {
|
|
499
|
+
err(`[Kiro MITM] Request processing failed: ${error.message}`);
|
|
500
|
+
if (!res.headersSent) {
|
|
501
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
502
|
+
}
|
|
503
|
+
res.end(JSON.stringify({
|
|
504
|
+
error: {
|
|
505
|
+
message: error.message,
|
|
506
|
+
type: "mitm_error",
|
|
507
|
+
handler: "kiro"
|
|
508
|
+
}
|
|
509
|
+
}));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Detect AWS EventStream binary format
|
|
514
|
+
function isBinaryEventStream(buffer) {
|
|
515
|
+
if (!buffer || buffer.length < 12) return false;
|
|
516
|
+
// AWS EventStream signature:
|
|
517
|
+
// - First 4 bytes: total frame length (big-endian)
|
|
518
|
+
// - Bytes 4-8: headers length (big-endian)
|
|
519
|
+
// - Typical frame length: 100-10000 bytes
|
|
520
|
+
const totalLen = buffer.readUInt32BE(0);
|
|
521
|
+
const headersLen = buffer.readUInt32BE(4);
|
|
522
|
+
// Sanity checks: frame length should be reasonable and headers should fit
|
|
523
|
+
return totalLen > 12 && totalLen < 1000000 && headersLen < totalLen - 12;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
module.exports = { intercept };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const zlib = require("zlib");
|
|
4
|
+
const { DATA_DIR } = require("./paths");
|
|
5
|
+
const { LOG_BLACKLIST_URL_PARTS } = require("./config");
|
|
6
|
+
|
|
7
|
+
function time() {
|
|
8
|
+
return new Date().toLocaleTimeString("en-US", { hour12: false });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const log = (msg) => console.log(`[${time()}] [MITM] ${msg}`);
|
|
12
|
+
const err = (msg) => console.error(`[${time()}] ❌ [MITM] ${msg}`);
|
|
13
|
+
|
|
14
|
+
const DUMP_DIR = path.join(DATA_DIR, "logs", "mitm");
|
|
15
|
+
if (!fs.existsSync(DUMP_DIR)) fs.mkdirSync(DUMP_DIR, { recursive: true });
|
|
16
|
+
|
|
17
|
+
// Clear all files inside DUMP_DIR (called on MITM server start to avoid unbounded growth)
|
|
18
|
+
function clearDumpDir() {
|
|
19
|
+
try {
|
|
20
|
+
if (!fs.existsSync(DUMP_DIR)) return;
|
|
21
|
+
for (const f of fs.readdirSync(DUMP_DIR)) {
|
|
22
|
+
try { fs.rmSync(path.join(DUMP_DIR, f), { recursive: true, force: true }); } catch { /* ignore */ }
|
|
23
|
+
}
|
|
24
|
+
} catch { /* ignore */ }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const EMPTY_BODY_RE = /^\s*(\{\s*\}|\[\s*\]|null)?\s*$/;
|
|
28
|
+
|
|
29
|
+
function slugify(s, max = 80) {
|
|
30
|
+
return String(s).replace(/[^a-zA-Z0-9]/g, "_").substring(0, max);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isBlacklisted(url) {
|
|
34
|
+
if (!url) return false;
|
|
35
|
+
return LOG_BLACKLIST_URL_PARTS.some(part => url.includes(part));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Decode body buffer based on content-encoding header
|
|
39
|
+
function decodeBody(buf, encoding) {
|
|
40
|
+
if (!buf || buf.length === 0) return buf;
|
|
41
|
+
try {
|
|
42
|
+
const enc = (encoding || "").toLowerCase();
|
|
43
|
+
if (enc.includes("gzip")) return zlib.gunzipSync(buf);
|
|
44
|
+
if (enc.includes("br")) return zlib.brotliDecompressSync(buf);
|
|
45
|
+
if (enc.includes("deflate")) return zlib.inflateSync(buf);
|
|
46
|
+
} catch { /* return raw on failure */ }
|
|
47
|
+
return buf;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Save raw request: method + url + headers + body
|
|
51
|
+
function dumpRequest(req, bodyBuffer, tag = "raw") {
|
|
52
|
+
if (isBlacklisted(req.url)) return null;
|
|
53
|
+
try {
|
|
54
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
55
|
+
const slug = slugify((req.headers.host || "") + req.url);
|
|
56
|
+
const file = path.join(DUMP_DIR, `${ts}_${tag}_${slug}.req.json`);
|
|
57
|
+
let parsed = null;
|
|
58
|
+
try { parsed = JSON.parse(bodyBuffer.toString()); } catch { /* not JSON */ }
|
|
59
|
+
fs.writeFileSync(file, JSON.stringify({
|
|
60
|
+
method: req.method,
|
|
61
|
+
url: req.url,
|
|
62
|
+
host: req.headers.host,
|
|
63
|
+
headers: req.headers,
|
|
64
|
+
body: parsed ?? bodyBuffer.toString("utf8")
|
|
65
|
+
}, null, 2));
|
|
66
|
+
return file;
|
|
67
|
+
} catch { return null; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Buffer-based response dumper — collects chunks then decodes + writes once on end()
|
|
71
|
+
// Trade-off: holds response in RAM, but enables gzip/br decoding for readable output.
|
|
72
|
+
function createResponseDumper(req, tag = "raw") {
|
|
73
|
+
if (isBlacklisted(req.url)) return null;
|
|
74
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
75
|
+
const slug = slugify((req.headers.host || "") + req.url);
|
|
76
|
+
const file = path.join(DUMP_DIR, `${ts}_${tag}_${slug}.res.txt`);
|
|
77
|
+
let status = 0;
|
|
78
|
+
let headers = {};
|
|
79
|
+
const chunks = [];
|
|
80
|
+
return {
|
|
81
|
+
writeHeader: (s, h) => { status = s; headers = h || {}; },
|
|
82
|
+
writeChunk: (chunk) => {
|
|
83
|
+
if (chunk == null) return;
|
|
84
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
85
|
+
},
|
|
86
|
+
end: () => {
|
|
87
|
+
try {
|
|
88
|
+
const raw = Buffer.concat(chunks);
|
|
89
|
+
const enc = headers["content-encoding"] || headers["Content-Encoding"];
|
|
90
|
+
const decoded = decodeBody(raw, enc);
|
|
91
|
+
const text = decoded.toString("utf8");
|
|
92
|
+
// Skip empty / trivially-empty bodies
|
|
93
|
+
if (EMPTY_BODY_RE.test(text)) return;
|
|
94
|
+
// Strip content-encoding since body is now decoded
|
|
95
|
+
const cleanHeaders = { ...headers };
|
|
96
|
+
delete cleanHeaders["content-encoding"];
|
|
97
|
+
delete cleanHeaders["Content-Encoding"];
|
|
98
|
+
const out = `STATUS: ${status}\nHEADERS: ${JSON.stringify(cleanHeaders, null, 2)}\n---BODY---\n${text}`;
|
|
99
|
+
fs.writeFileSync(file, out);
|
|
100
|
+
} catch { /* ignore */ }
|
|
101
|
+
},
|
|
102
|
+
file
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = { log, err, dumpRequest, createResponseDumper, clearDumpDir };
|