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,1171 @@
1
+ "use client";
2
+
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
+ import Image from "next/image";
5
+ import PropTypes from "prop-types";
6
+ import { Modal, Button, Input } from "@/shared/components";
7
+
8
+ const DEFAULT_CONCURRENCY = 4;
9
+ const BULK_JOB_STORAGE_KEY = "kiro-bulk-import-active-job";
10
+ const JOB_SESSION_EXPIRED_MESSAGE = "Bulk import progress could not be restored. The previous job session was cleared.";
11
+
12
+ function isJobTerminal(status) {
13
+ return ["completed", "cancelled", "failed"].includes(status);
14
+ }
15
+
16
+ function isJobActive(status) {
17
+ return ["queued", "running", "needs_manual"].includes(status);
18
+ }
19
+
20
+ function formatStepLabel(step) {
21
+ if (!step) return "waiting";
22
+ return step.replaceAll("_", " ");
23
+ }
24
+
25
+ function formatClock(value) {
26
+ if (!value) return "--:--:--";
27
+
28
+ try {
29
+ return new Date(value).toLocaleTimeString("id-ID", {
30
+ hour: "2-digit",
31
+ minute: "2-digit",
32
+ second: "2-digit",
33
+ });
34
+ } catch {
35
+ return value;
36
+ }
37
+ }
38
+
39
+ function groupAccountsByStatus(accounts = []) {
40
+ const order = ["running", "queued", "needs_manual", "success", "failed", "failed_invalid_credentials", "failed_exchange", "failed_timeout", "cancelled"];
41
+ const grouped = new Map();
42
+
43
+ accounts.forEach((account) => {
44
+ const key = account.status || "queued";
45
+ if (!grouped.has(key)) grouped.set(key, []);
46
+ grouped.get(key).push(account);
47
+ });
48
+
49
+ return order
50
+ .filter((status) => grouped.has(status))
51
+ .map((status) => ({ status, accounts: grouped.get(status) }));
52
+ }
53
+
54
+ function getStatusPanelClasses(status) {
55
+ const styles = {
56
+ queued: "border-slate-200 bg-slate-50/80 dark:border-slate-800 dark:bg-slate-900/20",
57
+ running: "border-blue-200 bg-blue-50/80 dark:border-blue-900/50 dark:bg-blue-950/20",
58
+ success: "border-green-200 bg-green-50/80 dark:border-green-900/50 dark:bg-green-950/20",
59
+ failed: "border-red-200 bg-red-50/80 dark:border-red-900/50 dark:bg-red-950/20",
60
+ failed_invalid_credentials: "border-red-200 bg-red-50/80 dark:border-red-900/50 dark:bg-red-950/20",
61
+ failed_exchange: "border-red-200 bg-red-50/80 dark:border-red-900/50 dark:bg-red-950/20",
62
+ failed_timeout: "border-red-200 bg-red-50/80 dark:border-red-900/50 dark:bg-red-950/20",
63
+ needs_manual: "border-amber-200 bg-amber-50/80 dark:border-amber-900/50 dark:bg-amber-950/20",
64
+ cancelled: "border-slate-200 bg-slate-50/80 dark:border-slate-800 dark:bg-slate-900/20",
65
+ };
66
+
67
+ return styles[status] || styles.queued;
68
+ }
69
+
70
+ async function fetchBulkJobById(jobId) {
71
+ if (!jobId) return null;
72
+ const res = await fetch(`/api/oauth/kiro/bulk-import/${jobId}`, { cache: "no-store" });
73
+ const data = await res.json();
74
+ return { res, data };
75
+ }
76
+
77
+ async function fetchLatestBulkJob(scope = "active") {
78
+ const res = await fetch(`/api/oauth/kiro/bulk-import/latest?scope=${encodeURIComponent(scope)}`, { cache: "no-store" });
79
+ const data = await res.json();
80
+ return { res, data };
81
+ }
82
+
83
+ function AccountStatusBadge({ status }) {
84
+ const palette = {
85
+ queued: "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-200",
86
+ running: "bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300",
87
+ success: "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300",
88
+ failed: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300",
89
+ failed_invalid_credentials: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300",
90
+ failed_exchange: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300",
91
+ failed_timeout: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300",
92
+ needs_manual: "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300",
93
+ cancelled: "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-200",
94
+ };
95
+
96
+ return (
97
+ <span className={`rounded-full px-2 py-1 text-[11px] font-medium ${palette[status] || palette.queued}`}>
98
+ {status.replaceAll("_", " ")}
99
+ </span>
100
+ );
101
+ }
102
+
103
+ AccountStatusBadge.propTypes = {
104
+ status: PropTypes.string.isRequired,
105
+ };
106
+
107
+ export default function KiroAuthModal({
108
+ isOpen,
109
+ onMethodSelect,
110
+ onImportSuccess,
111
+ onClose,
112
+ initialJobId,
113
+ initialSelectedMethod,
114
+ initialImportMode,
115
+ initialFlowKey,
116
+ onBulkJobChange,
117
+ }) {
118
+ const [selectedMethod, setSelectedMethod] = useState(null);
119
+ const [idcStartUrl, setIdcStartUrl] = useState("");
120
+ const [idcRegion, setIdcRegion] = useState("us-east-1");
121
+ const [refreshToken, setRefreshToken] = useState("");
122
+ const [importMode, setImportMode] = useState("single-token");
123
+ const [bulkText, setBulkText] = useState("");
124
+ const [concurrency, setConcurrency] = useState(String(DEFAULT_CONCURRENCY));
125
+ const [bulkResult, setBulkResult] = useState(null);
126
+ const [activeJob, setActiveJob] = useState(null);
127
+ const [error, setError] = useState(null);
128
+ const [jobRestoreNotice, setJobRestoreNotice] = useState(null);
129
+ const [importing, setImporting] = useState(false);
130
+ const [autoDetecting, setAutoDetecting] = useState(false);
131
+ const [autoDetected, setAutoDetected] = useState(false);
132
+ const completedRefreshJobsRef = useRef(new Set());
133
+ const previousSuccessCountRef = useRef(0);
134
+ const resumeJobId = isOpen
135
+ ? (initialJobId || (typeof window !== "undefined"
136
+ ? window.localStorage.getItem(BULK_JOB_STORAGE_KEY)
137
+ : null))
138
+ : null;
139
+ const effectiveSelectedMethod = selectedMethod || (resumeJobId ? "import" : null);
140
+ const effectiveImportMode = effectiveSelectedMethod === "import" && resumeJobId && !selectedMethod
141
+ ? "bulk-account"
142
+ : importMode;
143
+
144
+ useEffect(() => {
145
+ if (!isOpen) return;
146
+ if (!initialSelectedMethod) return;
147
+ setSelectedMethod(initialSelectedMethod);
148
+ setImportMode(initialImportMode || "single-token");
149
+ setBulkResult(null);
150
+ setError(null);
151
+ }, [initialFlowKey, initialImportMode, initialSelectedMethod, isOpen]);
152
+
153
+ useEffect(() => {
154
+ if (effectiveSelectedMethod !== "import" || effectiveImportMode !== "single-token" || !isOpen) return;
155
+
156
+ const autoDetect = async () => {
157
+ setAutoDetecting(true);
158
+ setError(null);
159
+ setAutoDetected(false);
160
+
161
+ try {
162
+ const res = await fetch("/api/oauth/kiro/auto-import");
163
+ const data = await res.json();
164
+
165
+ if (data.found) {
166
+ setRefreshToken(data.refreshToken);
167
+ setAutoDetected(true);
168
+ } else {
169
+ setError(data.error || "Could not auto-detect token");
170
+ }
171
+ } catch {
172
+ setError("Failed to auto-detect token");
173
+ } finally {
174
+ setAutoDetecting(false);
175
+ }
176
+ };
177
+
178
+ autoDetect();
179
+ }, [effectiveSelectedMethod, effectiveImportMode, isOpen]);
180
+
181
+ useEffect(() => {
182
+ if (!isOpen || effectiveSelectedMethod !== "import" || effectiveImportMode !== "bulk-account") return undefined;
183
+
184
+ const restoreJob = async () => {
185
+ if (activeJob?.jobId) return;
186
+
187
+ try {
188
+ setJobRestoreNotice(null);
189
+
190
+ const preferredJobId = initialJobId || null;
191
+ if (preferredJobId) {
192
+ const preferred = await fetchBulkJobById(preferredJobId);
193
+ if (preferred.res.ok && preferred.data?.job) {
194
+ setActiveJob(preferred.data.job);
195
+ return;
196
+ }
197
+ }
198
+
199
+ const latest = await fetchLatestBulkJob();
200
+ if (latest.res.ok && latest.data?.job) {
201
+ setActiveJob(latest.data.job);
202
+ if (typeof window !== "undefined") {
203
+ window.localStorage.setItem(BULK_JOB_STORAGE_KEY, latest.data.job.jobId);
204
+ }
205
+ return;
206
+ }
207
+
208
+ if (resumeJobId) {
209
+ const stored = await fetchBulkJobById(resumeJobId);
210
+ if (stored.res.ok && stored.data?.job && isJobActive(stored.data.job.status)) {
211
+ setActiveJob(stored.data.job);
212
+ return;
213
+ }
214
+
215
+ if (typeof window !== "undefined") {
216
+ window.localStorage.removeItem(BULK_JOB_STORAGE_KEY);
217
+ }
218
+ onBulkJobChange?.(null);
219
+ setJobRestoreNotice(JOB_SESSION_EXPIRED_MESSAGE);
220
+ }
221
+ } catch {
222
+ setJobRestoreNotice(JOB_SESSION_EXPIRED_MESSAGE);
223
+ }
224
+ };
225
+
226
+ void restoreJob();
227
+ }, [activeJob?.jobId, effectiveImportMode, effectiveSelectedMethod, initialJobId, isOpen, onBulkJobChange, resumeJobId]);
228
+
229
+ useEffect(() => {
230
+ if (!activeJob?.jobId) {
231
+ previousSuccessCountRef.current = 0;
232
+ return;
233
+ }
234
+
235
+ const currentSuccessCount = activeJob.summary?.success || 0;
236
+ if (currentSuccessCount > previousSuccessCountRef.current) {
237
+ onImportSuccess?.();
238
+ }
239
+ previousSuccessCountRef.current = currentSuccessCount;
240
+ }, [activeJob, onImportSuccess]);
241
+
242
+ useEffect(() => {
243
+ if (!activeJob?.jobId) {
244
+ return;
245
+ }
246
+
247
+ if (typeof window !== "undefined") {
248
+ window.localStorage.setItem(BULK_JOB_STORAGE_KEY, activeJob.jobId);
249
+ }
250
+ onBulkJobChange?.(activeJob);
251
+ }, [activeJob, onBulkJobChange]);
252
+
253
+ useEffect(() => {
254
+ if (!isOpen || !activeJob?.jobId || isJobTerminal(activeJob.status)) return undefined;
255
+
256
+ const interval = window.setInterval(async () => {
257
+ try {
258
+ const current = await fetchBulkJobById(activeJob.jobId);
259
+ if (current.res.ok && current.data?.job) {
260
+ setJobRestoreNotice(null);
261
+ setActiveJob(current.data.job);
262
+ return;
263
+ }
264
+
265
+ if (current.res.status === 404) {
266
+ const latest = await fetchLatestBulkJob();
267
+ if (latest.res.ok && latest.data?.job) {
268
+ setJobRestoreNotice(null);
269
+ setActiveJob(latest.data.job);
270
+ if (typeof window !== "undefined") {
271
+ window.localStorage.setItem(BULK_JOB_STORAGE_KEY, latest.data.job.jobId);
272
+ }
273
+ return;
274
+ }
275
+
276
+ if (typeof window !== "undefined") {
277
+ window.localStorage.removeItem(BULK_JOB_STORAGE_KEY);
278
+ }
279
+ onBulkJobChange?.(null);
280
+ setActiveJob(null);
281
+ setJobRestoreNotice("Bulk import progress expired or was cleared.");
282
+ }
283
+ } catch {
284
+ // Keep last-known UI state and let user retry/cancel manually.
285
+ }
286
+ }, 1500);
287
+
288
+ return () => window.clearInterval(interval);
289
+ }, [activeJob?.jobId, activeJob?.status, isOpen, onBulkJobChange]);
290
+
291
+ useEffect(() => {
292
+ if (!isOpen || effectiveImportMode !== "bulk-account" || !activeJob?.jobId || !isJobTerminal(activeJob.status)) {
293
+ return;
294
+ }
295
+
296
+ if ((activeJob.summary?.success || 0) <= 0) return;
297
+ if (completedRefreshJobsRef.current.has(activeJob.jobId)) return;
298
+
299
+ completedRefreshJobsRef.current.add(activeJob.jobId);
300
+ onImportSuccess?.();
301
+ }, [activeJob, effectiveImportMode, isOpen, onImportSuccess]);
302
+
303
+ const runningBulkAccountJob = effectiveImportMode === "bulk-account" && activeJob && !isJobTerminal(activeJob.status);
304
+ const finishedBulkAccountJob = effectiveImportMode === "bulk-account" && activeJob && isJobTerminal(activeJob.status);
305
+
306
+ const summaryItems = useMemo(() => {
307
+ if (!activeJob?.summary) return [];
308
+ return [
309
+ ["Total", activeJob.summary.total],
310
+ ["Queued", activeJob.summary.queued],
311
+ ["Running", activeJob.summary.running],
312
+ ["Success", activeJob.summary.success],
313
+ ["Failed", activeJob.summary.failed],
314
+ ["Manual", activeJob.summary.needs_manual],
315
+ ];
316
+ }, [activeJob]);
317
+
318
+ const activityItems = useMemo(
319
+ () => (Array.isArray(activeJob?.activity) ? [...activeJob.activity].reverse() : []),
320
+ [activeJob]
321
+ );
322
+ const groupedAccounts = useMemo(
323
+ () => groupAccountsByStatus(activeJob?.accounts || []),
324
+ [activeJob]
325
+ );
326
+
327
+ const spotlightAccount = useMemo(() => {
328
+ if (!activeJob?.accounts?.length) return null;
329
+ return activeJob.accounts.find((account) => account.status === "running")
330
+ || activeJob.accounts.find((account) => account.status === "needs_manual")
331
+ || activeJob.accounts.find((account) => account.status === "success")
332
+ || activeJob.accounts[0];
333
+ }, [activeJob]);
334
+
335
+ const handleMethodSelect = (method) => {
336
+ setSelectedMethod(method);
337
+ setError(null);
338
+ };
339
+
340
+ const resetImportState = () => {
341
+ setRefreshToken("");
342
+ setBulkText("");
343
+ setBulkResult(null);
344
+ setConcurrency(String(DEFAULT_CONCURRENCY));
345
+ setError(null);
346
+ setJobRestoreNotice(null);
347
+ setAutoDetected(false);
348
+ setActiveJob(null);
349
+ previousSuccessCountRef.current = 0;
350
+ if (typeof window !== "undefined") {
351
+ window.localStorage.removeItem(BULK_JOB_STORAGE_KEY);
352
+ }
353
+ onBulkJobChange?.(null);
354
+ };
355
+
356
+ const handleBack = () => {
357
+ if (runningBulkAccountJob) {
358
+ onClose?.();
359
+ return;
360
+ }
361
+ setSelectedMethod(null);
362
+ setImportMode("single-token");
363
+ resetImportState();
364
+ };
365
+
366
+ const handleImportToken = async () => {
367
+ if (!refreshToken.trim()) {
368
+ setError("Please enter a refresh token");
369
+ return;
370
+ }
371
+
372
+ setImporting(true);
373
+ setError(null);
374
+
375
+ try {
376
+ const res = await fetch("/api/oauth/kiro/import", {
377
+ method: "POST",
378
+ headers: { "Content-Type": "application/json" },
379
+ body: JSON.stringify({
380
+ mode: "token",
381
+ refreshToken: refreshToken.trim(),
382
+ }),
383
+ });
384
+
385
+ const data = await res.json();
386
+ if (!res.ok) {
387
+ throw new Error(data.error || "Import failed");
388
+ }
389
+
390
+ onImportSuccess?.();
391
+ } catch (err) {
392
+ setError(err.message);
393
+ } finally {
394
+ setImporting(false);
395
+ }
396
+ };
397
+
398
+ const handleBulkTokenImport = async (lines) => {
399
+ const res = await fetch("/api/oauth/kiro/import", {
400
+ method: "POST",
401
+ headers: { "Content-Type": "application/json" },
402
+ body: JSON.stringify({
403
+ mode: "token",
404
+ refreshTokens: lines,
405
+ }),
406
+ });
407
+
408
+ const data = await res.json();
409
+ if (!res.ok) {
410
+ throw new Error(data.error || "Bulk import failed");
411
+ }
412
+
413
+ setBulkResult({
414
+ success: data.imported || 0,
415
+ failed: data.failed || 0,
416
+ });
417
+
418
+ onImportSuccess?.();
419
+ };
420
+
421
+ const handleBulkAccountImport = async (lines) => {
422
+ const res = await fetch("/api/oauth/kiro/bulk-import", {
423
+ method: "POST",
424
+ headers: { "Content-Type": "application/json" },
425
+ body: JSON.stringify({
426
+ accounts: lines,
427
+ concurrency: Number.parseInt(concurrency, 10) || DEFAULT_CONCURRENCY,
428
+ }),
429
+ });
430
+
431
+ const data = await res.json();
432
+ if (!res.ok) {
433
+ const invalidHint = Array.isArray(data.invalidLines) && data.invalidLines.length > 0
434
+ ? ` Invalid lines: ${data.invalidLines.join(", ")}`
435
+ : "";
436
+ throw new Error((data.error || "Bulk account import failed") + invalidHint);
437
+ }
438
+
439
+ setActiveJob(data.job || null);
440
+ setJobRestoreNotice(null);
441
+ if (data.job?.jobId) {
442
+ completedRefreshJobsRef.current.delete(data.job.jobId);
443
+ }
444
+ setBulkResult(null);
445
+ };
446
+
447
+ const handleBulkImport = async () => {
448
+ const lines = bulkText
449
+ .split("\n")
450
+ .map((line) => line.trim())
451
+ .filter(Boolean);
452
+
453
+ if (!lines.length) {
454
+ setError(
455
+ effectiveImportMode === "bulk-account"
456
+ ? "Please enter at least one gmail|password line"
457
+ : "Please enter at least one refresh token line"
458
+ );
459
+ return;
460
+ }
461
+
462
+ setImporting(true);
463
+ setError(null);
464
+ setBulkResult(null);
465
+
466
+ try {
467
+ if (effectiveImportMode === "bulk-account") {
468
+ await handleBulkAccountImport(lines);
469
+ } else {
470
+ await handleBulkTokenImport(lines);
471
+ }
472
+ } catch (err) {
473
+ setError(err.message);
474
+ } finally {
475
+ setImporting(false);
476
+ }
477
+ };
478
+
479
+ const handleCancelJob = async () => {
480
+ if (!activeJob?.jobId) return;
481
+
482
+ try {
483
+ const res = await fetch(`/api/oauth/kiro/bulk-import/${activeJob.jobId}/cancel`, {
484
+ method: "POST",
485
+ });
486
+ const data = await res.json();
487
+ if (!res.ok) {
488
+ throw new Error(data.error || "Failed to cancel job");
489
+ }
490
+ if (data.job) setActiveJob(data.job);
491
+ } catch (err) {
492
+ setError(err.message);
493
+ }
494
+ };
495
+
496
+ const handleDoneRefresh = () => {
497
+ resetImportState();
498
+ onImportSuccess?.();
499
+ };
500
+
501
+ const handleOpenManualSession = async (workerId) => {
502
+ if (!activeJob?.jobId || !workerId) return;
503
+
504
+ try {
505
+ const res = await fetch(`/api/oauth/kiro/bulk-import/${activeJob.jobId}/manual/${workerId}`, {
506
+ method: "POST",
507
+ });
508
+ const data = await res.json();
509
+ if (!res.ok) {
510
+ throw new Error(data.error || "Failed to open manual session");
511
+ }
512
+ if (data.job) setActiveJob(data.job);
513
+ } catch (err) {
514
+ setError(err.message);
515
+ }
516
+ };
517
+
518
+ const handleIdcContinue = () => {
519
+ if (!idcStartUrl.trim()) {
520
+ setError("Please enter your IDC start URL");
521
+ return;
522
+ }
523
+
524
+ onMethodSelect("idc", { startUrl: idcStartUrl.trim(), region: idcRegion });
525
+ };
526
+
527
+ const handleSocialLogin = (provider) => {
528
+ onMethodSelect("social", { provider });
529
+ };
530
+
531
+ return (
532
+ <Modal
533
+ isOpen={isOpen}
534
+ title="Connect Kiro"
535
+ onClose={onClose}
536
+ size="full"
537
+ className="max-w-[min(96vw,1540px)]"
538
+ >
539
+ <div className="flex flex-col gap-4">
540
+ {!effectiveSelectedMethod && (
541
+ <div className="space-y-3">
542
+ <p className="mb-4 text-sm text-text-muted">
543
+ Choose your authentication method:
544
+ </p>
545
+
546
+ <button
547
+ onClick={() => onMethodSelect("builder-id")}
548
+ className="w-full rounded-lg border border-border p-4 text-left transition-colors hover:bg-sidebar"
549
+ >
550
+ <div className="flex items-start gap-3">
551
+ <span className="material-symbols-outlined mt-0.5 text-primary">shield</span>
552
+ <div className="flex-1">
553
+ <h3 className="mb-1 font-semibold">AWS Builder ID</h3>
554
+ <p className="text-sm text-text-muted">
555
+ Recommended for most users. Free AWS account required.
556
+ </p>
557
+ </div>
558
+ </div>
559
+ </button>
560
+
561
+ <button
562
+ onClick={() => handleMethodSelect("idc")}
563
+ className="w-full rounded-lg border border-border p-4 text-left transition-colors hover:bg-sidebar"
564
+ >
565
+ <div className="flex items-start gap-3">
566
+ <span className="material-symbols-outlined mt-0.5 text-primary">business</span>
567
+ <div className="flex-1">
568
+ <h3 className="mb-1 font-semibold">AWS IAM Identity Center</h3>
569
+ <p className="text-sm text-text-muted">
570
+ For enterprise users with custom AWS IAM Identity Center.
571
+ </p>
572
+ </div>
573
+ </div>
574
+ </button>
575
+
576
+ <button
577
+ onClick={() => handleMethodSelect("social-google")}
578
+ className="hidden w-full rounded-lg border border-border p-4 text-left transition-colors hover:bg-sidebar"
579
+ >
580
+ <div className="flex items-start gap-3">
581
+ <span className="material-symbols-outlined mt-0.5 text-primary">account_circle</span>
582
+ <div className="flex-1">
583
+ <h3 className="mb-1 font-semibold">Google Account</h3>
584
+ <p className="text-sm text-text-muted">
585
+ Login with your Google account (manual callback).
586
+ </p>
587
+ </div>
588
+ </div>
589
+ </button>
590
+
591
+ <button
592
+ onClick={() => handleMethodSelect("social-github")}
593
+ className="hidden w-full rounded-lg border border-border p-4 text-left transition-colors hover:bg-sidebar"
594
+ >
595
+ <div className="flex items-start gap-3">
596
+ <span className="material-symbols-outlined mt-0.5 text-primary">code</span>
597
+ <div className="flex-1">
598
+ <h3 className="mb-1 font-semibold">GitHub Account</h3>
599
+ <p className="text-sm text-text-muted">
600
+ Login with your GitHub account (manual callback).
601
+ </p>
602
+ </div>
603
+ </div>
604
+ </button>
605
+
606
+ <button
607
+ onClick={() => handleMethodSelect("import")}
608
+ className="w-full rounded-lg border border-border p-4 text-left transition-colors hover:bg-sidebar"
609
+ >
610
+ <div className="flex items-start gap-3">
611
+ <span className="material-symbols-outlined mt-0.5 text-primary">file_upload</span>
612
+ <div className="flex-1">
613
+ <h3 className="mb-1 font-semibold">Bulk Option</h3>
614
+ <p className="text-sm text-text-muted">
615
+ Import a single token, bulk tokens, or bulk Google accounts.
616
+ </p>
617
+ </div>
618
+ </div>
619
+ </button>
620
+ </div>
621
+ )}
622
+
623
+ {effectiveSelectedMethod === "idc" && (
624
+ <div className="space-y-4">
625
+ <div>
626
+ <label className="mb-2 block text-sm font-medium">
627
+ IDC Start URL <span className="text-red-500">*</span>
628
+ </label>
629
+ <Input
630
+ value={idcStartUrl}
631
+ onChange={(e) => setIdcStartUrl(e.target.value)}
632
+ placeholder="https://your-org.awsapps.com/start"
633
+ className="font-mono text-sm"
634
+ />
635
+ <p className="mt-1 text-xs text-text-muted">
636
+ Your organization&apos;s AWS IAM Identity Center URL
637
+ </p>
638
+ </div>
639
+
640
+ <div>
641
+ <label className="mb-2 block text-sm font-medium">
642
+ AWS Region
643
+ </label>
644
+ <Input
645
+ value={idcRegion}
646
+ onChange={(e) => setIdcRegion(e.target.value)}
647
+ placeholder="us-east-1"
648
+ className="font-mono text-sm"
649
+ />
650
+ <p className="mt-1 text-xs text-text-muted">
651
+ AWS region for your Identity Center (default: us-east-1)
652
+ </p>
653
+ </div>
654
+
655
+ {error && <p className="text-sm text-red-600">{error}</p>}
656
+
657
+ <div className="flex gap-2">
658
+ <Button onClick={handleIdcContinue} fullWidth>
659
+ Continue
660
+ </Button>
661
+ <Button onClick={handleBack} variant="ghost" fullWidth>
662
+ Back
663
+ </Button>
664
+ </div>
665
+ </div>
666
+ )}
667
+
668
+ {effectiveSelectedMethod === "social-google" && (
669
+ <div className="space-y-4">
670
+ <div className="rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-800 dark:bg-amber-900/20">
671
+ <div className="flex gap-2">
672
+ <span className="material-symbols-outlined text-amber-600 dark:text-amber-400">info</span>
673
+ <div className="flex-1 text-sm">
674
+ <p className="mb-1 font-medium text-amber-900 dark:text-amber-100">
675
+ Manual Callback Required
676
+ </p>
677
+ <p className="text-amber-800 dark:text-amber-200">
678
+ After login, you&apos;ll need to copy the callback URL from your browser and paste it back here.
679
+ </p>
680
+ </div>
681
+ </div>
682
+ </div>
683
+
684
+ <div className="flex gap-2">
685
+ <Button onClick={() => handleSocialLogin("google")} fullWidth>
686
+ Continue with Google
687
+ </Button>
688
+ <Button onClick={handleBack} variant="ghost" fullWidth>
689
+ Back
690
+ </Button>
691
+ </div>
692
+ </div>
693
+ )}
694
+
695
+ {effectiveSelectedMethod === "social-github" && (
696
+ <div className="space-y-4">
697
+ <div className="rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-800 dark:bg-amber-900/20">
698
+ <div className="flex gap-2">
699
+ <span className="material-symbols-outlined text-amber-600 dark:text-amber-400">info</span>
700
+ <div className="flex-1 text-sm">
701
+ <p className="mb-1 font-medium text-amber-900 dark:text-amber-100">
702
+ Manual Callback Required
703
+ </p>
704
+ <p className="text-amber-800 dark:text-amber-200">
705
+ After login, you&apos;ll need to copy the callback URL from your browser and paste it back here.
706
+ </p>
707
+ </div>
708
+ </div>
709
+ </div>
710
+
711
+ <div className="flex gap-2">
712
+ <Button onClick={() => handleSocialLogin("github")} fullWidth>
713
+ Continue with GitHub
714
+ </Button>
715
+ <Button onClick={handleBack} variant="ghost" fullWidth>
716
+ Back
717
+ </Button>
718
+ </div>
719
+ </div>
720
+ )}
721
+
722
+ {effectiveSelectedMethod === "import" && (
723
+ <div className="space-y-4">
724
+ <div className="flex flex-wrap gap-2">
725
+ <Button
726
+ size="sm"
727
+ variant={effectiveImportMode === "single-token" ? "primary" : "ghost"}
728
+ onClick={() => {
729
+ setImportMode("single-token");
730
+ setBulkResult(null);
731
+ setError(null);
732
+ }}
733
+ disabled={runningBulkAccountJob}
734
+ >
735
+ Single Token
736
+ </Button>
737
+ <Button
738
+ size="sm"
739
+ variant={effectiveImportMode === "bulk-token" ? "primary" : "ghost"}
740
+ onClick={() => {
741
+ setImportMode("bulk-token");
742
+ setBulkResult(null);
743
+ setError(null);
744
+ }}
745
+ disabled={runningBulkAccountJob}
746
+ >
747
+ Bulk Token
748
+ </Button>
749
+ <Button
750
+ size="sm"
751
+ variant={effectiveImportMode === "bulk-account" ? "primary" : "ghost"}
752
+ onClick={() => {
753
+ setImportMode("bulk-account");
754
+ setBulkResult(null);
755
+ setError(null);
756
+ }}
757
+ >
758
+ Bulk Account
759
+ </Button>
760
+ </div>
761
+
762
+ {effectiveImportMode === "single-token" && (
763
+ <>
764
+ {autoDetecting && (
765
+ <div className="py-6 text-center">
766
+ <div className="mx-auto mb-4 flex size-16 items-center justify-center rounded-full bg-primary/10">
767
+ <span className="material-symbols-outlined animate-spin text-3xl text-primary">
768
+ progress_activity
769
+ </span>
770
+ </div>
771
+ <h3 className="mb-2 text-lg font-semibold">Auto-detecting token...</h3>
772
+ <p className="text-sm text-text-muted">Reading from AWS SSO cache</p>
773
+ </div>
774
+ )}
775
+
776
+ {!autoDetecting && (
777
+ <>
778
+ {autoDetected && (
779
+ <div className="rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-800 dark:bg-green-900/20">
780
+ <div className="flex gap-2">
781
+ <span className="material-symbols-outlined text-green-600 dark:text-green-400">check_circle</span>
782
+ <p className="text-sm text-green-800 dark:text-green-200">
783
+ Token auto-detected from Kiro IDE successfully!
784
+ </p>
785
+ </div>
786
+ </div>
787
+ )}
788
+
789
+ {!autoDetected && !error && (
790
+ <div className="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-800 dark:bg-blue-900/20">
791
+ <div className="flex gap-2">
792
+ <span className="material-symbols-outlined text-blue-600 dark:text-blue-400">info</span>
793
+ <p className="text-sm text-blue-800 dark:text-blue-200">
794
+ Kiro IDE not detected. Please paste your refresh token manually.
795
+ </p>
796
+ </div>
797
+ </div>
798
+ )}
799
+
800
+ <div>
801
+ <label className="mb-2 block text-sm font-medium">
802
+ Refresh Token <span className="text-red-500">*</span>
803
+ </label>
804
+ <Input
805
+ value={refreshToken}
806
+ onChange={(e) => setRefreshToken(e.target.value)}
807
+ placeholder="Token will be auto-filled..."
808
+ className="font-mono text-sm"
809
+ />
810
+ </div>
811
+
812
+ {error && (
813
+ <div className="rounded-lg border border-red-200 bg-red-50 p-3 dark:border-red-800 dark:bg-red-900/20">
814
+ <p className="text-sm text-red-600 dark:text-red-400">{error}</p>
815
+ </div>
816
+ )}
817
+
818
+ <div className="flex gap-2">
819
+ <Button onClick={handleImportToken} fullWidth disabled={importing || !refreshToken.trim()}>
820
+ {importing ? "Importing..." : "Import Token"}
821
+ </Button>
822
+ <Button onClick={handleBack} variant="ghost" fullWidth>
823
+ Back
824
+ </Button>
825
+ </div>
826
+ </>
827
+ )}
828
+ </>
829
+ )}
830
+
831
+ {effectiveImportMode === "bulk-token" && (
832
+ <>
833
+ <div className="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-800 dark:bg-blue-900/20">
834
+ <div className="flex gap-2">
835
+ <span className="material-symbols-outlined text-blue-600 dark:text-blue-400">info</span>
836
+ <p className="text-sm text-blue-800 dark:text-blue-200">
837
+ Bulk token import accepts one refresh token per line.
838
+ </p>
839
+ </div>
840
+ </div>
841
+
842
+ <div>
843
+ <label className="mb-2 block text-sm font-medium">
844
+ Bulk Refresh Tokens <span className="text-red-500">*</span>
845
+ </label>
846
+ <textarea
847
+ value={bulkText}
848
+ onChange={(e) => setBulkText(e.target.value)}
849
+ placeholder={"aorAAAAAG...\naorAAAAAG..."}
850
+ className="min-h-[160px] w-full resize-y rounded-lg border border-border bg-background px-3 py-2 font-mono text-sm focus:outline-none focus:ring-1 focus:ring-primary"
851
+ />
852
+ <p className="mt-1 text-xs text-text-muted">
853
+ One refresh token per line.
854
+ </p>
855
+ </div>
856
+
857
+ {bulkResult && (
858
+ <div className={`text-sm font-medium ${bulkResult.failed > 0 ? "text-yellow-600 dark:text-yellow-400" : "text-green-600 dark:text-green-400"}`}>
859
+ {`Success: ${bulkResult.success} imported${bulkResult.failed > 0 ? `, ${bulkResult.failed} failed` : ""}`}
860
+ </div>
861
+ )}
862
+
863
+ {error && (
864
+ <div className="rounded-lg border border-red-200 bg-red-50 p-3 dark:border-red-800 dark:bg-red-900/20">
865
+ <p className="text-sm text-red-600 dark:text-red-400">{error}</p>
866
+ </div>
867
+ )}
868
+
869
+ {jobRestoreNotice && (
870
+ <div className="rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-800 dark:bg-amber-900/20">
871
+ <p className="text-sm text-amber-700 dark:text-amber-300">{jobRestoreNotice}</p>
872
+ </div>
873
+ )}
874
+
875
+ <div className="flex gap-2">
876
+ <Button onClick={handleBulkImport} fullWidth disabled={importing || !bulkText.trim()}>
877
+ {importing ? "Importing..." : "Import Tokens"}
878
+ </Button>
879
+ <Button onClick={handleBack} variant="ghost" fullWidth>
880
+ Back
881
+ </Button>
882
+ </div>
883
+ </>
884
+ )}
885
+
886
+ {effectiveImportMode === "bulk-account" && (
887
+ <>
888
+ <div className="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-800 dark:bg-blue-900/20">
889
+ <div className="flex gap-2">
890
+ <span className="material-symbols-outlined text-blue-600 dark:text-blue-400">info</span>
891
+ <p className="text-sm text-blue-800 dark:text-blue-200">
892
+ Bulk account import runs Playwright in the background, keeps passwords in memory only, and only needs manual action when Google or Kiro blocks a worker. Recommended worker count for a 24 GB machine: 4.
893
+ </p>
894
+ </div>
895
+ </div>
896
+
897
+ {!activeJob && (
898
+ <>
899
+ <div>
900
+ <label className="mb-2 block text-sm font-medium">
901
+ Bulk Accounts <span className="text-red-500">*</span>
902
+ </label>
903
+ <textarea
904
+ value={bulkText}
905
+ onChange={(e) => setBulkText(e.target.value)}
906
+ placeholder={"gmail1@example.com|password1\ngmail2@example.com|password2"}
907
+ className="min-h-[180px] w-full resize-y rounded-lg border border-border bg-background px-3 py-2 font-mono text-sm focus:outline-none focus:ring-1 focus:ring-primary"
908
+ />
909
+ <p className="mt-1 text-xs text-text-muted">
910
+ One account per line in the format gmail|password.
911
+ </p>
912
+ </div>
913
+
914
+ <div>
915
+ <label className="mb-2 block text-sm font-medium">
916
+ Concurrent Workers
917
+ </label>
918
+ <Input
919
+ type="number"
920
+ min="1"
921
+ max="8"
922
+ value={concurrency}
923
+ onChange={(e) => setConcurrency(e.target.value)}
924
+ placeholder="4"
925
+ />
926
+ <p className="mt-1 text-xs text-text-muted">
927
+ Default 4. Allowed range: 1 to 8 workers.
928
+ </p>
929
+ </div>
930
+ </>
931
+ )}
932
+
933
+ {activeJob && (
934
+ <div className="space-y-4 rounded-xl border border-border p-4">
935
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
936
+ <div>
937
+ <h3 className="font-semibold">Bulk Import Job</h3>
938
+ <p className="text-xs text-text-muted">
939
+ Job ID: <span className="font-mono">{activeJob.jobId}</span>
940
+ </p>
941
+ <p className="text-xs text-text-muted">
942
+ Status: <span className="font-medium">{activeJob.status}</span> | Workers: {activeJob.concurrency}
943
+ </p>
944
+ <p className="text-xs text-text-muted">
945
+ Browser workers run in the background. Close this modal if needed and reopen it later from the provider page with Resume Bulk Progress.
946
+ </p>
947
+ </div>
948
+ <div className="flex gap-2">
949
+ {runningBulkAccountJob && (
950
+ <Button size="sm" variant="secondary" onClick={handleCancelJob}>
951
+ Cancel Job
952
+ </Button>
953
+ )}
954
+ {finishedBulkAccountJob && (
955
+ <Button size="sm" onClick={handleDoneRefresh}>
956
+ Done & Refresh
957
+ </Button>
958
+ )}
959
+ </div>
960
+ </div>
961
+
962
+ <div className="grid grid-cols-2 gap-2 sm:grid-cols-3">
963
+ {summaryItems.map(([label, value]) => (
964
+ <div key={label} className="rounded-lg bg-sidebar px-3 py-2">
965
+ <p className="text-[11px] uppercase tracking-wide text-text-muted">{label}</p>
966
+ <p className="text-lg font-semibold">{value}</p>
967
+ </div>
968
+ ))}
969
+ </div>
970
+
971
+ {activeJob.error && (
972
+ <div className="rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/20 dark:text-red-300">
973
+ {activeJob.error}
974
+ </div>
975
+ )}
976
+
977
+ {activeJob.summary?.needs_manual > 0 && (
978
+ <div className="rounded-lg border border-amber-200 bg-amber-50 p-3 text-sm text-amber-800 dark:border-amber-800 dark:bg-amber-900/20 dark:text-amber-200">
979
+ Some accounts need manual assist. Open the blocked worker session below, finish the Google or Kiro prompts, and the job will continue updating automatically.
980
+ </div>
981
+ )}
982
+
983
+ <div className="grid gap-4 lg:grid-cols-[minmax(0,7fr)_minmax(320px,3fr)]">
984
+ <div className="space-y-4">
985
+ <div className="overflow-hidden rounded-xl border border-border bg-sidebar">
986
+ <div className="flex flex-col gap-2 border-b border-border px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
987
+ <div>
988
+ <p className="text-sm font-semibold">Live Browser Preview</p>
989
+ <p className="text-xs text-text-muted">
990
+ {activeJob.preview?.email || spotlightAccount?.email || "Waiting for worker"}
991
+ {activeJob.preview?.workerId ? ` | Worker ${activeJob.preview.workerId}` : ""}
992
+ </p>
993
+ </div>
994
+ <div className="text-right text-xs text-text-muted">
995
+ <p>{formatStepLabel(activeJob.preview?.step || spotlightAccount?.currentStep)}</p>
996
+ <p>Updated {formatClock(activeJob.preview?.updatedAt || spotlightAccount?.updatedAt)}</p>
997
+ </div>
998
+ </div>
999
+
1000
+ <div className="relative bg-black/90">
1001
+ {activeJob.preview?.imageData ? (
1002
+ <Image
1003
+ src={activeJob.preview.imageData}
1004
+ alt={`Live worker preview for ${activeJob.preview.email || "Kiro import"}`}
1005
+ width={1440}
1006
+ height={900}
1007
+ unoptimized
1008
+ className="h-[420px] w-full object-contain"
1009
+ />
1010
+ ) : (
1011
+ <div className="flex h-[420px] flex-col items-center justify-center gap-3 px-6 text-center text-slate-200">
1012
+ <span className="material-symbols-outlined text-5xl text-primary/80">browser_updated</span>
1013
+ <div>
1014
+ <p className="text-base font-medium">Preview will appear when a worker opens Google or Kiro</p>
1015
+ <p className="mt-1 text-sm text-slate-400">
1016
+ The import keeps running in the background even if the screenshot is not available yet.
1017
+ </p>
1018
+ </div>
1019
+ </div>
1020
+ )}
1021
+ </div>
1022
+ </div>
1023
+
1024
+ <div className="space-y-4">
1025
+ {groupedAccounts.map((group) => (
1026
+ <div key={group.status} className={`rounded-xl border p-4 ${getStatusPanelClasses(group.status)}`}>
1027
+ <div className="mb-3 flex items-center justify-between gap-3">
1028
+ <div className="flex items-center gap-2">
1029
+ <AccountStatusBadge status={group.status} />
1030
+ <p className="text-sm font-semibold capitalize">{formatStepLabel(group.status)}</p>
1031
+ </div>
1032
+ <p className="text-xs text-text-muted">{group.accounts.length} account{group.accounts.length > 1 ? "s" : ""}</p>
1033
+ </div>
1034
+
1035
+ <div className="grid gap-3 xl:grid-cols-2">
1036
+ {group.accounts.map((account) => (
1037
+ <div key={`${account.email}-${account.line}`} className="rounded-xl border border-border bg-background/80 p-4">
1038
+ <div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
1039
+ <div className="min-w-0">
1040
+ <p className="truncate text-sm font-semibold">{account.email}</p>
1041
+ <p className="text-[11px] text-text-muted">
1042
+ Line {account.line}{account.workerId ? ` | Worker ${account.workerId}` : ""} | {formatClock(account.updatedAt)}
1043
+ </p>
1044
+ </div>
1045
+ <AccountStatusBadge status={account.status} />
1046
+ </div>
1047
+
1048
+ <div className="mt-3 rounded-lg border border-border/70 bg-sidebar/70 px-3 py-2">
1049
+ <p className="text-[11px] uppercase tracking-wide text-text-muted">Current Step</p>
1050
+ <p className="mt-1 text-sm font-medium capitalize">{formatStepLabel(account.currentStep)}</p>
1051
+ </div>
1052
+
1053
+ {account.logs?.length > 0 && (
1054
+ <div className="mt-3 space-y-2">
1055
+ {account.logs.slice(-3).reverse().map((entry) => (
1056
+ <div key={entry.id} className="rounded-lg bg-sidebar/60 px-3 py-2">
1057
+ <div className="flex items-center justify-between gap-3">
1058
+ <p className="text-xs font-medium capitalize">{formatStepLabel(entry.step)}</p>
1059
+ <span className="text-[11px] text-text-muted">{formatClock(entry.at)}</span>
1060
+ </div>
1061
+ <p className="mt-1 text-xs text-text-muted">{entry.message}</p>
1062
+ </div>
1063
+ ))}
1064
+ </div>
1065
+ )}
1066
+
1067
+ {account.error && (
1068
+ <p className="mt-3 text-xs text-red-500">{account.error}</p>
1069
+ )}
1070
+
1071
+ {account.manualSessionAvailable && account.workerId ? (
1072
+ <div className="mt-3 flex items-center gap-2">
1073
+ <Button
1074
+ size="sm"
1075
+ variant={account.manualSessionOpened ? "secondary" : "primary"}
1076
+ onClick={() => handleOpenManualSession(account.workerId)}
1077
+ >
1078
+ {account.manualSessionOpened ? "Re-open Manual Session" : "Open Manual Session"}
1079
+ </Button>
1080
+ <p className="text-[11px] text-text-muted">
1081
+ Open only if this worker is blocked by CAPTCHA, 2FA, or recovery prompts.
1082
+ </p>
1083
+ </div>
1084
+ ) : null}
1085
+ </div>
1086
+ ))}
1087
+ </div>
1088
+ </div>
1089
+ ))}
1090
+ </div>
1091
+ </div>
1092
+
1093
+ <div className="rounded-xl border border-border bg-sidebar/70">
1094
+ <div className="border-b border-border px-4 py-3">
1095
+ <p className="text-sm font-semibold">Live Activity Log</p>
1096
+ <p className="text-xs text-text-muted">
1097
+ Every worker step is recorded here in near real time.
1098
+ </p>
1099
+ </div>
1100
+ <div className="max-h-[720px] space-y-3 overflow-y-auto p-4">
1101
+ {activityItems.length === 0 && (
1102
+ <div className="rounded-lg bg-background/70 px-3 py-4 text-sm text-text-muted">
1103
+ Waiting for the first worker event...
1104
+ </div>
1105
+ )}
1106
+ {activityItems.map((entry) => (
1107
+ <div key={entry.id} className="rounded-lg border border-border/70 bg-background/80 px-3 py-3">
1108
+ <div className="flex items-start justify-between gap-3">
1109
+ <div className="min-w-0">
1110
+ <p className="truncate text-sm font-semibold">{entry.email}</p>
1111
+ <p className="text-[11px] text-text-muted">
1112
+ {entry.workerId ? `Worker ${entry.workerId}` : "Waiting"} | {formatStepLabel(entry.step)}
1113
+ </p>
1114
+ </div>
1115
+ <span className="shrink-0 text-[11px] text-text-muted">{formatClock(entry.at)}</span>
1116
+ </div>
1117
+ <p className="mt-2 text-xs text-text-muted">{entry.message}</p>
1118
+ </div>
1119
+ ))}
1120
+ </div>
1121
+ </div>
1122
+ </div>
1123
+ </div>
1124
+ )}
1125
+
1126
+ {error && (
1127
+ <div className="rounded-lg border border-red-200 bg-red-50 p-3 dark:border-red-800 dark:bg-red-900/20">
1128
+ <p className="text-sm text-red-600 dark:text-red-400">{error}</p>
1129
+ </div>
1130
+ )}
1131
+
1132
+ <div className="flex gap-2">
1133
+ {!activeJob && (
1134
+ <Button onClick={handleBulkImport} fullWidth disabled={importing || !bulkText.trim()}>
1135
+ {importing ? "Starting..." : "Start Bulk Import"}
1136
+ </Button>
1137
+ )}
1138
+ {activeJob && !finishedBulkAccountJob && (
1139
+ <Button onClick={handleCancelJob} fullWidth variant="secondary" disabled={!runningBulkAccountJob}>
1140
+ {runningBulkAccountJob ? "Cancel Running Job" : "Job Stopped"}
1141
+ </Button>
1142
+ )}
1143
+ {finishedBulkAccountJob && (
1144
+ <Button onClick={handleDoneRefresh} fullWidth>
1145
+ Done & Refresh Connections
1146
+ </Button>
1147
+ )}
1148
+ <Button onClick={handleBack} variant="ghost" fullWidth>
1149
+ {activeJob ? "Back" : "Cancel"}
1150
+ </Button>
1151
+ </div>
1152
+ </>
1153
+ )}
1154
+ </div>
1155
+ )}
1156
+ </div>
1157
+ </Modal>
1158
+ );
1159
+ }
1160
+
1161
+ KiroAuthModal.propTypes = {
1162
+ isOpen: PropTypes.bool.isRequired,
1163
+ onMethodSelect: PropTypes.func.isRequired,
1164
+ onImportSuccess: PropTypes.func,
1165
+ initialJobId: PropTypes.string,
1166
+ initialSelectedMethod: PropTypes.string,
1167
+ initialImportMode: PropTypes.string,
1168
+ initialFlowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
1169
+ onBulkJobChange: PropTypes.func,
1170
+ onClose: PropTypes.func.isRequired,
1171
+ };