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,939 @@
|
|
|
1
|
+
import { PROVIDERS } from "../config/providers.js";
|
|
2
|
+
import { OAUTH_ENDPOINTS, GITHUB_COPILOT, REFRESH_LEAD_MS } from "../config/appConstants.js";
|
|
3
|
+
import { proxyAwareFetch } from "../utils/proxyFetch.js";
|
|
4
|
+
|
|
5
|
+
// xAI refresh — wraps the class method from src/lib/oauth/services/xai.js so
|
|
6
|
+
// the token-refresh switches below can stay flat (one function per provider).
|
|
7
|
+
let _xaiServiceSingleton = null;
|
|
8
|
+
async function refreshXaiToken(refreshToken, log) {
|
|
9
|
+
if (!refreshToken) return null;
|
|
10
|
+
return dedupRefresh("xai", refreshToken, async () => {
|
|
11
|
+
try {
|
|
12
|
+
if (!_xaiServiceSingleton) {
|
|
13
|
+
const mod = await import("../../src/lib/oauth/services/xai.js");
|
|
14
|
+
_xaiServiceSingleton = new mod.XaiService();
|
|
15
|
+
}
|
|
16
|
+
const tokens = await _xaiServiceSingleton.refreshAccessToken(refreshToken);
|
|
17
|
+
return {
|
|
18
|
+
accessToken: tokens.access_token,
|
|
19
|
+
refreshToken: tokens.refresh_token || refreshToken,
|
|
20
|
+
expiresIn: tokens.expires_in,
|
|
21
|
+
idToken: tokens.id_token,
|
|
22
|
+
};
|
|
23
|
+
} catch (e) {
|
|
24
|
+
log?.warn?.("TOKEN_REFRESH", `xai refresh failed: ${e?.message || e}`);
|
|
25
|
+
const msg = String(e?.message || "");
|
|
26
|
+
if (msg.includes("invalid_grant") || msg.includes("invalid_request")) {
|
|
27
|
+
return { error: "invalid_grant" };
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}, log);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Default token expiry buffer (refresh if expires within 5 minutes)
|
|
35
|
+
export const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
|
|
36
|
+
|
|
37
|
+
// Dedup: cache in-flight promise + recent result to prevent refresh_token_reused (Auth0 family revoke)
|
|
38
|
+
const REFRESH_RESULT_TTL_MS = 10_000;
|
|
39
|
+
const refreshDedupCache = new Map();
|
|
40
|
+
|
|
41
|
+
async function dedupRefresh(provider, oldToken, fn, log) {
|
|
42
|
+
if (!oldToken) return fn();
|
|
43
|
+
const key = `${provider}:${oldToken}`;
|
|
44
|
+
const hit = refreshDedupCache.get(key);
|
|
45
|
+
if (hit) {
|
|
46
|
+
if (hit.promise) {
|
|
47
|
+
log?.info?.("TOKEN_REFRESH", `Reusing in-flight refresh for ${provider}`);
|
|
48
|
+
return hit.promise;
|
|
49
|
+
}
|
|
50
|
+
if (hit.expiresAt > Date.now()) {
|
|
51
|
+
log?.info?.("TOKEN_REFRESH", `Reusing recent refresh result for ${provider}`);
|
|
52
|
+
return hit.result;
|
|
53
|
+
}
|
|
54
|
+
refreshDedupCache.delete(key);
|
|
55
|
+
}
|
|
56
|
+
const promise = (async () => {
|
|
57
|
+
try {
|
|
58
|
+
const result = await fn();
|
|
59
|
+
refreshDedupCache.set(key, { result, expiresAt: Date.now() + REFRESH_RESULT_TTL_MS });
|
|
60
|
+
return result;
|
|
61
|
+
} catch (err) {
|
|
62
|
+
refreshDedupCache.delete(key);
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
})();
|
|
66
|
+
refreshDedupCache.set(key, { promise });
|
|
67
|
+
return promise;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check if refresh result indicates unrecoverable error (caller should stop retry, force re-auth)
|
|
71
|
+
export function isUnrecoverableRefreshError(result) {
|
|
72
|
+
return (
|
|
73
|
+
result &&
|
|
74
|
+
typeof result === "object" &&
|
|
75
|
+
(result.error === "unrecoverable_refresh_error" ||
|
|
76
|
+
result.error === "refresh_token_reused" ||
|
|
77
|
+
result.error === "invalid_request" ||
|
|
78
|
+
result.error === "invalid_grant")
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get provider-specific refresh lead time, falls back to default buffer
|
|
83
|
+
export function getRefreshLeadMs(provider) {
|
|
84
|
+
return REFRESH_LEAD_MS[provider] || TOKEN_EXPIRY_BUFFER_MS;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Refresh OAuth access token using refresh token
|
|
89
|
+
*/
|
|
90
|
+
export async function refreshAccessToken(provider, refreshToken, credentials, log) {
|
|
91
|
+
const config = PROVIDERS[provider];
|
|
92
|
+
|
|
93
|
+
if (!config || !config.refreshUrl) {
|
|
94
|
+
log?.warn?.("TOKEN_REFRESH", `No refresh URL configured for provider: ${provider}`);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!refreshToken) {
|
|
99
|
+
log?.warn?.("TOKEN_REFRESH", `No refresh token available for provider: ${provider}`);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return dedupRefresh(provider, refreshToken, async () => {
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch(config.refreshUrl, {
|
|
106
|
+
method: "POST",
|
|
107
|
+
headers: {
|
|
108
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
109
|
+
Accept: "application/json",
|
|
110
|
+
},
|
|
111
|
+
body: new URLSearchParams({
|
|
112
|
+
grant_type: "refresh_token",
|
|
113
|
+
refresh_token: refreshToken,
|
|
114
|
+
client_id: config.clientId,
|
|
115
|
+
client_secret: config.clientSecret,
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const errorText = await response.text();
|
|
121
|
+
log?.error?.("TOKEN_REFRESH", `Failed to refresh token for ${provider}`, {
|
|
122
|
+
status: response.status,
|
|
123
|
+
error: errorText,
|
|
124
|
+
});
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const tokens = await response.json();
|
|
129
|
+
|
|
130
|
+
log?.info?.("TOKEN_REFRESH", `Successfully refreshed token for ${provider}`, {
|
|
131
|
+
hasNewAccessToken: !!tokens.access_token,
|
|
132
|
+
hasNewRefreshToken: !!tokens.refresh_token,
|
|
133
|
+
expiresIn: tokens.expires_in,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
accessToken: tokens.access_token,
|
|
138
|
+
refreshToken: tokens.refresh_token || refreshToken,
|
|
139
|
+
expiresIn: tokens.expires_in,
|
|
140
|
+
};
|
|
141
|
+
} catch (error) {
|
|
142
|
+
log?.error?.("TOKEN_REFRESH", `Error refreshing token for ${provider}`, {
|
|
143
|
+
error: error.message,
|
|
144
|
+
});
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}, log);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Specialized refresh for Claude OAuth tokens
|
|
152
|
+
*/
|
|
153
|
+
export async function refreshClaudeOAuthToken(refreshToken, log) {
|
|
154
|
+
if (!refreshToken) return null;
|
|
155
|
+
return dedupRefresh("claude", refreshToken, async () => {
|
|
156
|
+
try {
|
|
157
|
+
const response = await fetch(OAUTH_ENDPOINTS.anthropic.token, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: {
|
|
160
|
+
"Content-Type": "application/json",
|
|
161
|
+
Accept: "application/json",
|
|
162
|
+
},
|
|
163
|
+
body: JSON.stringify({
|
|
164
|
+
grant_type: "refresh_token",
|
|
165
|
+
refresh_token: refreshToken,
|
|
166
|
+
client_id: PROVIDERS.claude.clientId,
|
|
167
|
+
}),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
const errorText = await response.text();
|
|
172
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh Claude OAuth token", { status: response.status, error: errorText });
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const tokens = await response.json();
|
|
177
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Claude OAuth token", { hasNewAccessToken: !!tokens.access_token, expiresIn: tokens.expires_in });
|
|
178
|
+
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
|
|
179
|
+
} catch (error) {
|
|
180
|
+
log?.error?.("TOKEN_REFRESH", `Network error refreshing Claude token: ${error.message}`);
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}, log);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Specialized refresh for Google providers (Gemini, Antigravity)
|
|
188
|
+
*/
|
|
189
|
+
export async function refreshGoogleToken(refreshToken, clientId, clientSecret, log) {
|
|
190
|
+
if (!refreshToken) return null;
|
|
191
|
+
return dedupRefresh(`google:${clientId}`, refreshToken, async () => {
|
|
192
|
+
try {
|
|
193
|
+
const response = await fetch(OAUTH_ENDPOINTS.google.token, {
|
|
194
|
+
method: "POST",
|
|
195
|
+
headers: {
|
|
196
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
197
|
+
Accept: "application/json",
|
|
198
|
+
},
|
|
199
|
+
body: new URLSearchParams({
|
|
200
|
+
grant_type: "refresh_token",
|
|
201
|
+
refresh_token: refreshToken,
|
|
202
|
+
client_id: clientId,
|
|
203
|
+
client_secret: clientSecret,
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
const errorText = await response.text();
|
|
209
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh Google token", { status: response.status, error: errorText });
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const tokens = await response.json();
|
|
214
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Google token", { hasNewAccessToken: !!tokens.access_token, expiresIn: tokens.expires_in });
|
|
215
|
+
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
|
|
216
|
+
} catch (error) {
|
|
217
|
+
log?.error?.("TOKEN_REFRESH", `Network error refreshing Google token: ${error.message}`);
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}, log);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Specialized refresh for Qwen OAuth tokens
|
|
225
|
+
*/
|
|
226
|
+
export async function refreshQwenToken(refreshToken, log) {
|
|
227
|
+
if (!refreshToken) return null;
|
|
228
|
+
return dedupRefresh("qwen", refreshToken, async () => {
|
|
229
|
+
const endpoint = OAUTH_ENDPOINTS.qwen.token;
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const response = await fetch(endpoint, {
|
|
233
|
+
method: "POST",
|
|
234
|
+
headers: {
|
|
235
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
236
|
+
Accept: "application/json",
|
|
237
|
+
},
|
|
238
|
+
body: new URLSearchParams({
|
|
239
|
+
grant_type: "refresh_token",
|
|
240
|
+
refresh_token: refreshToken,
|
|
241
|
+
client_id: PROVIDERS.qwen.clientId,
|
|
242
|
+
}),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (response.status === 200) {
|
|
246
|
+
const tokens = await response.json();
|
|
247
|
+
|
|
248
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Qwen token", {
|
|
249
|
+
hasNewAccessToken: !!tokens.access_token,
|
|
250
|
+
hasNewRefreshToken: !!tokens.refresh_token,
|
|
251
|
+
expiresIn: tokens.expires_in,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
accessToken: tokens.access_token,
|
|
256
|
+
refreshToken: tokens.refresh_token || refreshToken,
|
|
257
|
+
expiresIn: tokens.expires_in,
|
|
258
|
+
providerSpecificData: tokens.resource_url
|
|
259
|
+
? { resourceUrl: tokens.resource_url }
|
|
260
|
+
: undefined,
|
|
261
|
+
};
|
|
262
|
+
} else {
|
|
263
|
+
const errorText = await response.text().catch(() => "");
|
|
264
|
+
log?.warn?.("TOKEN_REFRESH", `Error with Qwen endpoint`, {
|
|
265
|
+
status: response.status,
|
|
266
|
+
error: errorText,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
log?.warn?.("TOKEN_REFRESH", `Network error trying Qwen endpoint`, {
|
|
271
|
+
error: error.message,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh Qwen token");
|
|
276
|
+
return null;
|
|
277
|
+
}, log);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export async function refreshCodeBuddyToken(refreshToken, log) {
|
|
281
|
+
if (!refreshToken) return null;
|
|
282
|
+
return dedupRefresh("codebuddy", refreshToken, async () => {
|
|
283
|
+
try {
|
|
284
|
+
const response = await proxyAwareFetch(PROVIDERS.codebuddy.refreshUrl, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: {
|
|
287
|
+
"Content-Type": "application/json",
|
|
288
|
+
Accept: "application/json",
|
|
289
|
+
"User-Agent": "CLI/2.63.2 CodeBuddy/2.63.2",
|
|
290
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
291
|
+
"X-Domain": "www.codebuddy.ai",
|
|
292
|
+
"X-Refresh-Token": refreshToken,
|
|
293
|
+
"X-Auth-Refresh-Source": "plugin",
|
|
294
|
+
"X-Product": "SaaS",
|
|
295
|
+
},
|
|
296
|
+
body: "{}",
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh token for codebuddy", {
|
|
301
|
+
status: response.status,
|
|
302
|
+
error: await response.text().catch(() => ""),
|
|
303
|
+
});
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const payload = await response.json();
|
|
308
|
+
const data = payload?.data || payload;
|
|
309
|
+
const accessToken = data?.accessToken || data?.access_token;
|
|
310
|
+
if (!accessToken) return null;
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
accessToken,
|
|
314
|
+
refreshToken: data?.refreshToken || data?.refresh_token || refreshToken,
|
|
315
|
+
expiresIn: data?.expiresIn || data?.expires_in || 86400,
|
|
316
|
+
};
|
|
317
|
+
} catch (error) {
|
|
318
|
+
log?.error?.("TOKEN_REFRESH", "Error refreshing token for codebuddy", {
|
|
319
|
+
error: error.message,
|
|
320
|
+
});
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
}, log);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function classifyOAuthRefreshError(errorText = "", status = 0) {
|
|
327
|
+
let parsed = null;
|
|
328
|
+
try {
|
|
329
|
+
parsed = errorText ? JSON.parse(errorText) : null;
|
|
330
|
+
} catch {
|
|
331
|
+
parsed = null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const code = parsed?.error?.code || parsed?.error || parsed?.error_code || "";
|
|
335
|
+
const description = parsed?.error_description || parsed?.message || errorText || "";
|
|
336
|
+
const combined = `${code} ${description}`.toLowerCase();
|
|
337
|
+
const permanent = [
|
|
338
|
+
"refresh_token_expired",
|
|
339
|
+
"refresh_token_reused",
|
|
340
|
+
"refresh_token_invalidated",
|
|
341
|
+
"invalid_grant",
|
|
342
|
+
].some((marker) => combined.includes(marker));
|
|
343
|
+
|
|
344
|
+
return { status, code, description, permanent };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Specialized refresh for Codex (OpenAI) OAuth tokens.
|
|
349
|
+
* OpenAI uses rotating (one-time-use) refresh tokens.
|
|
350
|
+
* Returns { error: 'unrecoverable_refresh_error' } when token already consumed/invalid,
|
|
351
|
+
* so callers stop retrying and request re-authentication.
|
|
352
|
+
*/
|
|
353
|
+
export async function refreshCodexToken(refreshToken, log) {
|
|
354
|
+
if (!refreshToken) return null;
|
|
355
|
+
return dedupRefresh("codex", refreshToken, async () => {
|
|
356
|
+
try {
|
|
357
|
+
const response = await fetch(OAUTH_ENDPOINTS.openai.token, {
|
|
358
|
+
method: "POST",
|
|
359
|
+
headers: {
|
|
360
|
+
"Content-Type": "application/json",
|
|
361
|
+
Accept: "application/json",
|
|
362
|
+
},
|
|
363
|
+
body: JSON.stringify({
|
|
364
|
+
client_id: PROVIDERS.codex.clientId,
|
|
365
|
+
grant_type: "refresh_token",
|
|
366
|
+
refresh_token: refreshToken,
|
|
367
|
+
}),
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
if (!response.ok) {
|
|
371
|
+
const errorText = await response.text();
|
|
372
|
+
const failure = classifyOAuthRefreshError(errorText, response.status);
|
|
373
|
+
if (failure.permanent) {
|
|
374
|
+
log?.error?.("TOKEN_REFRESH", "Codex refresh token already used or invalid. Re-auth required.", {
|
|
375
|
+
status: response.status,
|
|
376
|
+
code: failure.code,
|
|
377
|
+
});
|
|
378
|
+
return { error: "unrecoverable_refresh_error", code: failure.code };
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh Codex token", {
|
|
382
|
+
status: response.status,
|
|
383
|
+
error: errorText,
|
|
384
|
+
code: failure.code,
|
|
385
|
+
permanent: failure.permanent,
|
|
386
|
+
});
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const tokens = await response.json();
|
|
391
|
+
|
|
392
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Codex token", {
|
|
393
|
+
hasNewAccessToken: !!tokens.access_token,
|
|
394
|
+
hasNewRefreshToken: !!tokens.refresh_token,
|
|
395
|
+
hasIdToken: !!tokens.id_token,
|
|
396
|
+
expiresIn: tokens.expires_in,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
accessToken: tokens.access_token,
|
|
401
|
+
refreshToken: tokens.refresh_token || refreshToken,
|
|
402
|
+
idToken: tokens.id_token,
|
|
403
|
+
expiresIn: tokens.expires_in,
|
|
404
|
+
};
|
|
405
|
+
} catch (error) {
|
|
406
|
+
log?.error?.("TOKEN_REFRESH", `Network error refreshing Codex token: ${error.message}`);
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
}, log);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Specialized refresh for Kiro (AWS CodeWhisperer) tokens
|
|
414
|
+
* Supports both AWS SSO OIDC (Builder ID/IDC) and Social Auth (Google/GitHub)
|
|
415
|
+
*/
|
|
416
|
+
export async function refreshKiroToken(refreshToken, providerSpecificData, log, proxyOptions = null) {
|
|
417
|
+
if (!refreshToken) return null;
|
|
418
|
+
return dedupRefresh("kiro", refreshToken, async () => {
|
|
419
|
+
const authMethod = providerSpecificData?.authMethod;
|
|
420
|
+
const clientId = providerSpecificData?.clientId;
|
|
421
|
+
const clientSecret = providerSpecificData?.clientSecret;
|
|
422
|
+
const region = providerSpecificData?.region;
|
|
423
|
+
|
|
424
|
+
// AWS SSO OIDC (Builder ID or IDC)
|
|
425
|
+
// If clientId and clientSecret exist, assume AWS SSO OIDC (default to builder-id if authMethod not specified)
|
|
426
|
+
if (clientId && clientSecret) {
|
|
427
|
+
const isIDC = authMethod === "idc";
|
|
428
|
+
const endpoint = isIDC && region
|
|
429
|
+
? `https://oidc.${region}.amazonaws.com/token`
|
|
430
|
+
: "https://oidc.us-east-1.amazonaws.com/token";
|
|
431
|
+
|
|
432
|
+
const response = await proxyAwareFetch(endpoint, {
|
|
433
|
+
method: "POST",
|
|
434
|
+
headers: {
|
|
435
|
+
"Content-Type": "application/json",
|
|
436
|
+
Accept: "application/json",
|
|
437
|
+
},
|
|
438
|
+
body: JSON.stringify({
|
|
439
|
+
clientId: clientId,
|
|
440
|
+
clientSecret: clientSecret,
|
|
441
|
+
refreshToken: refreshToken,
|
|
442
|
+
grantType: "refresh_token",
|
|
443
|
+
}),
|
|
444
|
+
}, proxyOptions);
|
|
445
|
+
|
|
446
|
+
if (!response.ok) {
|
|
447
|
+
const errorText = await response.text();
|
|
448
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh Kiro AWS token", {
|
|
449
|
+
status: response.status,
|
|
450
|
+
error: errorText,
|
|
451
|
+
});
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const tokens = await response.json();
|
|
456
|
+
|
|
457
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Kiro AWS token", {
|
|
458
|
+
hasNewAccessToken: !!tokens.accessToken,
|
|
459
|
+
expiresIn: tokens.expiresIn,
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
accessToken: tokens.accessToken,
|
|
464
|
+
refreshToken: tokens.refreshToken || refreshToken,
|
|
465
|
+
expiresIn: tokens.expiresIn,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Social Auth (Google/GitHub) - use Kiro's refresh endpoint
|
|
470
|
+
const response = await proxyAwareFetch(PROVIDERS.kiro.tokenUrl, {
|
|
471
|
+
method: "POST",
|
|
472
|
+
headers: {
|
|
473
|
+
"Content-Type": "application/json",
|
|
474
|
+
Accept: "application/json",
|
|
475
|
+
"User-Agent": "kiro-cli/1.0.0",
|
|
476
|
+
},
|
|
477
|
+
body: JSON.stringify({
|
|
478
|
+
refreshToken: refreshToken,
|
|
479
|
+
}),
|
|
480
|
+
}, proxyOptions);
|
|
481
|
+
|
|
482
|
+
if (!response.ok) {
|
|
483
|
+
const errorText = await response.text();
|
|
484
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh Kiro social token", {
|
|
485
|
+
status: response.status,
|
|
486
|
+
error: errorText,
|
|
487
|
+
});
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const tokens = await response.json();
|
|
492
|
+
|
|
493
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Kiro social token", {
|
|
494
|
+
hasNewAccessToken: !!tokens.accessToken,
|
|
495
|
+
expiresIn: tokens.expiresIn,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
accessToken: tokens.accessToken,
|
|
500
|
+
refreshToken: tokens.refreshToken || refreshToken,
|
|
501
|
+
expiresIn: tokens.expiresIn,
|
|
502
|
+
};
|
|
503
|
+
}, log);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Specialized refresh for iFlow OAuth tokens
|
|
508
|
+
*/
|
|
509
|
+
export async function refreshIflowToken(refreshToken, log) {
|
|
510
|
+
if (!refreshToken) return null;
|
|
511
|
+
return dedupRefresh("iflow", refreshToken, async () => {
|
|
512
|
+
const basicAuth = btoa(`${PROVIDERS.iflow.clientId}:${PROVIDERS.iflow.clientSecret}`);
|
|
513
|
+
|
|
514
|
+
const response = await fetch(OAUTH_ENDPOINTS.iflow.token, {
|
|
515
|
+
method: "POST",
|
|
516
|
+
headers: {
|
|
517
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
518
|
+
Accept: "application/json",
|
|
519
|
+
Authorization: `Basic ${basicAuth}`,
|
|
520
|
+
},
|
|
521
|
+
body: new URLSearchParams({
|
|
522
|
+
grant_type: "refresh_token",
|
|
523
|
+
refresh_token: refreshToken,
|
|
524
|
+
client_id: PROVIDERS.iflow.clientId,
|
|
525
|
+
client_secret: PROVIDERS.iflow.clientSecret,
|
|
526
|
+
}),
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
if (!response.ok) {
|
|
530
|
+
const errorText = await response.text();
|
|
531
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh iFlow token", {
|
|
532
|
+
status: response.status,
|
|
533
|
+
error: errorText,
|
|
534
|
+
});
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const tokens = await response.json();
|
|
539
|
+
|
|
540
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed iFlow token", {
|
|
541
|
+
hasNewAccessToken: !!tokens.access_token,
|
|
542
|
+
hasNewRefreshToken: !!tokens.refresh_token,
|
|
543
|
+
expiresIn: tokens.expires_in,
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
return {
|
|
547
|
+
accessToken: tokens.access_token,
|
|
548
|
+
refreshToken: tokens.refresh_token || refreshToken,
|
|
549
|
+
expiresIn: tokens.expires_in,
|
|
550
|
+
};
|
|
551
|
+
}, log);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Specialized refresh for GitHub Copilot OAuth tokens
|
|
556
|
+
*/
|
|
557
|
+
export async function refreshGitHubToken(refreshToken, log) {
|
|
558
|
+
if (!refreshToken) return null;
|
|
559
|
+
return dedupRefresh("github", refreshToken, async () => {
|
|
560
|
+
const params = {
|
|
561
|
+
grant_type: "refresh_token",
|
|
562
|
+
refresh_token: refreshToken,
|
|
563
|
+
client_id: PROVIDERS.github.clientId,
|
|
564
|
+
};
|
|
565
|
+
if (PROVIDERS.github.clientSecret) {
|
|
566
|
+
params.client_secret = PROVIDERS.github.clientSecret;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const response = await fetch(OAUTH_ENDPOINTS.github.token, {
|
|
570
|
+
method: "POST",
|
|
571
|
+
headers: {
|
|
572
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
573
|
+
Accept: "application/json",
|
|
574
|
+
},
|
|
575
|
+
body: new URLSearchParams(params),
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
if (!response.ok) {
|
|
579
|
+
const errorText = await response.text();
|
|
580
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh GitHub token", {
|
|
581
|
+
status: response.status,
|
|
582
|
+
error: errorText,
|
|
583
|
+
});
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const tokens = await response.json();
|
|
588
|
+
|
|
589
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed GitHub token", {
|
|
590
|
+
hasNewAccessToken: !!tokens.access_token,
|
|
591
|
+
hasNewRefreshToken: !!tokens.refresh_token,
|
|
592
|
+
expiresIn: tokens.expires_in,
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
return {
|
|
596
|
+
accessToken: tokens.access_token,
|
|
597
|
+
refreshToken: tokens.refresh_token || refreshToken,
|
|
598
|
+
expiresIn: tokens.expires_in,
|
|
599
|
+
};
|
|
600
|
+
}, log);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Refresh GitHub Copilot token using GitHub access token
|
|
605
|
+
*/
|
|
606
|
+
export async function refreshCopilotToken(githubAccessToken, log) {
|
|
607
|
+
if (!githubAccessToken) return null;
|
|
608
|
+
return dedupRefresh("copilot", githubAccessToken, async () => {
|
|
609
|
+
try {
|
|
610
|
+
const response = await fetch("https://api.github.com/copilot_internal/v2/token", {
|
|
611
|
+
headers: {
|
|
612
|
+
"Authorization": `token ${githubAccessToken}`,
|
|
613
|
+
"User-Agent": GITHUB_COPILOT.USER_AGENT,
|
|
614
|
+
"Editor-Version": `vscode/${GITHUB_COPILOT.VSCODE_VERSION}`,
|
|
615
|
+
"Editor-Plugin-Version": `copilot-chat/${GITHUB_COPILOT.COPILOT_CHAT_VERSION}`,
|
|
616
|
+
"Accept": "application/json",
|
|
617
|
+
"x-github-api-version": GITHUB_COPILOT.API_VERSION
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
if (!response.ok) {
|
|
622
|
+
const errorText = await response.text();
|
|
623
|
+
log?.error?.("TOKEN_REFRESH", "Failed to refresh Copilot token", {
|
|
624
|
+
status: response.status,
|
|
625
|
+
error: errorText
|
|
626
|
+
});
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const data = await response.json();
|
|
631
|
+
|
|
632
|
+
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Copilot token", {
|
|
633
|
+
hasToken: !!data.token,
|
|
634
|
+
expiresAt: data.expires_at
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
return {
|
|
638
|
+
token: data.token,
|
|
639
|
+
expiresAt: data.expires_at
|
|
640
|
+
};
|
|
641
|
+
} catch (error) {
|
|
642
|
+
log?.error?.("TOKEN_REFRESH", "Error refreshing Copilot token", {
|
|
643
|
+
error: error.message
|
|
644
|
+
});
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
}, log);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Get access token for a specific provider (with in-flight dedup).
|
|
652
|
+
* If a refresh is already in-flight for same provider+token, share the promise
|
|
653
|
+
* to prevent parallel OAuth requests → Auth0 'refresh_token_reused' family revoke.
|
|
654
|
+
*/
|
|
655
|
+
export async function getAccessToken(provider, credentials, log) {
|
|
656
|
+
if (!credentials || !credentials.refreshToken || typeof credentials.refreshToken !== "string") {
|
|
657
|
+
log?.warn?.("TOKEN_REFRESH", `No valid refresh token available for provider: ${provider}`);
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
// Dedup is handled inside each refreshXxxToken function
|
|
661
|
+
return _getAccessTokenInternal(provider, credentials, log);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
async function _getAccessTokenInternal(provider, credentials, log) {
|
|
665
|
+
switch (provider) {
|
|
666
|
+
case "gemini":
|
|
667
|
+
case "gemini-cli":
|
|
668
|
+
case "antigravity":
|
|
669
|
+
return await refreshGoogleToken(
|
|
670
|
+
credentials.refreshToken,
|
|
671
|
+
PROVIDERS[provider].clientId,
|
|
672
|
+
PROVIDERS[provider].clientSecret,
|
|
673
|
+
log
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
case "claude":
|
|
677
|
+
return await refreshClaudeOAuthToken(credentials.refreshToken, log);
|
|
678
|
+
|
|
679
|
+
case "codex":
|
|
680
|
+
return await refreshCodexToken(credentials.refreshToken, log);
|
|
681
|
+
|
|
682
|
+
case "qwen":
|
|
683
|
+
return await refreshQwenToken(credentials.refreshToken, log);
|
|
684
|
+
|
|
685
|
+
case "codebuddy":
|
|
686
|
+
return await refreshCodeBuddyToken(credentials.refreshToken, log);
|
|
687
|
+
|
|
688
|
+
case "iflow":
|
|
689
|
+
return await refreshIflowToken(credentials.refreshToken, log);
|
|
690
|
+
|
|
691
|
+
case "github":
|
|
692
|
+
return await refreshGitHubToken(credentials.refreshToken, log);
|
|
693
|
+
|
|
694
|
+
case "kiro":
|
|
695
|
+
return await refreshKiroToken(
|
|
696
|
+
credentials.refreshToken,
|
|
697
|
+
credentials.providerSpecificData,
|
|
698
|
+
log
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
case "xai":
|
|
702
|
+
return await refreshXaiToken(credentials.refreshToken, log);
|
|
703
|
+
|
|
704
|
+
case "vertex":
|
|
705
|
+
case "vertex-partner": {
|
|
706
|
+
const saJson = parseVertexSaJson(credentials.apiKey);
|
|
707
|
+
if (!saJson) return null;
|
|
708
|
+
return await refreshVertexToken(saJson, log);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
default:
|
|
712
|
+
log?.warn?.("TOKEN_REFRESH", `Unsupported provider for token refresh: ${provider}`);
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Refresh token by provider type (helper for handlers)
|
|
719
|
+
*/
|
|
720
|
+
export async function refreshTokenByProvider(provider, credentials, log) {
|
|
721
|
+
if (!credentials.refreshToken) return null;
|
|
722
|
+
|
|
723
|
+
switch (provider) {
|
|
724
|
+
case "gemini-cli":
|
|
725
|
+
case "antigravity":
|
|
726
|
+
return refreshGoogleToken(
|
|
727
|
+
credentials.refreshToken,
|
|
728
|
+
PROVIDERS[provider].clientId,
|
|
729
|
+
PROVIDERS[provider].clientSecret,
|
|
730
|
+
log
|
|
731
|
+
);
|
|
732
|
+
case "claude":
|
|
733
|
+
return refreshClaudeOAuthToken(credentials.refreshToken, log);
|
|
734
|
+
case "codex":
|
|
735
|
+
return refreshCodexToken(credentials.refreshToken, log);
|
|
736
|
+
case "qwen":
|
|
737
|
+
return refreshQwenToken(credentials.refreshToken, log);
|
|
738
|
+
case "codebuddy":
|
|
739
|
+
return refreshCodeBuddyToken(credentials.refreshToken, log);
|
|
740
|
+
case "iflow":
|
|
741
|
+
return refreshIflowToken(credentials.refreshToken, log);
|
|
742
|
+
case "github":
|
|
743
|
+
return refreshGitHubToken(credentials.refreshToken, log);
|
|
744
|
+
case "kiro":
|
|
745
|
+
return refreshKiroToken(
|
|
746
|
+
credentials.refreshToken,
|
|
747
|
+
credentials.providerSpecificData,
|
|
748
|
+
log
|
|
749
|
+
);
|
|
750
|
+
case "xai":
|
|
751
|
+
return refreshXaiToken(credentials.refreshToken, log);
|
|
752
|
+
case "vertex":
|
|
753
|
+
case "vertex-partner": {
|
|
754
|
+
const saJson = parseVertexSaJson(credentials.apiKey);
|
|
755
|
+
if (!saJson) return null;
|
|
756
|
+
return refreshVertexToken(saJson, log);
|
|
757
|
+
}
|
|
758
|
+
default:
|
|
759
|
+
return refreshAccessToken(provider, credentials.refreshToken, credentials, log);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Format credentials for provider
|
|
765
|
+
*/
|
|
766
|
+
export function formatProviderCredentials(provider, credentials, log) {
|
|
767
|
+
const config = PROVIDERS[provider];
|
|
768
|
+
if (!config) {
|
|
769
|
+
log?.warn?.("TOKEN_REFRESH", `No configuration found for provider: ${provider}`);
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
switch (provider) {
|
|
774
|
+
case "gemini":
|
|
775
|
+
return {
|
|
776
|
+
apiKey: credentials.apiKey,
|
|
777
|
+
accessToken: credentials.accessToken,
|
|
778
|
+
projectId: credentials.projectId
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
case "claude":
|
|
782
|
+
return {
|
|
783
|
+
apiKey: credentials.apiKey,
|
|
784
|
+
accessToken: credentials.accessToken
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
case "codex":
|
|
788
|
+
case "qwen":
|
|
789
|
+
case "iflow":
|
|
790
|
+
case "openai":
|
|
791
|
+
case "openrouter":
|
|
792
|
+
case "xai":
|
|
793
|
+
return {
|
|
794
|
+
apiKey: credentials.apiKey,
|
|
795
|
+
accessToken: credentials.accessToken
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
case "antigravity":
|
|
799
|
+
case "gemini-cli":
|
|
800
|
+
return {
|
|
801
|
+
accessToken: credentials.accessToken,
|
|
802
|
+
refreshToken: credentials.refreshToken,
|
|
803
|
+
projectId: credentials.projectId
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
default:
|
|
807
|
+
return {
|
|
808
|
+
apiKey: credentials.apiKey,
|
|
809
|
+
accessToken: credentials.accessToken,
|
|
810
|
+
refreshToken: credentials.refreshToken
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Get all access tokens for a user
|
|
817
|
+
*/
|
|
818
|
+
export async function getAllAccessTokens(userInfo, log) {
|
|
819
|
+
const results = {};
|
|
820
|
+
|
|
821
|
+
if (userInfo.connections && Array.isArray(userInfo.connections)) {
|
|
822
|
+
for (const connection of userInfo.connections) {
|
|
823
|
+
if (connection.isActive && connection.provider) {
|
|
824
|
+
const token = await getAccessToken(connection.provider, {
|
|
825
|
+
refreshToken: connection.refreshToken
|
|
826
|
+
}, log);
|
|
827
|
+
|
|
828
|
+
if (token) {
|
|
829
|
+
results[connection.provider] = token;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
return results;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Parse Vertex AI Service Account JSON from apiKey string
|
|
840
|
+
*/
|
|
841
|
+
export function parseVertexSaJson(apiKey) {
|
|
842
|
+
if (typeof apiKey !== "string") return null;
|
|
843
|
+
try {
|
|
844
|
+
const parsed = JSON.parse(apiKey);
|
|
845
|
+
if (parsed.type === "service_account" && parsed.client_email && parsed.private_key && parsed.project_id) {
|
|
846
|
+
return parsed;
|
|
847
|
+
}
|
|
848
|
+
return null;
|
|
849
|
+
} catch {
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Cache Vertex tokens keyed by service account email { token, expiresAt }
|
|
855
|
+
const vertexTokenCache = new Map();
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Mint a short-lived OAuth2 Bearer token for Google Cloud Vertex AI
|
|
859
|
+
* using Service Account JSON + jose (RS256 JWT assertion flow).
|
|
860
|
+
* Token is cached until 5 minutes before expiry.
|
|
861
|
+
*/
|
|
862
|
+
export async function refreshVertexToken(saJson, log) {
|
|
863
|
+
const cacheKey = saJson.client_email;
|
|
864
|
+
const cached = vertexTokenCache.get(cacheKey);
|
|
865
|
+
|
|
866
|
+
// Return cached token if still valid (5-min buffer)
|
|
867
|
+
if (cached && cached.expiresAt - Date.now() > 5 * 60 * 1000) {
|
|
868
|
+
return { accessToken: cached.token, expiresAt: cached.expiresAt };
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
try {
|
|
872
|
+
const { SignJWT, importPKCS8 } = await import("jose");
|
|
873
|
+
log?.debug?.("TOKEN_REFRESH", `Vertex minting token for ${saJson.client_email}`);
|
|
874
|
+
const privateKey = await importPKCS8(saJson.private_key.replace(/\\n/g, "\n"), "RS256");
|
|
875
|
+
const now = Math.floor(Date.now() / 1000);
|
|
876
|
+
|
|
877
|
+
const jwt = await new SignJWT({ scope: "https://www.googleapis.com/auth/cloud-platform" })
|
|
878
|
+
.setProtectedHeader({ alg: "RS256" })
|
|
879
|
+
.setIssuer(saJson.client_email)
|
|
880
|
+
.setAudience("https://oauth2.googleapis.com/token")
|
|
881
|
+
.setIssuedAt(now)
|
|
882
|
+
.setExpirationTime(now + 3600)
|
|
883
|
+
.sign(privateKey);
|
|
884
|
+
|
|
885
|
+
const res = await fetch("https://oauth2.googleapis.com/token", {
|
|
886
|
+
method: "POST",
|
|
887
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
888
|
+
body: new URLSearchParams({
|
|
889
|
+
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
890
|
+
assertion: jwt,
|
|
891
|
+
}),
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
if (!res.ok) {
|
|
895
|
+
const err = await res.text();
|
|
896
|
+
log?.error?.("TOKEN_REFRESH", `Vertex token mint failed: ${err}`);
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const { access_token, expires_in } = await res.json();
|
|
901
|
+
const expiresAt = Date.now() + (expires_in ?? 3600) * 1000;
|
|
902
|
+
|
|
903
|
+
vertexTokenCache.set(cacheKey, { token: access_token, expiresAt });
|
|
904
|
+
log?.info?.("TOKEN_REFRESH", `Vertex token minted for ${saJson.client_email}`);
|
|
905
|
+
|
|
906
|
+
return { accessToken: access_token, expiresAt };
|
|
907
|
+
} catch (error) {
|
|
908
|
+
log?.error?.("TOKEN_REFRESH", `Vertex token error: ${error.message}`);
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Refresh token with retry and exponential backoff
|
|
915
|
+
* Retries on failure with increasing delay: 1s, 2s, 3s...
|
|
916
|
+
* @param {function} refreshFn - Async function that returns token or null
|
|
917
|
+
* @param {number} maxRetries - Max retry attempts (default 3)
|
|
918
|
+
* @param {object} log - Logger instance (optional)
|
|
919
|
+
* @returns {Promise<object|null>} Token result or null if all retries fail
|
|
920
|
+
*/
|
|
921
|
+
export async function refreshWithRetry(refreshFn, maxRetries = 3, log = null) {
|
|
922
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
923
|
+
if (attempt > 0) {
|
|
924
|
+
const delay = attempt * 1000;
|
|
925
|
+
log?.debug?.("TOKEN_REFRESH", `Retry ${attempt}/${maxRetries} after ${delay}ms`);
|
|
926
|
+
await new Promise(r => setTimeout(r, delay));
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
try {
|
|
930
|
+
const result = await refreshFn();
|
|
931
|
+
if (result) return result;
|
|
932
|
+
} catch (error) {
|
|
933
|
+
log?.warn?.("TOKEN_REFRESH", `Attempt ${attempt + 1}/${maxRetries} failed: ${error.message}`);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
log?.error?.("TOKEN_REFRESH", `All ${maxRetries} retry attempts failed`);
|
|
938
|
+
return null;
|
|
939
|
+
}
|