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.
Files changed (763) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/LICENSE +21 -0
  3. package/README.md +96 -0
  4. package/README.zh-CN.md +1311 -0
  5. package/assets/pixel-router-2d.png +0 -0
  6. package/cli/LICENSE +42 -0
  7. package/cli/README.md +125 -0
  8. package/cli/app/package.json +58 -0
  9. package/cli/cli.js +806 -0
  10. package/cli/package.json +48 -0
  11. package/i18n/README.ja-JP.md +1209 -0
  12. package/i18n/README.ru.md +1311 -0
  13. package/i18n/README.vi.md +1310 -0
  14. package/i18n/README.zh-CN.md +1306 -0
  15. package/images/9router.png +0 -0
  16. package/jsconfig.json +12 -0
  17. package/next.config.mjs +71 -0
  18. package/open-sse/config/appConstants.js +203 -0
  19. package/open-sse/config/codexInstructions.js +119 -0
  20. package/open-sse/config/constants.js +4 -0
  21. package/open-sse/config/defaultThinkingSignature.js +12 -0
  22. package/open-sse/config/errorConfig.js +85 -0
  23. package/open-sse/config/googleTtsLanguages.js +62 -0
  24. package/open-sse/config/kiroConstants.js +322 -0
  25. package/open-sse/config/models.js +13 -0
  26. package/open-sse/config/ollamaModels.js +19 -0
  27. package/open-sse/config/providerModels.js +944 -0
  28. package/open-sse/config/providers.js +458 -0
  29. package/open-sse/config/runtimeConfig.js +72 -0
  30. package/open-sse/config/ttsModels.js +129 -0
  31. package/open-sse/executors/antigravity.js +504 -0
  32. package/open-sse/executors/azure.js +57 -0
  33. package/open-sse/executors/base.js +185 -0
  34. package/open-sse/executors/codex.js +469 -0
  35. package/open-sse/executors/commandcode.js +88 -0
  36. package/open-sse/executors/cursor.js +795 -0
  37. package/open-sse/executors/default.js +497 -0
  38. package/open-sse/executors/gemini-cli.js +89 -0
  39. package/open-sse/executors/github.js +379 -0
  40. package/open-sse/executors/grok-web.js +345 -0
  41. package/open-sse/executors/iflow.js +108 -0
  42. package/open-sse/executors/index.js +75 -0
  43. package/open-sse/executors/kiro.js +508 -0
  44. package/open-sse/executors/ollama-local.js +14 -0
  45. package/open-sse/executors/opencode-go.js +41 -0
  46. package/open-sse/executors/opencode.js +32 -0
  47. package/open-sse/executors/perplexity-web.js +507 -0
  48. package/open-sse/executors/qoder.js +450 -0
  49. package/open-sse/executors/qwen.js +129 -0
  50. package/open-sse/executors/vertex.js +131 -0
  51. package/open-sse/executors/xiaomi-tokenplan.js +19 -0
  52. package/open-sse/handlers/chatCore/nonStreamingHandler.js +230 -0
  53. package/open-sse/handlers/chatCore/requestDetail.js +102 -0
  54. package/open-sse/handlers/chatCore/sseToJsonHandler.js +231 -0
  55. package/open-sse/handlers/chatCore/streamingHandler.js +103 -0
  56. package/open-sse/handlers/chatCore.js +287 -0
  57. package/open-sse/handlers/embeddingProviders/_base.js +4 -0
  58. package/open-sse/handlers/embeddingProviders/gemini.js +54 -0
  59. package/open-sse/handlers/embeddingProviders/index.js +23 -0
  60. package/open-sse/handlers/embeddingProviders/openai.js +39 -0
  61. package/open-sse/handlers/embeddingProviders/openaiCompatNode.js +13 -0
  62. package/open-sse/handlers/embeddingsCore.js +126 -0
  63. package/open-sse/handlers/fetch/index.js +237 -0
  64. package/open-sse/handlers/imageGenerationCore.js +189 -0
  65. package/open-sse/handlers/imageProviders/_base.js +31 -0
  66. package/open-sse/handlers/imageProviders/blackForestLabs.js +43 -0
  67. package/open-sse/handlers/imageProviders/cloudflareAi.js +178 -0
  68. package/open-sse/handlers/imageProviders/codex.js +198 -0
  69. package/open-sse/handlers/imageProviders/comfyui.js +8 -0
  70. package/open-sse/handlers/imageProviders/falAi.js +41 -0
  71. package/open-sse/handlers/imageProviders/gemini.js +25 -0
  72. package/open-sse/handlers/imageProviders/huggingface.js +22 -0
  73. package/open-sse/handlers/imageProviders/index.js +40 -0
  74. package/open-sse/handlers/imageProviders/nanobanana.js +58 -0
  75. package/open-sse/handlers/imageProviders/openai.js +40 -0
  76. package/open-sse/handlers/imageProviders/runwayml.js +47 -0
  77. package/open-sse/handlers/imageProviders/sdwebui.js +17 -0
  78. package/open-sse/handlers/imageProviders/stabilityAi.js +34 -0
  79. package/open-sse/handlers/responsesHandler.js +103 -0
  80. package/open-sse/handlers/search/callers.js +371 -0
  81. package/open-sse/handlers/search/chatSearch.js +409 -0
  82. package/open-sse/handlers/search/index.js +201 -0
  83. package/open-sse/handlers/search/normalizers.js +223 -0
  84. package/open-sse/handlers/sttCore.js +194 -0
  85. package/open-sse/handlers/ttsCore.js +74 -0
  86. package/open-sse/handlers/ttsProviders/_base.js +39 -0
  87. package/open-sse/handlers/ttsProviders/edgeTts.js +89 -0
  88. package/open-sse/handlers/ttsProviders/elevenlabs.js +48 -0
  89. package/open-sse/handlers/ttsProviders/gemini.js +117 -0
  90. package/open-sse/handlers/ttsProviders/genericFormats.js +169 -0
  91. package/open-sse/handlers/ttsProviders/googleTts.js +54 -0
  92. package/open-sse/handlers/ttsProviders/index.js +50 -0
  93. package/open-sse/handlers/ttsProviders/localDevice.js +87 -0
  94. package/open-sse/handlers/ttsProviders/minimax.js +59 -0
  95. package/open-sse/handlers/ttsProviders/openai.js +30 -0
  96. package/open-sse/handlers/ttsProviders/openrouter.js +70 -0
  97. package/open-sse/index.js +82 -0
  98. package/open-sse/rtk/applyFilter.js +15 -0
  99. package/open-sse/rtk/autodetect.js +111 -0
  100. package/open-sse/rtk/caveman.js +100 -0
  101. package/open-sse/rtk/cavemanPrompts.js +78 -0
  102. package/open-sse/rtk/constants.js +55 -0
  103. package/open-sse/rtk/filters/buildOutput.js +127 -0
  104. package/open-sse/rtk/filters/dedupLog.js +44 -0
  105. package/open-sse/rtk/filters/find.js +49 -0
  106. package/open-sse/rtk/filters/gitDiff.js +92 -0
  107. package/open-sse/rtk/filters/gitStatus.js +117 -0
  108. package/open-sse/rtk/filters/grep.js +48 -0
  109. package/open-sse/rtk/filters/ls.js +79 -0
  110. package/open-sse/rtk/filters/readNumbered.js +27 -0
  111. package/open-sse/rtk/filters/searchList.js +52 -0
  112. package/open-sse/rtk/filters/smartTruncate.js +15 -0
  113. package/open-sse/rtk/filters/tree.js +32 -0
  114. package/open-sse/rtk/index.js +155 -0
  115. package/open-sse/rtk/registry.js +38 -0
  116. package/open-sse/services/accountFallback.js +238 -0
  117. package/open-sse/services/combo.js +198 -0
  118. package/open-sse/services/compact.js +71 -0
  119. package/open-sse/services/kiroModels.js +332 -0
  120. package/open-sse/services/model.js +261 -0
  121. package/open-sse/services/oauthCredentialManager.js +151 -0
  122. package/open-sse/services/projectId.js +306 -0
  123. package/open-sse/services/provider.js +356 -0
  124. package/open-sse/services/qoderModels.js +214 -0
  125. package/open-sse/services/tokenRefresh.js +939 -0
  126. package/open-sse/services/usage.js +1496 -0
  127. package/open-sse/transformer/responsesTransformer.js +439 -0
  128. package/open-sse/transformer/streamToJsonConverter.js +103 -0
  129. package/open-sse/translator/formats.js +36 -0
  130. package/open-sse/translator/helpers/claudeHelper.js +216 -0
  131. package/open-sse/translator/helpers/geminiHelper.js +372 -0
  132. package/open-sse/translator/helpers/imageHelper.js +34 -0
  133. package/open-sse/translator/helpers/maxTokensHelper.js +27 -0
  134. package/open-sse/translator/helpers/openaiHelper.js +130 -0
  135. package/open-sse/translator/helpers/responsesApiHelper.js +139 -0
  136. package/open-sse/translator/helpers/toolCallHelper.js +148 -0
  137. package/open-sse/translator/index.js +251 -0
  138. package/open-sse/translator/request/antigravity-to-openai.js +229 -0
  139. package/open-sse/translator/request/claude-to-openai.js +232 -0
  140. package/open-sse/translator/request/gemini-to-openai.js +147 -0
  141. package/open-sse/translator/request/openai-responses.js +318 -0
  142. package/open-sse/translator/request/openai-to-claude.js +401 -0
  143. package/open-sse/translator/request/openai-to-commandcode.js +170 -0
  144. package/open-sse/translator/request/openai-to-cursor.js +183 -0
  145. package/open-sse/translator/request/openai-to-gemini.js +470 -0
  146. package/open-sse/translator/request/openai-to-kiro.js +629 -0
  147. package/open-sse/translator/request/openai-to-kiro.old.js +278 -0
  148. package/open-sse/translator/request/openai-to-ollama.js +192 -0
  149. package/open-sse/translator/request/openai-to-vertex.js +42 -0
  150. package/open-sse/translator/response/claude-to-openai.js +206 -0
  151. package/open-sse/translator/response/commandcode-to-openai.js +197 -0
  152. package/open-sse/translator/response/cursor-to-openai.js +30 -0
  153. package/open-sse/translator/response/gemini-to-openai.js +245 -0
  154. package/open-sse/translator/response/kiro-to-openai.js +195 -0
  155. package/open-sse/translator/response/ollama-to-openai.js +152 -0
  156. package/open-sse/translator/response/openai-responses.js +590 -0
  157. package/open-sse/translator/response/openai-to-antigravity.js +122 -0
  158. package/open-sse/translator/response/openai-to-claude.js +266 -0
  159. package/open-sse/utils/bypassHandler.js +298 -0
  160. package/open-sse/utils/claudeCloaking.js +155 -0
  161. package/open-sse/utils/claudeHeaderCache.js +70 -0
  162. package/open-sse/utils/clientDetector.js +63 -0
  163. package/open-sse/utils/cursorChecksum.js +149 -0
  164. package/open-sse/utils/cursorProtobuf.js +904 -0
  165. package/open-sse/utils/debugLog.js +14 -0
  166. package/open-sse/utils/error.js +147 -0
  167. package/open-sse/utils/ollamaTransform.js +85 -0
  168. package/open-sse/utils/proxyFetch.js +368 -0
  169. package/open-sse/utils/reasoningContentInjector.js +79 -0
  170. package/open-sse/utils/requestLogger.js +260 -0
  171. package/open-sse/utils/responsesStreamHelpers.js +49 -0
  172. package/open-sse/utils/sessionManager.js +82 -0
  173. package/open-sse/utils/stream.js +462 -0
  174. package/open-sse/utils/streamHandler.js +250 -0
  175. package/open-sse/utils/streamHelpers.js +122 -0
  176. package/open-sse/utils/toolDeduper.js +49 -0
  177. package/open-sse/utils/usageTracking.js +347 -0
  178. package/package.json +100 -0
  179. package/postcss.config.mjs +12 -0
  180. package/public/favicon.svg +11 -0
  181. package/public/file.svg +1 -0
  182. package/public/globe.svg +1 -0
  183. package/public/i18n/literals/ar.json +195 -0
  184. package/public/i18n/literals/bn.json +195 -0
  185. package/public/i18n/literals/cs.json +195 -0
  186. package/public/i18n/literals/da.json +195 -0
  187. package/public/i18n/literals/de.json +195 -0
  188. package/public/i18n/literals/el.json +195 -0
  189. package/public/i18n/literals/es.json +195 -0
  190. package/public/i18n/literals/fi.json +195 -0
  191. package/public/i18n/literals/fr.json +195 -0
  192. package/public/i18n/literals/he.json +195 -0
  193. package/public/i18n/literals/hi.json +195 -0
  194. package/public/i18n/literals/hu.json +195 -0
  195. package/public/i18n/literals/id.json +195 -0
  196. package/public/i18n/literals/it.json +195 -0
  197. package/public/i18n/literals/ja.json +195 -0
  198. package/public/i18n/literals/ko.json +195 -0
  199. package/public/i18n/literals/nl.json +195 -0
  200. package/public/i18n/literals/no.json +195 -0
  201. package/public/i18n/literals/pl.json +195 -0
  202. package/public/i18n/literals/pt-BR.json +195 -0
  203. package/public/i18n/literals/pt-PT.json +195 -0
  204. package/public/i18n/literals/ro.json +195 -0
  205. package/public/i18n/literals/ru.json +195 -0
  206. package/public/i18n/literals/sv.json +195 -0
  207. package/public/i18n/literals/th.json +195 -0
  208. package/public/i18n/literals/tl.json +195 -0
  209. package/public/i18n/literals/tr.json +195 -0
  210. package/public/i18n/literals/uk.json +195 -0
  211. package/public/i18n/literals/ur.json +195 -0
  212. package/public/i18n/literals/vi.json +195 -0
  213. package/public/i18n/literals/zh-CN.json +772 -0
  214. package/public/i18n/literals/zh-TW.json +195 -0
  215. package/public/icons/discord.svg +4 -0
  216. package/public/icons/icon-192.svg +4 -0
  217. package/public/icons/icon-512.svg +4 -0
  218. package/public/next.svg +1 -0
  219. package/public/providers/alicode-intl.png +0 -0
  220. package/public/providers/alicode.png +0 -0
  221. package/public/providers/amp.png +0 -0
  222. package/public/providers/anthropic-m.png +0 -0
  223. package/public/providers/anthropic.png +0 -0
  224. package/public/providers/antigravity.png +0 -0
  225. package/public/providers/assemblyai.png +0 -0
  226. package/public/providers/aws-polly.png +0 -0
  227. package/public/providers/azure.png +0 -0
  228. package/public/providers/black-forest-labs.png +0 -0
  229. package/public/providers/blackbox.png +0 -0
  230. package/public/providers/brave-search.png +0 -0
  231. package/public/providers/byteplus.png +0 -0
  232. package/public/providers/cartesia.png +0 -0
  233. package/public/providers/cerebras.png +0 -0
  234. package/public/providers/chutes.png +0 -0
  235. package/public/providers/claude.png +0 -0
  236. package/public/providers/cline.png +0 -0
  237. package/public/providers/cloudflare-ai.png +0 -0
  238. package/public/providers/codebuddy.svg +37 -0
  239. package/public/providers/codex.png +0 -0
  240. package/public/providers/cohere.png +0 -0
  241. package/public/providers/comfyui.png +0 -0
  242. package/public/providers/commandcode.png +0 -0
  243. package/public/providers/continue.png +0 -0
  244. package/public/providers/copilot.png +0 -0
  245. package/public/providers/coqui.png +0 -0
  246. package/public/providers/cursor.png +0 -0
  247. package/public/providers/deepgram.png +0 -0
  248. package/public/providers/deepseek-tui.png +0 -0
  249. package/public/providers/deepseek.png +0 -0
  250. package/public/providers/droid.png +0 -0
  251. package/public/providers/edge-tts.png +0 -0
  252. package/public/providers/elevenlabs.png +0 -0
  253. package/public/providers/exa.png +0 -0
  254. package/public/providers/fal-ai.png +0 -0
  255. package/public/providers/firecrawl.png +0 -0
  256. package/public/providers/fireworks.png +0 -0
  257. package/public/providers/gemini-cli.png +0 -0
  258. package/public/providers/gemini.png +0 -0
  259. package/public/providers/github.png +0 -0
  260. package/public/providers/glm-cn.png +0 -0
  261. package/public/providers/glm.png +0 -0
  262. package/public/providers/google-pse.png +0 -0
  263. package/public/providers/google-tts.png +0 -0
  264. package/public/providers/grok-web.png +0 -0
  265. package/public/providers/groq.png +0 -0
  266. package/public/providers/hermes.png +0 -0
  267. package/public/providers/huggingface.png +0 -0
  268. package/public/providers/hyperbolic.png +0 -0
  269. package/public/providers/iflow.png +0 -0
  270. package/public/providers/inworld.png +0 -0
  271. package/public/providers/jcode.png +0 -0
  272. package/public/providers/jina-ai.png +0 -0
  273. package/public/providers/jina-reader.png +0 -0
  274. package/public/providers/kilocode.png +0 -0
  275. package/public/providers/kimi-coding.png +0 -0
  276. package/public/providers/kimi.png +0 -0
  277. package/public/providers/kiro.png +0 -0
  278. package/public/providers/linkup.png +0 -0
  279. package/public/providers/local-device.png +0 -0
  280. package/public/providers/minimax-cn.png +0 -0
  281. package/public/providers/minimax.png +0 -0
  282. package/public/providers/mistral.png +0 -0
  283. package/public/providers/nanobanana.png +0 -0
  284. package/public/providers/nebius.png +0 -0
  285. package/public/providers/nvidia.png +0 -0
  286. package/public/providers/oai-cc.png +0 -0
  287. package/public/providers/oai-r.png +0 -0
  288. package/public/providers/ollama-local.png +0 -0
  289. package/public/providers/ollama.png +0 -0
  290. package/public/providers/openai.png +0 -0
  291. package/public/providers/openclaw.png +0 -0
  292. package/public/providers/opencode-go.png +0 -0
  293. package/public/providers/opencode.png +0 -0
  294. package/public/providers/openrouter.png +0 -0
  295. package/public/providers/perplexity-web.png +0 -0
  296. package/public/providers/perplexity.png +0 -0
  297. package/public/providers/playht.png +0 -0
  298. package/public/providers/qoder.png +0 -0
  299. package/public/providers/qwen.png +0 -0
  300. package/public/providers/recraft.png +0 -0
  301. package/public/providers/roo.png +0 -0
  302. package/public/providers/runwayml.png +0 -0
  303. package/public/providers/sdwebui.png +0 -0
  304. package/public/providers/searchapi.png +0 -0
  305. package/public/providers/searxng.png +0 -0
  306. package/public/providers/serper.png +0 -0
  307. package/public/providers/siliconflow.png +0 -0
  308. package/public/providers/stability-ai.png +0 -0
  309. package/public/providers/tavily.png +0 -0
  310. package/public/providers/together.png +0 -0
  311. package/public/providers/topaz.png +0 -0
  312. package/public/providers/tortoise.png +0 -0
  313. package/public/providers/vertex-partner.png +0 -0
  314. package/public/providers/vertex.png +0 -0
  315. package/public/providers/volcengine-ark.png +0 -0
  316. package/public/providers/voyage-ai.png +0 -0
  317. package/public/providers/xai.png +0 -0
  318. package/public/providers/xiaomi-mimo.png +0 -0
  319. package/public/providers/xiaomi-tokenplan.png +0 -0
  320. package/public/providers/youcom.png +0 -0
  321. package/public/sw.js +22 -0
  322. package/public/vercel.svg +1 -0
  323. package/public/window.svg +1 -0
  324. package/scripts/compact-request-details.mjs +71 -0
  325. package/scripts/import-codex-gptjson.mjs +342 -0
  326. package/scripts/start-standalone.mjs +25 -0
  327. package/scripts/translate-readme.js +201 -0
  328. package/src/app/(dashboard)/dashboard/automation/page.js +294 -0
  329. package/src/app/(dashboard)/dashboard/basic-chat/BasicChatPageClient.js +967 -0
  330. package/src/app/(dashboard)/dashboard/basic-chat/page.js +5 -0
  331. package/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.js +66 -0
  332. package/src/app/(dashboard)/dashboard/cli-tools/[toolId]/ToolDetailClient.js +173 -0
  333. package/src/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js +11 -0
  334. package/src/app/(dashboard)/dashboard/cli-tools/components/AntigravityToolCard.js +481 -0
  335. package/src/app/(dashboard)/dashboard/cli-tools/components/ApiKeySelect.js +66 -0
  336. package/src/app/(dashboard)/dashboard/cli-tools/components/BaseUrlSelect.js +174 -0
  337. package/src/app/(dashboard)/dashboard/cli-tools/components/ClaudeToolCard.js +390 -0
  338. package/src/app/(dashboard)/dashboard/cli-tools/components/ClineToolCard.js +301 -0
  339. package/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js +458 -0
  340. package/src/app/(dashboard)/dashboard/cli-tools/components/CopilotToolCard.js +323 -0
  341. package/src/app/(dashboard)/dashboard/cli-tools/components/CoworkToolCard.js +640 -0
  342. package/src/app/(dashboard)/dashboard/cli-tools/components/DeepSeekTuiToolCard.js +338 -0
  343. package/src/app/(dashboard)/dashboard/cli-tools/components/DefaultToolCard.js +271 -0
  344. package/src/app/(dashboard)/dashboard/cli-tools/components/DroidToolCard.js +410 -0
  345. package/src/app/(dashboard)/dashboard/cli-tools/components/EndpointPresetControl.js +128 -0
  346. package/src/app/(dashboard)/dashboard/cli-tools/components/HermesToolCard.js +317 -0
  347. package/src/app/(dashboard)/dashboard/cli-tools/components/JcodeToolCard.js +380 -0
  348. package/src/app/(dashboard)/dashboard/cli-tools/components/KiloToolCard.js +275 -0
  349. package/src/app/(dashboard)/dashboard/cli-tools/components/MitmLinkCard.js +40 -0
  350. package/src/app/(dashboard)/dashboard/cli-tools/components/MitmServerCard.js +329 -0
  351. package/src/app/(dashboard)/dashboard/cli-tools/components/MitmToolCard.js +318 -0
  352. package/src/app/(dashboard)/dashboard/cli-tools/components/OpenClawToolCard.js +388 -0
  353. package/src/app/(dashboard)/dashboard/cli-tools/components/OpenCodeToolCard.js +500 -0
  354. package/src/app/(dashboard)/dashboard/cli-tools/components/ToolSummaryCard.js +39 -0
  355. package/src/app/(dashboard)/dashboard/cli-tools/components/cliEndpointMatch.js +13 -0
  356. package/src/app/(dashboard)/dashboard/cli-tools/components/index.js +19 -0
  357. package/src/app/(dashboard)/dashboard/cli-tools/page.js +7 -0
  358. package/src/app/(dashboard)/dashboard/combos/page.js +612 -0
  359. package/src/app/(dashboard)/dashboard/console-log/ConsoleLogClient.js +91 -0
  360. package/src/app/(dashboard)/dashboard/console-log/page.js +8 -0
  361. package/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.js +1555 -0
  362. package/src/app/(dashboard)/dashboard/endpoint/page.js +7 -0
  363. package/src/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page.js +1903 -0
  364. package/src/app/(dashboard)/dashboard/media-providers/[kind]/page.js +289 -0
  365. package/src/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js +410 -0
  366. package/src/app/(dashboard)/dashboard/media-providers/web/page.js +209 -0
  367. package/src/app/(dashboard)/dashboard/mitm/MitmPageClient.js +117 -0
  368. package/src/app/(dashboard)/dashboard/mitm/page.js +5 -0
  369. package/src/app/(dashboard)/dashboard/page.js +7 -0
  370. package/src/app/(dashboard)/dashboard/profile/page.js +1140 -0
  371. package/src/app/(dashboard)/dashboard/providers/[id]/AddApiKeyModal.js +389 -0
  372. package/src/app/(dashboard)/dashboard/providers/[id]/AddCustomModelModal.js +125 -0
  373. package/src/app/(dashboard)/dashboard/providers/[id]/CompatibleModelsSection.js +250 -0
  374. package/src/app/(dashboard)/dashboard/providers/[id]/ConnectionRow.js +299 -0
  375. package/src/app/(dashboard)/dashboard/providers/[id]/CooldownTimer.js +42 -0
  376. package/src/app/(dashboard)/dashboard/providers/[id]/EditCompatibleNodeModal.js +161 -0
  377. package/src/app/(dashboard)/dashboard/providers/[id]/ModelRow.js +95 -0
  378. package/src/app/(dashboard)/dashboard/providers/[id]/PassthroughModelsSection.js +183 -0
  379. package/src/app/(dashboard)/dashboard/providers/[id]/page.js +2053 -0
  380. package/src/app/(dashboard)/dashboard/providers/[id]/page.new.js +1724 -0
  381. package/src/app/(dashboard)/dashboard/providers/components/ConnectionsCard.js +497 -0
  382. package/src/app/(dashboard)/dashboard/providers/components/ModelAvailabilityBadge.js +185 -0
  383. package/src/app/(dashboard)/dashboard/providers/components/ModelsCard.js +294 -0
  384. package/src/app/(dashboard)/dashboard/providers/new/page.js +220 -0
  385. package/src/app/(dashboard)/dashboard/providers/page.js +1365 -0
  386. package/src/app/(dashboard)/dashboard/proxy-pools/page.js +1092 -0
  387. package/src/app/(dashboard)/dashboard/quota/page.js +11 -0
  388. package/src/app/(dashboard)/dashboard/skills/page.js +112 -0
  389. package/src/app/(dashboard)/dashboard/translator/page.js +303 -0
  390. package/src/app/(dashboard)/dashboard/usage/components/OverviewCards.js +35 -0
  391. package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/ProviderLimitCard.js +185 -0
  392. package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/QuotaProgressBar.js +127 -0
  393. package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/QuotaTable.js +259 -0
  394. package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.js +1394 -0
  395. package/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.js +244 -0
  396. package/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js +327 -0
  397. package/src/app/(dashboard)/dashboard/usage/components/RequestDetailsTab.js +433 -0
  398. package/src/app/(dashboard)/dashboard/usage/components/UsageChart.js +141 -0
  399. package/src/app/(dashboard)/dashboard/usage/components/UsageTable.js +247 -0
  400. package/src/app/(dashboard)/dashboard/usage/page.js +75 -0
  401. package/src/app/(dashboard)/layout.js +6 -0
  402. package/src/app/api/auth/login/route.js +76 -0
  403. package/src/app/api/auth/logout/route.js +12 -0
  404. package/src/app/api/auth/oidc/callback/route.js +87 -0
  405. package/src/app/api/auth/oidc/start/route.js +52 -0
  406. package/src/app/api/auth/oidc/test/route.js +84 -0
  407. package/src/app/api/auth/status/route.js +45 -0
  408. package/src/app/api/cli-tools/all-statuses/route.js +46 -0
  409. package/src/app/api/cli-tools/antigravity-mitm/alias/route.js +53 -0
  410. package/src/app/api/cli-tools/antigravity-mitm/route.js +202 -0
  411. package/src/app/api/cli-tools/claude-settings/route.js +203 -0
  412. package/src/app/api/cli-tools/cline-settings/route.js +133 -0
  413. package/src/app/api/cli-tools/codex-gateway/accounts/route.js +16 -0
  414. package/src/app/api/cli-tools/codex-settings/route.js +239 -0
  415. package/src/app/api/cli-tools/copilot-settings/route.js +148 -0
  416. package/src/app/api/cli-tools/cowork-mcp-registry/route.js +77 -0
  417. package/src/app/api/cli-tools/cowork-mcp-tools/route.js +95 -0
  418. package/src/app/api/cli-tools/cowork-settings/route.js +412 -0
  419. package/src/app/api/cli-tools/deepseek-tui-settings/route.js +164 -0
  420. package/src/app/api/cli-tools/droid-settings/route.js +213 -0
  421. package/src/app/api/cli-tools/hermes-settings/route.js +175 -0
  422. package/src/app/api/cli-tools/jcode-settings/route.js +216 -0
  423. package/src/app/api/cli-tools/kilo-settings/route.js +131 -0
  424. package/src/app/api/cli-tools/openclaw-settings/route.js +292 -0
  425. package/src/app/api/cli-tools/opencode-settings/route.js +259 -0
  426. package/src/app/api/combos/[id]/route.js +81 -0
  427. package/src/app/api/combos/route.js +48 -0
  428. package/src/app/api/health/route.js +15 -0
  429. package/src/app/api/init/route.js +4 -0
  430. package/src/app/api/keys/[id]/route.js +58 -0
  431. package/src/app/api/keys/route.js +42 -0
  432. package/src/app/api/locale/route.js +30 -0
  433. package/src/app/api/mcp/[plugin]/message/route.js +21 -0
  434. package/src/app/api/mcp/[plugin]/sse/route.js +37 -0
  435. package/src/app/api/media-providers/tts/deepgram/voices/route.js +65 -0
  436. package/src/app/api/media-providers/tts/elevenlabs/voices/route.js +71 -0
  437. package/src/app/api/media-providers/tts/inworld/voices/route.js +61 -0
  438. package/src/app/api/media-providers/tts/minimax/voices/route.js +113 -0
  439. package/src/app/api/media-providers/tts/voices/route.js +99 -0
  440. package/src/app/api/models/alias/route.js +53 -0
  441. package/src/app/api/models/availability/route.js +103 -0
  442. package/src/app/api/models/custom/route.js +48 -0
  443. package/src/app/api/models/disabled/route.js +50 -0
  444. package/src/app/api/models/route.js +64 -0
  445. package/src/app/api/models/test/ping.js +191 -0
  446. package/src/app/api/models/test/route.js +14 -0
  447. package/src/app/api/oauth/[provider]/[action]/route.js +343 -0
  448. package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/cancel/route.js +19 -0
  449. package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/manual/[workerId]/route.js +30 -0
  450. package/src/app/api/oauth/codebuddy/bulk-import/[jobId]/route.js +23 -0
  451. package/src/app/api/oauth/codebuddy/bulk-import/latest/route.js +25 -0
  452. package/src/app/api/oauth/codebuddy/bulk-import/route.js +49 -0
  453. package/src/app/api/oauth/codebuddy/quota-cookie/route.js +133 -0
  454. package/src/app/api/oauth/codex/import-token/route.js +96 -0
  455. package/src/app/api/oauth/cursor/auto-import/route.js +258 -0
  456. package/src/app/api/oauth/cursor/import/route.js +100 -0
  457. package/src/app/api/oauth/gitlab/pat/route.js +62 -0
  458. package/src/app/api/oauth/iflow/cookie/route.js +137 -0
  459. package/src/app/api/oauth/kiro/auto-import/route.js +85 -0
  460. package/src/app/api/oauth/kiro/bulk-import/[jobId]/cancel/route.js +18 -0
  461. package/src/app/api/oauth/kiro/bulk-import/[jobId]/manual/[workerId]/route.js +29 -0
  462. package/src/app/api/oauth/kiro/bulk-import/[jobId]/route.js +22 -0
  463. package/src/app/api/oauth/kiro/bulk-import/latest/route.js +25 -0
  464. package/src/app/api/oauth/kiro/bulk-import/route.js +49 -0
  465. package/src/app/api/oauth/kiro/import/route.js +110 -0
  466. package/src/app/api/oauth/kiro/social-authorize/route.js +27 -0
  467. package/src/app/api/oauth/kiro/social-exchange/route.js +41 -0
  468. package/src/app/api/pricing/route.js +134 -0
  469. package/src/app/api/provider-nodes/[id]/route.js +101 -0
  470. package/src/app/api/provider-nodes/route.js +104 -0
  471. package/src/app/api/provider-nodes/validate/route.js +201 -0
  472. package/src/app/api/providers/[id]/models/route.js +526 -0
  473. package/src/app/api/providers/[id]/route.js +189 -0
  474. package/src/app/api/providers/[id]/test/route.js +23 -0
  475. package/src/app/api/providers/[id]/test/testUtils.js +714 -0
  476. package/src/app/api/providers/[id]/test-models/route.js +66 -0
  477. package/src/app/api/providers/client/route.js +126 -0
  478. package/src/app/api/providers/kilo/free-models/route.js +55 -0
  479. package/src/app/api/providers/route.js +206 -0
  480. package/src/app/api/providers/suggested-models/filters.js +20 -0
  481. package/src/app/api/providers/suggested-models/route.js +32 -0
  482. package/src/app/api/providers/test-batch/route.js +131 -0
  483. package/src/app/api/providers/validate/route.js +637 -0
  484. package/src/app/api/proxy-pools/[id]/route.js +123 -0
  485. package/src/app/api/proxy-pools/[id]/test/route.js +70 -0
  486. package/src/app/api/proxy-pools/cloudflare-deploy/route.js +145 -0
  487. package/src/app/api/proxy-pools/deno-deploy/route.js +175 -0
  488. package/src/app/api/proxy-pools/route.js +93 -0
  489. package/src/app/api/proxy-pools/vercel-deploy/route.js +142 -0
  490. package/src/app/api/settings/database/route.js +36 -0
  491. package/src/app/api/settings/proxy-test/route.js +23 -0
  492. package/src/app/api/settings/require-login/route.js +15 -0
  493. package/src/app/api/settings/route.js +100 -0
  494. package/src/app/api/shutdown/route.js +24 -0
  495. package/src/app/api/tags/route.js +18 -0
  496. package/src/app/api/translator/console-logs/route.js +24 -0
  497. package/src/app/api/translator/console-logs/stream/route.js +79 -0
  498. package/src/app/api/translator/load/route.js +45 -0
  499. package/src/app/api/translator/save/route.js +44 -0
  500. package/src/app/api/translator/send/route.js +94 -0
  501. package/src/app/api/translator/translate/route.js +90 -0
  502. package/src/app/api/tunnel/disable/route.js +12 -0
  503. package/src/app/api/tunnel/enable/route.js +16 -0
  504. package/src/app/api/tunnel/status/route.js +13 -0
  505. package/src/app/api/tunnel/tailscale-check/route.js +50 -0
  506. package/src/app/api/tunnel/tailscale-disable/route.js +12 -0
  507. package/src/app/api/tunnel/tailscale-enable/route.js +12 -0
  508. package/src/app/api/tunnel/tailscale-install/route.js +72 -0
  509. package/src/app/api/usage/[connectionId]/route.js +188 -0
  510. package/src/app/api/usage/chart/route.js +21 -0
  511. package/src/app/api/usage/history/route.js +12 -0
  512. package/src/app/api/usage/logs/route.js +12 -0
  513. package/src/app/api/usage/providers/route.js +42 -0
  514. package/src/app/api/usage/request-details/route.js +57 -0
  515. package/src/app/api/usage/request-logs/route.js +13 -0
  516. package/src/app/api/usage/stats/route.js +23 -0
  517. package/src/app/api/usage/stream/route.js +79 -0
  518. package/src/app/api/v1/api/chat/route.js +37 -0
  519. package/src/app/api/v1/audio/speech/route.js +16 -0
  520. package/src/app/api/v1/audio/transcriptions/route.js +19 -0
  521. package/src/app/api/v1/audio/voices/route.js +68 -0
  522. package/src/app/api/v1/chat/completions/route.js +35 -0
  523. package/src/app/api/v1/embeddings/route.js +21 -0
  524. package/src/app/api/v1/images/generations/route.js +16 -0
  525. package/src/app/api/v1/messages/count_tokens/route.js +52 -0
  526. package/src/app/api/v1/messages/route.js +36 -0
  527. package/src/app/api/v1/models/[kind]/route.js +55 -0
  528. package/src/app/api/v1/models/info/route.js +110 -0
  529. package/src/app/api/v1/models/route.js +451 -0
  530. package/src/app/api/v1/responses/compact/route.js +37 -0
  531. package/src/app/api/v1/responses/route.js +30 -0
  532. package/src/app/api/v1/route.js +1 -0
  533. package/src/app/api/v1/search/route.js +21 -0
  534. package/src/app/api/v1/web/fetch/route.js +21 -0
  535. package/src/app/api/v1beta/models/[...path]/route.js +328 -0
  536. package/src/app/api/v1beta/models/route.js +44 -0
  537. package/src/app/api/version/route.js +45 -0
  538. package/src/app/api/version/shutdown/route.js +15 -0
  539. package/src/app/api/version/update/route.js +21 -0
  540. package/src/app/callback/page.js +148 -0
  541. package/src/app/dashboard/settings/pricing/page.js +173 -0
  542. package/src/app/favicon.ico +0 -0
  543. package/src/app/globals.css +496 -0
  544. package/src/app/landing/components/AnimatedBackground.js +57 -0
  545. package/src/app/landing/components/Features.js +133 -0
  546. package/src/app/landing/components/FlowAnimation.js +175 -0
  547. package/src/app/landing/components/Footer.js +61 -0
  548. package/src/app/landing/components/GetStarted.js +97 -0
  549. package/src/app/landing/components/HeroSection.js +47 -0
  550. package/src/app/landing/components/HowItWorks.js +66 -0
  551. package/src/app/landing/components/Navigation.js +72 -0
  552. package/src/app/landing/page.js +106 -0
  553. package/src/app/layout.js +49 -0
  554. package/src/app/login/page.js +197 -0
  555. package/src/app/manifest.js +30 -0
  556. package/src/app/page.js +5 -0
  557. package/src/dashboardGuard.js +242 -0
  558. package/src/i18n/RuntimeI18nProvider.js +27 -0
  559. package/src/i18n/config.js +146 -0
  560. package/src/i18n/runtime.js +162 -0
  561. package/src/lib/appUpdater.js +200 -0
  562. package/src/lib/auth/dashboardSession.js +68 -0
  563. package/src/lib/auth/loginLimiter.js +52 -0
  564. package/src/lib/auth/oidc.js +234 -0
  565. package/src/lib/consoleLogBuffer.js +79 -0
  566. package/src/lib/dataDir.js +29 -0
  567. package/src/lib/db/adapters/betterSqliteAdapter.js +55 -0
  568. package/src/lib/db/adapters/bunSqliteAdapter.js +63 -0
  569. package/src/lib/db/adapters/nodeSqliteAdapter.js +84 -0
  570. package/src/lib/db/adapters/sqljsAdapter.js +115 -0
  571. package/src/lib/db/backup.js +35 -0
  572. package/src/lib/db/driver.js +85 -0
  573. package/src/lib/db/helpers/jsonCol.js +9 -0
  574. package/src/lib/db/helpers/kvStore.js +39 -0
  575. package/src/lib/db/helpers/metaStore.js +22 -0
  576. package/src/lib/db/index.js +171 -0
  577. package/src/lib/db/migrate.js +286 -0
  578. package/src/lib/db/migrations/001-initial.js +14 -0
  579. package/src/lib/db/migrations/index.js +10 -0
  580. package/src/lib/db/paths.js +18 -0
  581. package/src/lib/db/repos/aliasRepo.js +62 -0
  582. package/src/lib/db/repos/apiKeysRepo.js +75 -0
  583. package/src/lib/db/repos/combosRepo.js +73 -0
  584. package/src/lib/db/repos/connectionsRepo.js +226 -0
  585. package/src/lib/db/repos/disabledModelsRepo.js +56 -0
  586. package/src/lib/db/repos/nodesRepo.js +95 -0
  587. package/src/lib/db/repos/pricingRepo.js +108 -0
  588. package/src/lib/db/repos/proxyPoolsRepo.js +103 -0
  589. package/src/lib/db/repos/requestDetailsRepo.js +259 -0
  590. package/src/lib/db/repos/settingsRepo.js +104 -0
  591. package/src/lib/db/repos/usageRepo.js +731 -0
  592. package/src/lib/db/schema.js +157 -0
  593. package/src/lib/db/version.js +21 -0
  594. package/src/lib/disabledModelsDb.js +4 -0
  595. package/src/lib/localDb.js +21 -0
  596. package/src/lib/mcp/stdioSseBridge.js +198 -0
  597. package/src/lib/mitmAliasCache.js +46 -0
  598. package/src/lib/network/connectionProxy.js +160 -0
  599. package/src/lib/network/initOutboundProxy.js +25 -0
  600. package/src/lib/network/outboundProxy.js +68 -0
  601. package/src/lib/network/proxyTest.js +91 -0
  602. package/src/lib/oauth/constants/oauth.js +284 -0
  603. package/src/lib/oauth/constants/xai.js +61 -0
  604. package/src/lib/oauth/providers.js +1506 -0
  605. package/src/lib/oauth/services/antigravity.js +321 -0
  606. package/src/lib/oauth/services/claude.js +136 -0
  607. package/src/lib/oauth/services/codebuddyBulkImportManager.js +454 -0
  608. package/src/lib/oauth/services/codex.js +144 -0
  609. package/src/lib/oauth/services/cursor.js +179 -0
  610. package/src/lib/oauth/services/gemini.js +240 -0
  611. package/src/lib/oauth/services/github.js +225 -0
  612. package/src/lib/oauth/services/iflow.js +202 -0
  613. package/src/lib/oauth/services/index.js +17 -0
  614. package/src/lib/oauth/services/kiro.js +334 -0
  615. package/src/lib/oauth/services/kiroBulkImportManager.js +778 -0
  616. package/src/lib/oauth/services/kiroConnections.js +93 -0
  617. package/src/lib/oauth/services/kiroGoogleAutomation.js +1136 -0
  618. package/src/lib/oauth/services/oauth.js +157 -0
  619. package/src/lib/oauth/services/openai.js +123 -0
  620. package/src/lib/oauth/services/qoder.js +216 -0
  621. package/src/lib/oauth/services/qwen.js +170 -0
  622. package/src/lib/oauth/services/xai.js +238 -0
  623. package/src/lib/oauth/utils/banner.js +63 -0
  624. package/src/lib/oauth/utils/pkce.js +39 -0
  625. package/src/lib/oauth/utils/server.js +415 -0
  626. package/src/lib/oauth/utils/ui.js +48 -0
  627. package/src/lib/providerNormalization.js +45 -0
  628. package/src/lib/qoder/constants.js +64 -0
  629. package/src/lib/qoder/cosy.js +175 -0
  630. package/src/lib/qoder/encoding.js +55 -0
  631. package/src/lib/requestDetailsDb.js +4 -0
  632. package/src/lib/tunnel/cloudflare/cloudflared.js +449 -0
  633. package/src/lib/tunnel/cloudflare/config.js +9 -0
  634. package/src/lib/tunnel/cloudflare/healthCheck.js +29 -0
  635. package/src/lib/tunnel/cloudflare/manager.js +151 -0
  636. package/src/lib/tunnel/cloudflare/pid.js +23 -0
  637. package/src/lib/tunnel/index.js +48 -0
  638. package/src/lib/tunnel/shared/dnsResolver.js +17 -0
  639. package/src/lib/tunnel/shared/internetCheck.js +26 -0
  640. package/src/lib/tunnel/shared/state.js +41 -0
  641. package/src/lib/tunnel/shared/watchdogConfig.js +8 -0
  642. package/src/lib/tunnel/tailscale/config.js +7 -0
  643. package/src/lib/tunnel/tailscale/healthCheck.js +29 -0
  644. package/src/lib/tunnel/tailscale/manager.js +129 -0
  645. package/src/lib/tunnel/tailscale/tailscale.js +790 -0
  646. package/src/lib/updater/updater.js +235 -0
  647. package/src/lib/usage/fetcher.js +208 -0
  648. package/src/lib/usageDb.js +7 -0
  649. package/src/mitm/antigravityIdeVersion.js +50 -0
  650. package/src/mitm/cert/generate.js +32 -0
  651. package/src/mitm/cert/install.js +269 -0
  652. package/src/mitm/cert/rootCA.js +173 -0
  653. package/src/mitm/config.js +87 -0
  654. package/src/mitm/dbReader.js +22 -0
  655. package/src/mitm/dns/dnsConfig.js +266 -0
  656. package/src/mitm/handlers/antigravity.js +33 -0
  657. package/src/mitm/handlers/base.js +226 -0
  658. package/src/mitm/handlers/copilot.js +35 -0
  659. package/src/mitm/handlers/cursor.js +15 -0
  660. package/src/mitm/handlers/kiro.js +526 -0
  661. package/src/mitm/logger.js +106 -0
  662. package/src/mitm/manager.js +851 -0
  663. package/src/mitm/paths.js +32 -0
  664. package/src/mitm/server.js +435 -0
  665. package/src/mitm/winElevated.js +81 -0
  666. package/src/models/index.js +38 -0
  667. package/src/proxy.js +5 -0
  668. package/src/shared/components/AddCustomEmbeddingModal.js +183 -0
  669. package/src/shared/components/Avatar.js +88 -0
  670. package/src/shared/components/Badge.js +54 -0
  671. package/src/shared/components/BulkAccountAutomationModal.js +508 -0
  672. package/src/shared/components/Button.js +56 -0
  673. package/src/shared/components/Card.js +116 -0
  674. package/src/shared/components/ChangelogModal.js +97 -0
  675. package/src/shared/components/CodeBuddyQuotaCookieModal.js +109 -0
  676. package/src/shared/components/ComboFormModal.js +176 -0
  677. package/src/shared/components/CursorAuthModal.js +212 -0
  678. package/src/shared/components/DonateModal.js +136 -0
  679. package/src/shared/components/Drawer.js +82 -0
  680. package/src/shared/components/EditConnectionModal.js +286 -0
  681. package/src/shared/components/Footer.js +132 -0
  682. package/src/shared/components/GitLabAuthModal.js +194 -0
  683. package/src/shared/components/Header.js +380 -0
  684. package/src/shared/components/HeaderLanguage.js +46 -0
  685. package/src/shared/components/HeaderMenu.js +126 -0
  686. package/src/shared/components/IFlowCookieModal.js +132 -0
  687. package/src/shared/components/Input.js +65 -0
  688. package/src/shared/components/KiroAuthModal.js +1171 -0
  689. package/src/shared/components/KiroOAuthWrapper.js +149 -0
  690. package/src/shared/components/KiroSocialOAuthModal.js +205 -0
  691. package/src/shared/components/LanguageSwitcher.js +190 -0
  692. package/src/shared/components/Loading.js +75 -0
  693. package/src/shared/components/ManualConfigModal.js +44 -0
  694. package/src/shared/components/McpMarketplaceModal.js +255 -0
  695. package/src/shared/components/Modal.js +146 -0
  696. package/src/shared/components/ModelSelectModal.js +537 -0
  697. package/src/shared/components/NineRemoteButton.js +23 -0
  698. package/src/shared/components/NineRemotePromoModal.js +99 -0
  699. package/src/shared/components/NoAuthProxyCard.js +86 -0
  700. package/src/shared/components/OAuthModal.js +682 -0
  701. package/src/shared/components/Pagination.js +150 -0
  702. package/src/shared/components/PricingModal.js +208 -0
  703. package/src/shared/components/ProviderIcon.js +63 -0
  704. package/src/shared/components/ProviderInfoCard.js +82 -0
  705. package/src/shared/components/RequestLogger.js +121 -0
  706. package/src/shared/components/SegmentedControl.js +48 -0
  707. package/src/shared/components/Select.js +67 -0
  708. package/src/shared/components/Sidebar.js +441 -0
  709. package/src/shared/components/ThemeProvider.js +15 -0
  710. package/src/shared/components/ThemeToggle.js +42 -0
  711. package/src/shared/components/Toggle.js +69 -0
  712. package/src/shared/components/Tooltip.js +25 -0
  713. package/src/shared/components/UsageStats.js +505 -0
  714. package/src/shared/components/index.js +46 -0
  715. package/src/shared/components/layouts/AuthLayout.js +29 -0
  716. package/src/shared/components/layouts/DashboardLayout.js +104 -0
  717. package/src/shared/components/layouts/index.js +4 -0
  718. package/src/shared/constants/cliTools.js +397 -0
  719. package/src/shared/constants/colors.js +77 -0
  720. package/src/shared/constants/config.js +99 -0
  721. package/src/shared/constants/coworkPlugins.js +75 -0
  722. package/src/shared/constants/index.js +4 -0
  723. package/src/shared/constants/locales.js +36 -0
  724. package/src/shared/constants/mitmToolHosts.js +12 -0
  725. package/src/shared/constants/models.js +38 -0
  726. package/src/shared/constants/pricing.js +303 -0
  727. package/src/shared/constants/providers.js +289 -0
  728. package/src/shared/constants/skills.js +78 -0
  729. package/src/shared/constants/ttsProviders.js +138 -0
  730. package/src/shared/hooks/index.js +2 -0
  731. package/src/shared/hooks/useCopyToClipboard.js +43 -0
  732. package/src/shared/hooks/useTheme.js +60 -0
  733. package/src/shared/services/bootstrap.js +12 -0
  734. package/src/shared/services/initializeApp.js +268 -0
  735. package/src/shared/utils/api.js +93 -0
  736. package/src/shared/utils/apiKey.js +98 -0
  737. package/src/shared/utils/clineAuth.js +37 -0
  738. package/src/shared/utils/cn.js +11 -0
  739. package/src/shared/utils/connectionStatus.js +162 -0
  740. package/src/shared/utils/index.js +40 -0
  741. package/src/shared/utils/machine.js +6 -0
  742. package/src/shared/utils/machineId.js +66 -0
  743. package/src/shared/utils/providerModelsFetcher.js +30 -0
  744. package/src/sse/handlers/chat.js +261 -0
  745. package/src/sse/handlers/embeddings.js +141 -0
  746. package/src/sse/handlers/fetch.js +213 -0
  747. package/src/sse/handlers/imageGeneration.js +142 -0
  748. package/src/sse/handlers/search.js +206 -0
  749. package/src/sse/handlers/stt.js +88 -0
  750. package/src/sse/handlers/tts.js +114 -0
  751. package/src/sse/services/auth.js +346 -0
  752. package/src/sse/services/codexGateway.js +215 -0
  753. package/src/sse/services/model.js +99 -0
  754. package/src/sse/services/tokenRefresh.js +319 -0
  755. package/src/sse/utils/logger.js +75 -0
  756. package/src/store/headerSearchStore.js +19 -0
  757. package/src/store/index.js +6 -0
  758. package/src/store/notificationStore.js +45 -0
  759. package/src/store/providerStore.js +55 -0
  760. package/src/store/settingsStore.js +51 -0
  761. package/src/store/themeStore.js +54 -0
  762. package/src/store/userStore.js +20 -0
  763. package/start.sh +4 -0
@@ -0,0 +1,851 @@
1
+ const { exec, spawn, execSync } = require("child_process");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const net = require("net");
6
+ const https = require("https");
7
+ const crypto = require("crypto");
8
+ const { addDNSEntry, removeDNSEntry, removeAllDNSEntries, removeAllDNSEntriesSync, checkAllDNSStatus, TOOL_HOSTS, isSudoAvailable, isSudoPasswordRequired } = require("./dns/dnsConfig");
9
+ const { isAdmin } = require("./winElevated.js");
10
+
11
+ const IS_WIN = process.platform === "win32";
12
+ const IS_MAC = process.platform === "darwin";
13
+ const { generateCert } = require("./cert/generate");
14
+ const { installCert, uninstallCert } = require("./cert/install");
15
+ const { isCertExpired } = require("./cert/rootCA");
16
+ const { DATA_DIR, MITM_DIR } = require("./paths");
17
+ const { log, err } = require("./logger");
18
+ const { LSOF_BIN } = require("./config");
19
+
20
+ const DEFAULT_MITM_ROUTER_BASE = "http://localhost:20128";
21
+
22
+ function shellQuoteSingle(str) {
23
+ if (str == null || str === "") return "''";
24
+ return `'${String(str).replace(/'/g, "'\\''")}'`;
25
+ }
26
+
27
+ async function resolveMitmRouterBaseUrl() {
28
+ if (!_getSettings) return DEFAULT_MITM_ROUTER_BASE;
29
+ try {
30
+ const s = await _getSettings();
31
+ const raw = s && s.mitmRouterBaseUrl != null ? String(s.mitmRouterBaseUrl).trim() : "";
32
+ if (!raw) return DEFAULT_MITM_ROUTER_BASE;
33
+ const u = new URL(raw);
34
+ if (u.protocol !== "http:" && u.protocol !== "https:") return DEFAULT_MITM_ROUTER_BASE;
35
+ return raw.replace(/\/+$/, "");
36
+ } catch {
37
+ return DEFAULT_MITM_ROUTER_BASE;
38
+ }
39
+ }
40
+
41
+ const MITM_PORT = 443;
42
+ const MITM_WIN_NODE_PORT = 8443;
43
+ const PID_FILE = path.join(MITM_DIR, ".mitm.pid");
44
+
45
+ const MITM_MAX_RESTARTS = 5;
46
+ const MITM_RESTART_DELAYS_MS = [5000, 10000, 20000, 30000, 60000];
47
+ const MITM_RESTART_RESET_MS = 60000;
48
+
49
+ let mitmRestartCount = 0;
50
+ let mitmLastStartTime = 0;
51
+ let mitmIsRestarting = false;
52
+
53
+ function resolveBundledServerPath() {
54
+ if (process.env.MITM_SERVER_PATH) return process.env.MITM_SERVER_PATH;
55
+ const sibling = path.join(__dirname, "server.js");
56
+ if (fs.existsSync(sibling)) return sibling;
57
+ const fromCwd = path.join(process.cwd(), "src", "mitm", "server.js");
58
+ if (fs.existsSync(fromCwd)) return fromCwd;
59
+ const fromNext = path.join(process.cwd(), "..", "src", "mitm", "server.js");
60
+ if (fs.existsSync(fromNext)) return fromNext;
61
+ return fromCwd;
62
+ }
63
+
64
+ // Copy bundled server.js into DATA_DIR so MITM doesn't lock node_modules
65
+ // (prevents EBUSY on `npm i -g 9router@latest` while MITM is running).
66
+ function ensureRuntimeServer(bundledPath) {
67
+ try {
68
+ if (!bundledPath || !fs.existsSync(bundledPath)) return bundledPath;
69
+
70
+ // Dev mode: source file has relative requires (./logger, ./config...),
71
+ // only the bundled file inside node_modules is self-contained + safe to copy.
72
+ if (!bundledPath.includes(`${path.sep}node_modules${path.sep}`)) {
73
+ return bundledPath;
74
+ }
75
+
76
+ const runtimeDir = path.join(DATA_DIR, "runtime", "mitm");
77
+ const runtimeServer = path.join(runtimeDir, "server.js");
78
+
79
+ // Skip copy if sizes match (bundle unchanged since last run)
80
+ if (fs.existsSync(runtimeServer)) {
81
+ try {
82
+ if (fs.statSync(bundledPath).size === fs.statSync(runtimeServer).size) return runtimeServer;
83
+ } catch { /* recopy */ }
84
+ }
85
+
86
+ fs.mkdirSync(runtimeDir, { recursive: true });
87
+ fs.copyFileSync(bundledPath, runtimeServer);
88
+ return runtimeServer;
89
+ } catch (e) {
90
+ try { log(`[MITM] runtime copy failed: ${e.message}`); } catch { /* ignore */ }
91
+ return bundledPath;
92
+ }
93
+ }
94
+
95
+ const SERVER_PATH = ensureRuntimeServer(resolveBundledServerPath());
96
+ const ENCRYPT_ALGO = "aes-256-gcm";
97
+ const ENCRYPT_SALT = "9router-mitm-pwd";
98
+
99
+ function getProcessUsingPort443() {
100
+ try {
101
+ if (IS_WIN) {
102
+ const psCmd = `powershell -NonInteractive -WindowStyle Hidden -Command ` +
103
+ `"$c = Get-NetTCPConnection -LocalPort 443 -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1; if ($c) { $c.OwningProcess } else { 0 }"`;
104
+ const pidStr = execSync(psCmd, { encoding: "utf8", windowsHide: true }).trim();
105
+ const pid = parseInt(pidStr, 10);
106
+ if (pid && pid > 4) {
107
+ const tasklistResult = execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: "utf8", windowsHide: true });
108
+ const processMatch = tasklistResult.match(/"([^"]+)"/);
109
+ if (processMatch) return processMatch[1].replace(".exe", "");
110
+ }
111
+ } else {
112
+ const result = execSync(`${LSOF_BIN} -i :443`, { encoding: "utf8", windowsHide: true });
113
+ const lines = result.trim().split("\n");
114
+ if (lines.length > 1) return lines[1].split(/\s+/)[0];
115
+ }
116
+ } catch {
117
+ return null;
118
+ }
119
+ return null;
120
+ }
121
+
122
+ let serverProcess = null;
123
+ let serverPid = null;
124
+
125
+ function getCachedPassword() { return globalThis.__mitmSudoPassword || null; }
126
+ function setCachedPassword(pwd) { globalThis.__mitmSudoPassword = pwd; }
127
+
128
+ function isProcessAlive(pid) {
129
+ try {
130
+ process.kill(pid, 0);
131
+ return true;
132
+ } catch (err) {
133
+ return err.code === "EACCES";
134
+ }
135
+ }
136
+
137
+ function killProcess(pid, force = false, sudoPassword = null) {
138
+ if (IS_WIN) {
139
+ const flag = force ? "/F " : "";
140
+ exec(`taskkill ${flag}/PID ${pid}`, { windowsHide: true }, () => { });
141
+ } else {
142
+ const sig = force ? "SIGKILL" : "SIGTERM";
143
+ const cmd = `pkill -${sig} -P ${pid} 2>/dev/null; kill -${sig} ${pid} 2>/dev/null`;
144
+ if (sudoPassword || isSudoAvailable()) {
145
+ const { execWithPassword } = require("./dns/dnsConfig");
146
+ execWithPassword(cmd, sudoPassword || "").catch(() => exec(cmd, { windowsHide: true }, () => { }));
147
+ } else {
148
+ exec(cmd, { windowsHide: true }, () => { });
149
+ }
150
+ }
151
+ }
152
+
153
+ function deriveKey() {
154
+ try {
155
+ const { machineIdSync } = require("node-machine-id");
156
+ const raw = machineIdSync();
157
+ return crypto.createHash("sha256").update(raw + ENCRYPT_SALT).digest();
158
+ } catch {
159
+ return crypto.createHash("sha256").update(ENCRYPT_SALT).digest();
160
+ }
161
+ }
162
+
163
+ function encryptPassword(plaintext) {
164
+ const key = deriveKey();
165
+ const iv = crypto.randomBytes(12);
166
+ const cipher = crypto.createCipheriv(ENCRYPT_ALGO, key, iv);
167
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
168
+ const tag = cipher.getAuthTag();
169
+ return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted.toString("hex")}`;
170
+ }
171
+
172
+ function decryptPassword(stored) {
173
+ try {
174
+ const [ivHex, tagHex, dataHex] = stored.split(":");
175
+ if (!ivHex || !tagHex || !dataHex) return null;
176
+ const key = deriveKey();
177
+ const decipher = crypto.createDecipheriv(ENCRYPT_ALGO, key, Buffer.from(ivHex, "hex"));
178
+ decipher.setAuthTag(Buffer.from(tagHex, "hex"));
179
+ return decipher.update(Buffer.from(dataHex, "hex")) + decipher.final("utf8");
180
+ } catch {
181
+ return null;
182
+ }
183
+ }
184
+
185
+ let _getSettings = null;
186
+ let _updateSettings = null;
187
+
188
+ function initDbHooks(getSettingsFn, updateSettingsFn) {
189
+ _getSettings = getSettingsFn;
190
+ _updateSettings = updateSettingsFn;
191
+ }
192
+
193
+ async function saveMitmSettings(enabled, password) {
194
+ if (!_updateSettings) return;
195
+ try {
196
+ const updates = { mitmEnabled: enabled };
197
+ if (password) updates.mitmSudoEncrypted = encryptPassword(password);
198
+ await _updateSettings(updates);
199
+ } catch (e) {
200
+ err(`Failed to save settings: ${e.message}`);
201
+ }
202
+ }
203
+
204
+ async function clearEncryptedPassword() {
205
+ if (!_updateSettings) return;
206
+ try {
207
+ await _updateSettings({ mitmSudoEncrypted: null });
208
+ } catch (e) {
209
+ err(`Failed to clear encrypted password: ${e.message}`);
210
+ }
211
+ }
212
+
213
+ async function loadEncryptedPassword() {
214
+ if (!_getSettings) return null;
215
+ try {
216
+ const settings = await _getSettings();
217
+ if (!settings.mitmSudoEncrypted) return null;
218
+ return decryptPassword(settings.mitmSudoEncrypted);
219
+ } catch {
220
+ return null;
221
+ }
222
+ }
223
+
224
+ async function saveDnsToolState(tool, enabled) {
225
+ if (!_updateSettings || !_getSettings) return;
226
+ try {
227
+ const s = await _getSettings();
228
+ const next = { ...(s.dnsToolEnabled || {}), [tool]: enabled };
229
+ await _updateSettings({ dnsToolEnabled: next });
230
+ } catch (e) {
231
+ err(`Failed to save DNS state: ${e.message}`);
232
+ }
233
+ }
234
+
235
+ async function loadDnsToolState() {
236
+ if (!_getSettings) return {};
237
+ try {
238
+ const s = await _getSettings();
239
+ return s.dnsToolEnabled || {};
240
+ } catch {
241
+ return {};
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Re-apply DNS for tools previously enabled — called on app startup after MITM running.
247
+ */
248
+ async function restoreToolDNS(sudoPassword) {
249
+ const state = await loadDnsToolState();
250
+ const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
251
+ for (const [tool, enabled] of Object.entries(state)) {
252
+ if (!enabled || !TOOL_HOSTS[tool]) continue;
253
+ try {
254
+ await addDNSEntry(tool, password);
255
+ } catch (e) {
256
+ err(`DNS ${tool}: restore failed — ${e.message}`);
257
+ }
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Check if user has privilege to mutate hosts file.
263
+ * Win: needs admin. Mac/Linux: root OR cached/encrypted sudo password.
264
+ */
265
+ async function hasDnsPrivilege() {
266
+ if (IS_WIN) return isAdmin();
267
+ if (isAdmin()) return true;
268
+ if (!isSudoPasswordRequired()) return true;
269
+ const pwd = getCachedPassword() || await loadEncryptedPassword();
270
+ return !!pwd;
271
+ }
272
+
273
+ function checkPort443Free() {
274
+ return new Promise((resolve) => {
275
+ const tester = net.createServer();
276
+ tester.once("error", (err) => {
277
+ if (err.code === "EADDRINUSE") resolve("in-use");
278
+ else resolve("no-permission");
279
+ });
280
+ tester.once("listening", () => { tester.close(() => resolve("free")); });
281
+ tester.listen(MITM_PORT, "127.0.0.1");
282
+ });
283
+ }
284
+
285
+ function getPort443Owner(sudoPassword) {
286
+ return new Promise((resolve) => {
287
+ if (IS_WIN) {
288
+ const psCmd = `powershell -NonInteractive -WindowStyle Hidden -Command "` +
289
+ `$c = Get-NetTCPConnection -LocalPort 443 -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1; ` +
290
+ `if ($c) { $c.OwningProcess } else { 0 }"`;
291
+ exec(psCmd, { windowsHide: true }, (err, stdout) => {
292
+ if (err) return resolve(null);
293
+ const pid = parseInt(stdout.trim(), 10);
294
+ if (!pid || pid <= 4) return resolve(null);
295
+ exec(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { windowsHide: true }, (e2, out2) => {
296
+ const m = out2?.match(/"([^"]+)"/);
297
+ resolve({ pid, name: m ? m[1] : "unknown" });
298
+ });
299
+ });
300
+ } else {
301
+ // Only find process actually LISTENING on TCP port 443
302
+ exec(`${LSOF_BIN} -nP -iTCP:443 -sTCP:LISTEN -t`, { windowsHide: true }, (err, stdout) => {
303
+ if (err || !stdout?.trim()) return resolve(null);
304
+ const pid = parseInt(stdout.trim().split("\n")[0], 10);
305
+ if (!pid || isNaN(pid)) return resolve(null);
306
+ exec(`ps -p ${pid} -o comm=`, { windowsHide: true }, (e2, out2) => {
307
+ resolve({ pid, name: (out2?.trim() || "unknown") });
308
+ });
309
+ });
310
+ }
311
+ });
312
+ }
313
+
314
+ async function killLeftoverMitm(sudoPassword) {
315
+ if (serverProcess && !serverProcess.killed) {
316
+ try { serverProcess.kill("SIGKILL"); } catch { /* ignore */ }
317
+ serverProcess = null;
318
+ serverPid = null;
319
+ }
320
+ try {
321
+ if (fs.existsSync(PID_FILE)) {
322
+ const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
323
+ if (savedPid && isProcessAlive(savedPid)) {
324
+ killProcess(savedPid, true, sudoPassword);
325
+ await new Promise(r => setTimeout(r, 500));
326
+ }
327
+ fs.unlinkSync(PID_FILE);
328
+ }
329
+ } catch { /* ignore */ }
330
+ if (!IS_WIN && SERVER_PATH) {
331
+ try {
332
+ const escaped = SERVER_PATH.replace(/'/g, "'\\''");
333
+ if (sudoPassword || isSudoAvailable()) {
334
+ const { execWithPassword } = require("./dns/dnsConfig");
335
+ await execWithPassword(`pkill -SIGKILL -f "${escaped}" 2>/dev/null || true`, sudoPassword || "").catch(() => { });
336
+ } else {
337
+ exec(`pkill -SIGKILL -f "${escaped}" 2>/dev/null || true`, { windowsHide: true }, () => { });
338
+ }
339
+ await new Promise(r => setTimeout(r, 500));
340
+ } catch { /* ignore */ }
341
+ }
342
+ }
343
+
344
+ function pollMitmHealth(timeoutMs, port = MITM_PORT) {
345
+ return new Promise((resolve) => {
346
+ const deadline = Date.now() + timeoutMs;
347
+ const check = () => {
348
+ const req = https.request(
349
+ { hostname: "127.0.0.1", port, path: "/_mitm_health", method: "GET", rejectUnauthorized: false },
350
+ (res) => {
351
+ let body = "";
352
+ res.on("data", (d) => { body += d; });
353
+ res.on("end", () => {
354
+ try {
355
+ const json = JSON.parse(body);
356
+ resolve(json.ok === true ? { ok: true, pid: json.pid || null } : null);
357
+ } catch { resolve(null); }
358
+ });
359
+ }
360
+ );
361
+ req.on("error", () => {
362
+ if (Date.now() < deadline) setTimeout(check, 500);
363
+ else resolve(null);
364
+ });
365
+ req.end();
366
+ };
367
+ check();
368
+ });
369
+ }
370
+
371
+ /**
372
+ * Get full MITM status including per-tool DNS status
373
+ */
374
+ async function getMitmStatus() {
375
+ let running = serverProcess !== null && !serverProcess.killed;
376
+ let pid = serverPid;
377
+
378
+ if (!running) {
379
+ try {
380
+ if (fs.existsSync(PID_FILE)) {
381
+ const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
382
+ if (savedPid && isProcessAlive(savedPid)) {
383
+ running = true;
384
+ pid = savedPid;
385
+ } else {
386
+ fs.unlinkSync(PID_FILE);
387
+ }
388
+ }
389
+ } catch { /* ignore */ }
390
+ }
391
+
392
+ const dnsStatus = checkAllDNSStatus();
393
+ const rootCACertPath = path.join(MITM_DIR, "rootCA.crt");
394
+ const certExists = fs.existsSync(rootCACertPath);
395
+ const { checkCertInstalled } = require("./cert/install");
396
+ const certTrusted = certExists ? await checkCertInstalled(rootCACertPath) : false;
397
+
398
+ return { running, pid, certExists, certTrusted, dnsStatus };
399
+ }
400
+
401
+ async function scheduleMitmRestart(apiKey) {
402
+ if (mitmIsRestarting) return;
403
+
404
+ const aliveMs = Date.now() - mitmLastStartTime;
405
+ if (aliveMs >= MITM_RESTART_RESET_MS) mitmRestartCount = 0;
406
+
407
+ if (mitmRestartCount >= MITM_MAX_RESTARTS) {
408
+ err("Max restart attempts reached. Giving up.");
409
+ return;
410
+ }
411
+
412
+ const attempt = mitmRestartCount;
413
+ const delay = MITM_RESTART_DELAYS_MS[Math.min(attempt, MITM_RESTART_DELAYS_MS.length - 1)];
414
+ mitmRestartCount++;
415
+ mitmIsRestarting = true;
416
+
417
+ log(`Restarting in ${delay / 1000}s... (${mitmRestartCount}/${MITM_MAX_RESTARTS})`);
418
+ await new Promise((r) => setTimeout(r, delay));
419
+
420
+ try {
421
+ const settings = _getSettings ? await _getSettings() : null;
422
+ if (settings && !settings.mitmEnabled) {
423
+ log("MITM disabled, skipping restart");
424
+ mitmIsRestarting = false;
425
+ return;
426
+ }
427
+ const password = getCachedPassword() || await loadEncryptedPassword();
428
+ if (!password && !IS_WIN) {
429
+ err("No cached password, cannot auto-restart");
430
+ mitmIsRestarting = false;
431
+ return;
432
+ }
433
+ await startServer(apiKey, password);
434
+ log("🔄 Restarted successfully");
435
+ mitmRestartCount = 0;
436
+ mitmIsRestarting = false;
437
+ } catch (e) {
438
+ err(`Restart attempt ${mitmRestartCount}/${MITM_MAX_RESTARTS} failed: ${e.message}`);
439
+ mitmIsRestarting = false;
440
+ // Schedule next retry
441
+ scheduleMitmRestart(apiKey);
442
+ }
443
+ }
444
+
445
+ /**
446
+ * Start MITM server only (cert + server, no DNS)
447
+ */
448
+ async function killPort443Owner(owner, sudoPassword) {
449
+ if (!owner || !owner.pid) return;
450
+ if (IS_WIN) {
451
+ try {
452
+ execSync(`powershell -NonInteractive -WindowStyle Hidden -Command "Stop-Process -Id ${owner.pid} -Force -ErrorAction SilentlyContinue"`, { windowsHide: true });
453
+ } catch { /* best effort */ }
454
+ } else {
455
+ try {
456
+ const { execWithPassword } = require("./dns/dnsConfig");
457
+ if (sudoPassword || isSudoAvailable()) {
458
+ await execWithPassword(`kill -9 ${owner.pid}`, sudoPassword || "");
459
+ } else {
460
+ execSync(`kill -9 ${owner.pid}`, { windowsHide: true });
461
+ }
462
+ } catch { /* best effort */ }
463
+ }
464
+ await new Promise(r => setTimeout(r, 800));
465
+ }
466
+
467
+ async function startServer(apiKey, sudoPassword, forceKillPort443 = false) {
468
+ if (!serverProcess || serverProcess.killed) {
469
+ try {
470
+ if (fs.existsSync(PID_FILE)) {
471
+ const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
472
+ if (savedPid && isProcessAlive(savedPid)) {
473
+ serverPid = savedPid;
474
+ log(`♻️ Reusing existing process (PID: ${savedPid})`);
475
+ await saveMitmSettings(true, sudoPassword);
476
+ if (sudoPassword) setCachedPassword(sudoPassword);
477
+ return { running: true, pid: savedPid };
478
+ } else {
479
+ fs.unlinkSync(PID_FILE);
480
+ }
481
+ }
482
+ } catch { /* ignore */ }
483
+ }
484
+
485
+ if (serverProcess && !serverProcess.killed) {
486
+ throw new Error("MITM server is already running");
487
+ }
488
+
489
+ await killLeftoverMitm(sudoPassword);
490
+
491
+ if (!IS_WIN) {
492
+ const portStatus = await checkPort443Free();
493
+ if (portStatus === "in-use" || portStatus === "no-permission") {
494
+ const owner = await getPort443Owner(sudoPassword);
495
+ if (owner) {
496
+ const shortName = owner.name.includes("/")
497
+ ? owner.name.split("/").filter(Boolean).pop()
498
+ : owner.name;
499
+ if (forceKillPort443) {
500
+ log(`Killing process on port 443 (PID ${owner.pid}, name=${shortName})...`);
501
+ await killPort443Owner(owner, sudoPassword);
502
+ } else {
503
+ const e = new Error(`Port 443 is already in use by "${shortName}" (PID ${owner.pid}).`);
504
+ e.code = "PORT_443_BUSY";
505
+ e.portOwner = { pid: owner.pid, name: shortName };
506
+ throw e;
507
+ }
508
+ }
509
+ }
510
+ }
511
+
512
+ // Step 1: Generate Root CA if missing or expired
513
+ const rootCACertPath = path.join(MITM_DIR, "rootCA.crt");
514
+ const rootCAKeyPath = path.join(MITM_DIR, "rootCA.key");
515
+ const certExists = fs.existsSync(rootCACertPath) && fs.existsSync(rootCAKeyPath);
516
+
517
+ if (!certExists || isCertExpired(rootCACertPath)) {
518
+ if (certExists) {
519
+ // Uninstall expired cert from system store before regenerating
520
+ log("🔐 Cert expired — uninstalling old cert...");
521
+ const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
522
+ try { await uninstallCert(password, rootCACertPath); } catch { /* best effort */ }
523
+ }
524
+ log("🔐 Generating Root CA...");
525
+ await generateCert();
526
+ }
527
+
528
+ // Step 1.5: Auto-install Root CA if not trusted yet
529
+ const { checkCertInstalled } = require("./cert/install");
530
+ const rootCATrusted = await checkCertInstalled(rootCACertPath);
531
+ const linuxNoSystemTrust = !IS_WIN && !IS_MAC && !isSudoAvailable();
532
+ if (!rootCATrusted) {
533
+ log("🔐 Cert: not trusted → installing...");
534
+ const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
535
+ if (linuxNoSystemTrust) {
536
+ log(`🔐 Cert: skipping system trust (no sudo). Install ${rootCACertPath} as a trusted CA on machines that use this proxy.`);
537
+ } else {
538
+ if (!password && isSudoPasswordRequired()) {
539
+ throw new Error("Sudo password required to install Root CA certificate");
540
+ }
541
+ try {
542
+ await installCert(password, rootCACertPath);
543
+ log("🔐 Cert: ✅ trusted");
544
+ } catch (e) {
545
+ throw new Error(`Failed to trust certificate: ${e.message}`);
546
+ }
547
+ }
548
+ } else {
549
+ log("🔐 Cert: already trusted ✅");
550
+ }
551
+
552
+ // Step 2: Spawn server (Root CA already installed in Step 1.5)
553
+ // Verify server.js exists — recopy if runtime file was deleted (antivirus/cleanup)
554
+ let effectiveServerPath = SERVER_PATH;
555
+ if (!effectiveServerPath || !fs.existsSync(effectiveServerPath)) {
556
+ log(`[MITM] server.js missing at ${effectiveServerPath} → recopying`);
557
+ effectiveServerPath = ensureRuntimeServer(resolveBundledServerPath());
558
+ if (!effectiveServerPath || !fs.existsSync(effectiveServerPath)) {
559
+ throw new Error(`MITM server.js not found at ${effectiveServerPath}. Reinstall 9router.`);
560
+ }
561
+ }
562
+ const mitmRouterBase = await resolveMitmRouterBaseUrl();
563
+ log(`🚀 Starting server... (router: ${mitmRouterBase})`);
564
+ if (IS_WIN) {
565
+ // Check port 443 — ask user before killing
566
+ const winOwner = await getPort443Owner(sudoPassword);
567
+ if (winOwner) {
568
+ if (forceKillPort443) {
569
+ log(`Killing process on port 443 (PID ${winOwner.pid}, name=${winOwner.name})...`);
570
+ await killPort443Owner(winOwner, sudoPassword);
571
+ } else {
572
+ const e = new Error(`Port 443 is already in use by "${winOwner.name}" (PID ${winOwner.pid}).`);
573
+ e.code = "PORT_443_BUSY";
574
+ e.portOwner = { pid: winOwner.pid, name: winOwner.name };
575
+ throw e;
576
+ }
577
+ }
578
+
579
+ // Spawn directly — process already has admin rights
580
+ // cwd=tmpdir so process doesn't lock the install dir on Windows (EBUSY on update)
581
+ serverProcess = spawn(
582
+ process.execPath,
583
+ [effectiveServerPath],
584
+ {
585
+ detached: false,
586
+ windowsHide: true,
587
+ cwd: os.tmpdir(),
588
+ stdio: ["ignore", "pipe", "pipe"],
589
+ env: {
590
+ ...process.env,
591
+ ROUTER_API_KEY: apiKey,
592
+ NODE_ENV: "production",
593
+ MITM_ROUTER_BASE: mitmRouterBase,
594
+ },
595
+ }
596
+ );
597
+
598
+ if (_updateSettings) await _updateSettings({ mitmCertInstalled: true }).catch(() => { });
599
+ } else if (isSudoAvailable()) {
600
+ // Pass HOME explicitly so os.homedir() resolves to the unprivileged user's home
601
+ // instead of /root when sudo resets the environment.
602
+ const inlineCmd = [
603
+ `HOME=${shellQuoteSingle(os.homedir())}`,
604
+ `ROUTER_API_KEY=${shellQuoteSingle(apiKey)}`,
605
+ `MITM_ROUTER_BASE=${shellQuoteSingle(mitmRouterBase)}`,
606
+ "NODE_ENV=production",
607
+ shellQuoteSingle(process.execPath),
608
+ shellQuoteSingle(effectiveServerPath),
609
+ ].join(" ");
610
+ serverProcess = spawn(
611
+ "sudo", ["-S", "-E", "sh", "-c", inlineCmd],
612
+ { detached: false, windowsHide: true, stdio: ["pipe", "pipe", "pipe"] }
613
+ );
614
+ serverProcess.stdin.write(`${sudoPassword}\n`);
615
+ serverProcess.stdin.end();
616
+ } else {
617
+ // Docker/minimal images: no sudo — same as Windows-style direct spawn
618
+ serverProcess = spawn(process.execPath, [effectiveServerPath], {
619
+ detached: false,
620
+ windowsHide: true,
621
+ cwd: os.tmpdir(),
622
+ stdio: ["ignore", "pipe", "pipe"],
623
+ env: {
624
+ ...process.env,
625
+ ROUTER_API_KEY: apiKey,
626
+ NODE_ENV: "production",
627
+ MITM_ROUTER_BASE: mitmRouterBase,
628
+ },
629
+ });
630
+ }
631
+
632
+ if (serverProcess) {
633
+ serverPid = serverProcess.pid;
634
+ fs.writeFileSync(PID_FILE, String(serverPid));
635
+ mitmLastStartTime = Date.now();
636
+ }
637
+
638
+ // Set NODE_EXTRA_CA_CERTS so Node-based GUI apps (Electron/AG language_server) trust MITM cert
639
+ if (IS_MAC) {
640
+ const rootCAPath = path.join(MITM_DIR, "rootCA.crt");
641
+ if (fs.existsSync(rootCAPath)) {
642
+ exec(`launchctl setenv NODE_EXTRA_CA_CERTS "${rootCAPath}"`, { windowsHide: true }, (e) => {
643
+ if (e) log(`[launchctl] Failed to set NODE_EXTRA_CA_CERTS: ${e.message}`);
644
+ else log(`[launchctl] NODE_EXTRA_CA_CERTS set to ${rootCAPath}`);
645
+ });
646
+ }
647
+ } else if (IS_WIN) {
648
+ const rootCAPath = path.join(MITM_DIR, "rootCA.crt");
649
+ if (fs.existsSync(rootCAPath)) {
650
+ exec(`setx NODE_EXTRA_CA_CERTS "${rootCAPath}"`, { windowsHide: true }, (e) => {
651
+ if (e) log(`[setx] Failed to set NODE_EXTRA_CA_CERTS: ${e.message}`);
652
+ else log(`[setx] NODE_EXTRA_CA_CERTS set for current user`);
653
+ });
654
+ }
655
+ }
656
+
657
+ let startError = null;
658
+ if (serverProcess) {
659
+ serverProcess.stdout.on("data", (data) => {
660
+ // server.js already formats its own logs — print as-is
661
+ process.stdout.write(data);
662
+ });
663
+ serverProcess.stderr.on("data", (data) => {
664
+ const msg = data.toString().trim();
665
+ // Mac/Linux: filter sudo password prompt noise
666
+ if (msg && (IS_WIN || (!msg.includes("Password:") && !msg.includes("password for")))) {
667
+ err(msg);
668
+ startError = msg;
669
+ }
670
+ // Detect wrong/missing password — clear cache and stop retry loop
671
+ if (!IS_WIN && (msg.includes("incorrect password") || msg.includes("no password was provided"))) {
672
+ setCachedPassword(null);
673
+ clearEncryptedPassword();
674
+ mitmIsRestarting = true; // prevent scheduleMitmRestart from firing
675
+ }
676
+ });
677
+ serverProcess.on("exit", (code) => {
678
+ log(`Server exited (code: ${code})`);
679
+ serverProcess = null;
680
+ serverPid = null;
681
+ try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
682
+ // Auto-restart on unexpected exit
683
+ if (code !== 0 && !mitmIsRestarting) scheduleMitmRestart(apiKey);
684
+ });
685
+ }
686
+
687
+ const health = await pollMitmHealth(8000, MITM_PORT);
688
+ if (!health) {
689
+ if (serverProcess && !serverProcess.killed) { try { serverProcess.kill(); } catch { /* ignore */ } serverProcess = null; }
690
+ const processUsing443 = getProcessUsingPort443();
691
+ const portInfo = processUsing443 ? ` Port 443 already in use by ${processUsing443}.` : "";
692
+ const reason = startError || `Check sudo password or port 443 access.${portInfo}`;
693
+ throw new Error(`MITM server failed to start. ${reason}`);
694
+ }
695
+
696
+ if (_updateSettings) await _updateSettings({ mitmCertInstalled: true }).catch(() => { });
697
+
698
+ log(`✅ Server healthy (PID: ${serverPid || health.pid})`);
699
+
700
+ // Log DNS status per tool
701
+ const dnsStatus = checkAllDNSStatus();
702
+ for (const [tool, active] of Object.entries(dnsStatus)) {
703
+ log(`🌐 DNS ${tool}: ${active ? "✅ active" : "❌ inactive"}`);
704
+ }
705
+
706
+ await saveMitmSettings(true, sudoPassword);
707
+ if (sudoPassword) setCachedPassword(sudoPassword);
708
+
709
+ return { running: true, pid: serverPid };
710
+ }
711
+
712
+ /**
713
+ * Stop MITM server — removes ALL tool DNS entries first, then kills server
714
+ */
715
+ async function stopServer(sudoPassword) {
716
+ // Prevent auto-restart from triggering on intentional stop
717
+ mitmIsRestarting = true;
718
+ mitmRestartCount = 0;
719
+ log("⏹ Stopping server...");
720
+
721
+ // Kill server process
722
+ const proc = serverProcess;
723
+ const pidToKill = proc && !proc.killed
724
+ ? proc.pid
725
+ : (() => { try { return parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10); } catch { return null; } })();
726
+
727
+ if (pidToKill && isProcessAlive(pidToKill)) {
728
+ log(`Killing server (PID: ${pidToKill})...`);
729
+ killProcess(pidToKill, false, sudoPassword);
730
+ await new Promise(r => setTimeout(r, 1000));
731
+ if (isProcessAlive(pidToKill)) killProcess(pidToKill, true, sudoPassword);
732
+ }
733
+ serverProcess = null;
734
+ serverPid = null;
735
+
736
+ if (IS_WIN) {
737
+ const hostsFile = path.join(process.env.SystemRoot || "C:\\Windows", "System32", "drivers", "etc", "hosts");
738
+ const allHosts = Object.values(TOOL_HOSTS).flat();
739
+ try {
740
+ const { isAdmin, runElevatedPowerShell, quotePs } = require("./winElevated.js");
741
+ if (isAdmin()) {
742
+ // Direct fs write — bypass PowerShell to avoid parser pitfalls
743
+ const content = fs.readFileSync(hostsFile, "utf8");
744
+ const filtered = content.split(/\r?\n/).filter(l => !allHosts.some(h => l.includes(h))).join("\r\n");
745
+ const next = filtered.replace(/[\r\n\s]+$/g, "") + "\r\n";
746
+ if (next !== content) fs.writeFileSync(hostsFile, next, "utf8");
747
+ try { require("child_process").execSync("ipconfig /flushdns", { windowsHide: true, stdio: "ignore" }); } catch { /* ignore */ }
748
+ log("🌐 DNS: ✅ all tool hosts removed");
749
+ } else {
750
+ const hostsList = allHosts.map(quotePs).join(",");
751
+ const script = `
752
+ $hosts = @(${hostsList})
753
+ $lines = Get-Content -LiteralPath ${quotePs(hostsFile)}
754
+ $filtered = $lines | Where-Object {
755
+ $line = $_
756
+ -not ($hosts | Where-Object { $line -match [regex]::Escape($_) })
757
+ }
758
+ Set-Content -LiteralPath ${quotePs(hostsFile)} -Value $filtered
759
+ ipconfig /flushdns | Out-Null
760
+ `;
761
+ await runElevatedPowerShell(script);
762
+ }
763
+ } catch (e) { err(`Failed to clean hosts: ${e.message}`); }
764
+ } else {
765
+ await removeAllDNSEntries(sudoPassword);
766
+ }
767
+
768
+ // Unset NODE_EXTRA_CA_CERTS so apps don't keep trusting stale MITM cert
769
+ if (IS_MAC) {
770
+ exec(`launchctl unsetenv NODE_EXTRA_CA_CERTS`, { windowsHide: true }, (e) => {
771
+ if (e) log(`[launchctl] Failed to unset NODE_EXTRA_CA_CERTS: ${e.message}`);
772
+ else log(`[launchctl] NODE_EXTRA_CA_CERTS unset`);
773
+ });
774
+ } else if (IS_WIN) {
775
+ exec(`reg delete HKCU\\Environment /F /V NODE_EXTRA_CA_CERTS`, { windowsHide: true }, (e) => {
776
+ if (e) log(`[reg] Failed to unset NODE_EXTRA_CA_CERTS: ${e.message}`);
777
+ else log(`[reg] NODE_EXTRA_CA_CERTS unset`);
778
+ });
779
+ }
780
+
781
+ try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
782
+ await saveMitmSettings(false, null);
783
+ mitmIsRestarting = false;
784
+
785
+ return { running: false, pid: null };
786
+ }
787
+
788
+ /**
789
+ * Enable DNS for a specific tool (requires server running)
790
+ */
791
+ async function enableToolDNS(tool, sudoPassword) {
792
+ const status = await getMitmStatus();
793
+ if (!status.running) throw new Error("MITM server is not running. Start the server first.");
794
+
795
+ const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
796
+ await addDNSEntry(tool, password);
797
+ await saveDnsToolState(tool, true);
798
+ return { success: true };
799
+ }
800
+
801
+ /**
802
+ * Disable DNS for a specific tool
803
+ */
804
+ async function disableToolDNS(tool, sudoPassword) {
805
+ const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
806
+ await removeDNSEntry(tool, password);
807
+ await saveDnsToolState(tool, false);
808
+ return { success: true };
809
+ }
810
+
811
+ /**
812
+ * Install Root CA to system trust store (standalone, no server start)
813
+ */
814
+ async function trustCert(sudoPassword) {
815
+ const rootCACertPath = path.join(MITM_DIR, "rootCA.crt");
816
+ if (!fs.existsSync(rootCACertPath)) throw new Error("Root CA not found. Start server first to generate it.");
817
+ const { installCert } = require("./cert/install");
818
+ if (!IS_WIN && !IS_MAC && !isSudoAvailable()) {
819
+ log(`🔐 Cert: system trust unavailable (no sudo). Use file: ${rootCACertPath}`);
820
+ return;
821
+ }
822
+ const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
823
+ if (!password && isSudoPasswordRequired()) throw new Error("Sudo password required to trust certificate");
824
+ await installCert(password, rootCACertPath);
825
+ if (password) setCachedPassword(password);
826
+ }
827
+
828
+ // Legacy aliases for backward compatibility
829
+ const startMitm = startServer;
830
+ const stopMitm = stopServer;
831
+
832
+ module.exports = {
833
+ getMitmStatus,
834
+ startServer,
835
+ stopServer,
836
+ enableToolDNS,
837
+ disableToolDNS,
838
+ trustCert,
839
+ // Legacy
840
+ startMitm,
841
+ stopMitm,
842
+ getCachedPassword,
843
+ setCachedPassword,
844
+ loadEncryptedPassword,
845
+ clearEncryptedPassword,
846
+ isSudoPasswordRequired,
847
+ initDbHooks,
848
+ restoreToolDNS,
849
+ hasDnsPrivilege,
850
+ removeAllDNSEntriesSync,
851
+ };