@within-7/minto 0.3.9 → 0.4.0

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 (383) hide show
  1. package/dist/Tool.js.map +2 -2
  2. package/dist/commands/agents/AgentsCommand.js +461 -657
  3. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  4. package/dist/commands/agents/types.js +1 -0
  5. package/dist/commands/agents/types.js.map +2 -2
  6. package/dist/commands/agents/utils/fileOperations.js +96 -36
  7. package/dist/commands/agents/utils/fileOperations.js.map +3 -3
  8. package/dist/commands/agents/utils/index.js +3 -1
  9. package/dist/commands/agents/utils/index.js.map +2 -2
  10. package/dist/commands/context.js +54 -23
  11. package/dist/commands/context.js.map +2 -2
  12. package/dist/commands/ctx_viz.js +1 -1
  13. package/dist/commands/effort.js +87 -0
  14. package/dist/commands/effort.js.map +7 -0
  15. package/dist/commands/export.js +684 -94
  16. package/dist/commands/export.js.map +2 -2
  17. package/dist/commands/ide.js +18 -0
  18. package/dist/commands/ide.js.map +7 -0
  19. package/dist/commands/language.js +19 -46
  20. package/dist/commands/language.js.map +2 -2
  21. package/dist/commands/mcp-interactive.js +425 -217
  22. package/dist/commands/mcp-interactive.js.map +2 -2
  23. package/dist/commands/memory.js +168 -0
  24. package/dist/commands/memory.js.map +7 -0
  25. package/dist/commands/model.js +457 -65
  26. package/dist/commands/model.js.map +2 -2
  27. package/dist/commands/outputStyle.js +64 -0
  28. package/dist/commands/outputStyle.js.map +7 -0
  29. package/dist/commands/permissions.js +75 -49
  30. package/dist/commands/permissions.js.map +2 -2
  31. package/dist/commands/plugin/utils.js +33 -1
  32. package/dist/commands/plugin/utils.js.map +2 -2
  33. package/dist/commands/plugin.js +891 -185
  34. package/dist/commands/plugin.js.map +3 -3
  35. package/dist/commands/refreshCommands.js +2 -0
  36. package/dist/commands/refreshCommands.js.map +2 -2
  37. package/dist/commands/resume.js +1 -1
  38. package/dist/commands/resume.js.map +1 -1
  39. package/dist/commands/review.js +51 -0
  40. package/dist/commands/review.js.map +7 -0
  41. package/dist/commands/sandbox.js +168 -70
  42. package/dist/commands/sandbox.js.map +2 -2
  43. package/dist/commands/setup.js +593 -107
  44. package/dist/commands/setup.js.map +2 -2
  45. package/dist/commands/stats.js +188 -131
  46. package/dist/commands/stats.js.map +2 -2
  47. package/dist/commands/status.js +75 -13
  48. package/dist/commands/status.js.map +2 -2
  49. package/dist/commands/terminalSetup.js +6 -0
  50. package/dist/commands/terminalSetup.js.map +2 -2
  51. package/dist/commands/undo.js +146 -174
  52. package/dist/commands/undo.js.map +2 -2
  53. package/dist/commands/vim.js +22 -0
  54. package/dist/commands/vim.js.map +7 -0
  55. package/dist/commands.js +12 -0
  56. package/dist/commands.js.map +2 -2
  57. package/dist/components/Help.js +165 -32
  58. package/dist/components/Help.js.map +2 -2
  59. package/dist/components/HighlightedCode.js +1 -0
  60. package/dist/components/HighlightedCode.js.map +2 -2
  61. package/dist/components/InfoPanel/InfoPanel.js +123 -0
  62. package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
  63. package/dist/components/InfoPanel/index.js +5 -0
  64. package/dist/components/InfoPanel/index.js.map +7 -0
  65. package/dist/components/InfoPanel/types.js +1 -0
  66. package/dist/components/InfoPanel/types.js.map +7 -0
  67. package/dist/components/ModelSelector/BrandTextInput.js +43 -0
  68. package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
  69. package/dist/components/ModelSelector/ModelSelector.js +590 -565
  70. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  71. package/dist/components/ModelSelector/WizardContainer.js +45 -0
  72. package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
  73. package/dist/components/ModelSelector/index.js +1 -3
  74. package/dist/components/ModelSelector/index.js.map +2 -2
  75. package/dist/components/PromptInput.js +26 -11
  76. package/dist/components/PromptInput.js.map +2 -2
  77. package/dist/components/PulseLabel.js +44 -0
  78. package/dist/components/PulseLabel.js.map +7 -0
  79. package/dist/components/RequestStatusIndicator.js +1 -1
  80. package/dist/components/RequestStatusIndicator.js.map +1 -1
  81. package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
  82. package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
  83. package/dist/components/SimpleSelector/index.js +5 -0
  84. package/dist/components/SimpleSelector/index.js.map +7 -0
  85. package/dist/components/SimpleSelector/types.js +1 -0
  86. package/dist/components/SimpleSelector/types.js.map +7 -0
  87. package/dist/components/Spinner.js +12 -42
  88. package/dist/components/Spinner.js.map +3 -3
  89. package/dist/components/StartupStatus.js +57 -0
  90. package/dist/components/StartupStatus.js.map +7 -0
  91. package/dist/components/StatusOverlayContent.js +21 -0
  92. package/dist/components/StatusOverlayContent.js.map +7 -0
  93. package/dist/components/SubagentBlock.js +43 -6
  94. package/dist/components/SubagentBlock.js.map +2 -2
  95. package/dist/components/TabbedListView/ScrollableList.js +31 -5
  96. package/dist/components/TabbedListView/ScrollableList.js.map +2 -2
  97. package/dist/components/TabbedListView/TabBar.js +13 -8
  98. package/dist/components/TabbedListView/TabBar.js.map +2 -2
  99. package/dist/components/TabbedListView/TabbedListView.js +123 -48
  100. package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
  101. package/dist/components/TodoPanel.js +1 -1
  102. package/dist/components/TodoPanel.js.map +1 -1
  103. package/dist/components/ToolUseLoader.js +5 -0
  104. package/dist/components/ToolUseLoader.js.map +2 -2
  105. package/dist/components/TrustDialog.js +0 -2
  106. package/dist/components/TrustDialog.js.map +2 -2
  107. package/dist/components/messages/TaskInModuleView.js +1 -1
  108. package/dist/components/messages/TaskInModuleView.js.map +2 -2
  109. package/dist/components/messages/TaskToolMessage.js +1 -1
  110. package/dist/components/messages/TaskToolMessage.js.map +2 -2
  111. package/dist/components/messages/UserPromptMessage.js +6 -1
  112. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  113. package/dist/constants/modelCapabilities.js +103 -18
  114. package/dist/constants/modelCapabilities.js.map +2 -2
  115. package/dist/constants/product.js +2 -0
  116. package/dist/constants/product.js.map +2 -2
  117. package/dist/constants/prompts/agentPrompt.js +30 -0
  118. package/dist/constants/prompts/agentPrompt.js.map +7 -0
  119. package/dist/constants/prompts/codeConventions.js +27 -0
  120. package/dist/constants/prompts/codeConventions.js.map +7 -0
  121. package/dist/constants/prompts/doingTasks.js +15 -0
  122. package/dist/constants/prompts/doingTasks.js.map +7 -0
  123. package/dist/constants/prompts/envInfo.js +17 -0
  124. package/dist/constants/prompts/envInfo.js.map +7 -0
  125. package/dist/constants/prompts/executingWithCare.js +17 -0
  126. package/dist/constants/prompts/executingWithCare.js.map +7 -0
  127. package/dist/constants/prompts/identity.js +10 -0
  128. package/dist/constants/prompts/identity.js.map +7 -0
  129. package/dist/constants/prompts/index.js +78 -0
  130. package/dist/constants/prompts/index.js.map +7 -0
  131. package/dist/constants/prompts/taskManagement.js +60 -0
  132. package/dist/constants/prompts/taskManagement.js.map +7 -0
  133. package/dist/constants/prompts/toneAndStyle.js +62 -0
  134. package/dist/constants/prompts/toneAndStyle.js.map +7 -0
  135. package/dist/constants/prompts/toolUsagePolicy.js +38 -0
  136. package/dist/constants/prompts/toolUsagePolicy.js.map +7 -0
  137. package/dist/constants/prompts.js +5 -176
  138. package/dist/constants/prompts.js.map +2 -2
  139. package/dist/constants/providerRegistry.js +235 -0
  140. package/dist/constants/providerRegistry.js.map +7 -0
  141. package/dist/constants/providers.js +35 -0
  142. package/dist/constants/providers.js.map +7 -0
  143. package/dist/context/PermissionContext.js +0 -1
  144. package/dist/context/PermissionContext.js.map +2 -2
  145. package/dist/context.js +87 -31
  146. package/dist/context.js.map +2 -2
  147. package/dist/core/backupHook.js +29 -0
  148. package/dist/core/backupHook.js.map +7 -0
  149. package/dist/core/config/defaults.js +11 -2
  150. package/dist/core/config/defaults.js.map +2 -2
  151. package/dist/core/config/schema.js +21 -3
  152. package/dist/core/config/schema.js.map +2 -2
  153. package/dist/core/costTracker.js +18 -16
  154. package/dist/core/costTracker.js.map +2 -2
  155. package/dist/core/index.js +0 -1
  156. package/dist/core/index.js.map +2 -2
  157. package/dist/core/tokenStatsManager.js +22 -4
  158. package/dist/core/tokenStatsManager.js.map +2 -2
  159. package/dist/cost-tracker.js +0 -16
  160. package/dist/cost-tracker.js.map +2 -2
  161. package/dist/entrypoints/bootstrap.js +3 -1
  162. package/dist/entrypoints/bootstrap.js.map +2 -2
  163. package/dist/entrypoints/cli.js +81 -68
  164. package/dist/entrypoints/cli.js.map +2 -2
  165. package/dist/hooks/useAgentTokenStats.js +1 -1
  166. package/dist/hooks/useAgentTokenStats.js.map +2 -2
  167. package/dist/hooks/useAgentTranscripts.js +2 -1
  168. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  169. package/dist/hooks/useBackgroundShells.js +29 -0
  170. package/dist/hooks/useBackgroundShells.js.map +7 -0
  171. package/dist/hooks/useCanUseTool.js +1 -1
  172. package/dist/hooks/useCanUseTool.js.map +2 -2
  173. package/dist/hooks/useDeferredLoading.js +64 -0
  174. package/dist/hooks/useDeferredLoading.js.map +7 -0
  175. package/dist/hooks/useHookStatus.js +1 -1
  176. package/dist/hooks/useHookStatus.js.map +2 -2
  177. package/dist/hooks/useSessionTracking.js +55 -0
  178. package/dist/hooks/useSessionTracking.js.map +7 -0
  179. package/dist/hooks/useTerminalSize.js +21 -0
  180. package/dist/hooks/useTerminalSize.js.map +2 -2
  181. package/dist/hooks/useTextInput.js +1 -0
  182. package/dist/hooks/useTextInput.js.map +2 -2
  183. package/dist/hooks/useUnifiedCompletion.js +3 -2
  184. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  185. package/dist/i18n/locales/en.js +299 -1
  186. package/dist/i18n/locales/en.js.map +2 -2
  187. package/dist/i18n/locales/zh-CN.js +300 -2
  188. package/dist/i18n/locales/zh-CN.js.map +2 -2
  189. package/dist/i18n/types.js.map +1 -1
  190. package/dist/messages.js +41 -17
  191. package/dist/messages.js.map +2 -2
  192. package/dist/permissions.js +94 -1
  193. package/dist/permissions.js.map +2 -2
  194. package/dist/query.js +27 -19
  195. package/dist/query.js.map +2 -2
  196. package/dist/screens/REPL.js +83 -74
  197. package/dist/screens/REPL.js.map +2 -2
  198. package/dist/services/adapters/responsesAPI.js +6 -0
  199. package/dist/services/adapters/responsesAPI.js.map +2 -2
  200. package/dist/services/agentTeams/index.js +35 -0
  201. package/dist/services/agentTeams/index.js.map +7 -0
  202. package/dist/services/agentTeams/mailbox.js +114 -0
  203. package/dist/services/agentTeams/mailbox.js.map +7 -0
  204. package/dist/services/agentTeams/teamManager.js +149 -0
  205. package/dist/services/agentTeams/teamManager.js.map +7 -0
  206. package/dist/services/agentTeams/teamTaskStore.js +114 -0
  207. package/dist/services/agentTeams/teamTaskStore.js.map +7 -0
  208. package/dist/services/agentTeams/teammateSpawner.js +80 -0
  209. package/dist/services/agentTeams/teammateSpawner.js.map +7 -0
  210. package/dist/services/checkpointManager.js +16 -3
  211. package/dist/services/checkpointManager.js.map +2 -2
  212. package/dist/services/claude.js +19 -1728
  213. package/dist/services/claude.js.map +3 -3
  214. package/dist/services/customCommands.js +30 -8
  215. package/dist/services/customCommands.js.map +2 -2
  216. package/dist/services/gpt5ConnectionTest.js +4 -2
  217. package/dist/services/gpt5ConnectionTest.js.map +2 -2
  218. package/dist/services/hookExecutor.js +411 -127
  219. package/dist/services/hookExecutor.js.map +2 -2
  220. package/dist/services/llm/anthropicProvider.js +807 -0
  221. package/dist/services/llm/anthropicProvider.js.map +7 -0
  222. package/dist/services/llm/dispatch.js +218 -0
  223. package/dist/services/llm/dispatch.js.map +7 -0
  224. package/dist/services/llm/index.js +44 -0
  225. package/dist/services/llm/index.js.map +7 -0
  226. package/dist/services/llm/mintoContext.js +69 -0
  227. package/dist/services/llm/mintoContext.js.map +7 -0
  228. package/dist/services/llm/openaiProvider.js +622 -0
  229. package/dist/services/llm/openaiProvider.js.map +7 -0
  230. package/dist/services/llm/types.js +157 -0
  231. package/dist/services/llm/types.js.map +7 -0
  232. package/dist/services/mcpClient.js +183 -33
  233. package/dist/services/mcpClient.js.map +2 -2
  234. package/dist/services/notifier.js +14 -0
  235. package/dist/services/notifier.js.map +2 -2
  236. package/dist/services/oauth.js +4 -2
  237. package/dist/services/oauth.js.map +2 -2
  238. package/dist/services/openai.js +66 -56
  239. package/dist/services/openai.js.map +3 -3
  240. package/dist/services/outputStyles.js +102 -21
  241. package/dist/services/outputStyles.js.map +2 -2
  242. package/dist/services/plugins/lspServers.js +1 -1
  243. package/dist/services/plugins/lspServers.js.map +2 -2
  244. package/dist/services/plugins/pluginRuntime.js +2 -1
  245. package/dist/services/plugins/pluginRuntime.js.map +2 -2
  246. package/dist/services/plugins/pluginValidation.js +10 -3
  247. package/dist/services/plugins/pluginValidation.js.map +2 -2
  248. package/dist/services/plugins/skillMarketplace.js +20 -9
  249. package/dist/services/plugins/skillMarketplace.js.map +2 -2
  250. package/dist/services/sentry.js +1 -1
  251. package/dist/services/sentry.js.map +2 -2
  252. package/dist/services/sessionMemory.js +16 -3
  253. package/dist/services/sessionMemory.js.map +2 -2
  254. package/dist/services/systemReminder.js +367 -9
  255. package/dist/services/systemReminder.js.map +2 -2
  256. package/dist/services/taskStore.js +19 -0
  257. package/dist/services/taskStore.js.map +2 -2
  258. package/dist/tools/ArchitectTool/ArchitectTool.js.map +1 -1
  259. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +1 -1
  260. package/dist/tools/BashOutputTool/BashOutputTool.js.map +1 -1
  261. package/dist/tools/BashTool/BashTool.js +28 -0
  262. package/dist/tools/BashTool/BashTool.js.map +2 -2
  263. package/dist/tools/FileEditTool/FileEditTool.js +8 -1
  264. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  265. package/dist/tools/FileReadTool/FileReadTool.js +14 -0
  266. package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
  267. package/dist/tools/FileWriteTool/FileWriteTool.js +10 -1
  268. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  269. package/dist/tools/GlobTool/GlobTool.js.map +1 -1
  270. package/dist/tools/GrepTool/GrepTool.js.map +1 -1
  271. package/dist/tools/KillShellTool/KillShellTool.js.map +1 -1
  272. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
  273. package/dist/tools/LspTool/LspTool.js +11 -2
  274. package/dist/tools/LspTool/LspTool.js.map +2 -2
  275. package/dist/tools/MCPTool/MCPTool.js.map +1 -1
  276. package/dist/tools/MemoryReadTool/MemoryReadTool.js +2 -1
  277. package/dist/tools/MemoryReadTool/MemoryReadTool.js.map +2 -2
  278. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js +2 -1
  279. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js.map +2 -2
  280. package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
  281. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  282. package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
  283. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  284. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +1 -1
  285. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +8 -2
  286. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  287. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +2 -0
  288. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  289. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js.map +1 -1
  290. package/dist/tools/SlashCommandTool/SlashCommandTool.js +174 -18
  291. package/dist/tools/SlashCommandTool/SlashCommandTool.js.map +3 -3
  292. package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +1 -1
  293. package/dist/tools/TaskGetTool/TaskGetTool.js.map +1 -1
  294. package/dist/tools/TaskListTool/TaskListTool.js.map +1 -1
  295. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +1 -1
  296. package/dist/tools/TaskStopTool/TaskStopTool.js.map +1 -1
  297. package/dist/tools/TaskTool/TaskTool.js +84 -11
  298. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  299. package/dist/tools/TaskTool/prompt.js +12 -6
  300. package/dist/tools/TaskTool/prompt.js.map +2 -2
  301. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +1 -1
  302. package/dist/tools/ThinkTool/ThinkTool.js.map +1 -1
  303. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +1 -1
  304. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +1 -1
  305. package/dist/tools/WebSearchTool/WebSearchTool.js.map +1 -1
  306. package/dist/tools/WebSearchTool/searchProviders.js +2 -1
  307. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  308. package/dist/tools/lsTool/lsTool.js.map +2 -2
  309. package/dist/tools/lsTool/prompt.js.map +1 -1
  310. package/dist/tools.js +14 -3
  311. package/dist/tools.js.map +2 -2
  312. package/dist/types/PermissionMode.js +21 -1
  313. package/dist/types/PermissionMode.js.map +2 -2
  314. package/dist/types/agentTeams.js +1 -0
  315. package/dist/types/agentTeams.js.map +7 -0
  316. package/dist/types/hooks.js +8 -2
  317. package/dist/types/hooks.js.map +2 -2
  318. package/dist/types/plugin.js +3 -5
  319. package/dist/types/plugin.js.map +2 -2
  320. package/dist/utils/agentHookExecutor.js +1 -4
  321. package/dist/utils/agentHookExecutor.js.map +2 -2
  322. package/dist/utils/agentLoader.js +91 -15
  323. package/dist/utils/agentLoader.js.map +2 -2
  324. package/dist/utils/agentMemory.js.map +2 -2
  325. package/dist/utils/animationManager.js +1 -1
  326. package/dist/utils/animationManager.js.map +2 -2
  327. package/dist/utils/ask.js +1 -1
  328. package/dist/utils/async.js +5 -1
  329. package/dist/utils/async.js.map +2 -2
  330. package/dist/utils/autoCompactCore.js +60 -0
  331. package/dist/utils/autoCompactCore.js.map +2 -2
  332. package/dist/utils/claudeCodeSync.js +439 -0
  333. package/dist/utils/claudeCodeSync.js.map +7 -0
  334. package/dist/utils/config.js +27 -151
  335. package/dist/utils/config.js.map +2 -2
  336. package/dist/utils/configSchema.js +227 -0
  337. package/dist/utils/configSchema.js.map +7 -0
  338. package/dist/utils/debugLogger.js.map +2 -2
  339. package/dist/utils/env.js +4 -3
  340. package/dist/utils/env.js.map +2 -2
  341. package/dist/utils/envConfig.js +34 -0
  342. package/dist/utils/envConfig.js.map +3 -3
  343. package/dist/utils/execFileNoThrow.js +2 -1
  344. package/dist/utils/execFileNoThrow.js.map +2 -2
  345. package/dist/utils/gpt5.js +146 -0
  346. package/dist/utils/gpt5.js.map +7 -0
  347. package/dist/utils/hookManager.js +374 -140
  348. package/dist/utils/hookManager.js.map +2 -2
  349. package/dist/utils/markdown.js +47 -0
  350. package/dist/utils/markdown.js.map +2 -2
  351. package/dist/utils/marketplaceManager.js +80 -43
  352. package/dist/utils/marketplaceManager.js.map +2 -2
  353. package/dist/utils/memoizeWithTTL.js +25 -0
  354. package/dist/utils/memoizeWithTTL.js.map +7 -0
  355. package/dist/utils/messages.js +2 -2
  356. package/dist/utils/messages.js.map +2 -2
  357. package/dist/utils/model.js +34 -9
  358. package/dist/utils/model.js.map +2 -2
  359. package/dist/utils/pluginInstaller.js +68 -29
  360. package/dist/utils/pluginInstaller.js.map +2 -2
  361. package/dist/utils/pluginLoader.js +249 -57
  362. package/dist/utils/pluginLoader.js.map +2 -2
  363. package/dist/utils/repoFetcher.js +110 -0
  364. package/dist/utils/repoFetcher.js.map +7 -0
  365. package/dist/utils/safeFetch.js +45 -0
  366. package/dist/utils/safeFetch.js.map +7 -0
  367. package/dist/utils/skillLoader.js +77 -12
  368. package/dist/utils/skillLoader.js.map +2 -2
  369. package/dist/utils/streamingState.js +52 -0
  370. package/dist/utils/streamingState.js.map +7 -0
  371. package/dist/utils/stringSubstitution.js +4 -5
  372. package/dist/utils/stringSubstitution.js.map +2 -2
  373. package/dist/utils/style.js +6 -3
  374. package/dist/utils/style.js.map +2 -2
  375. package/dist/utils/teamConfig.js +162 -16
  376. package/dist/utils/teamConfig.js.map +2 -2
  377. package/dist/utils/terminal.js +1 -1
  378. package/dist/utils/terminal.js.map +2 -2
  379. package/dist/utils/toolRiskClassification.js +0 -6
  380. package/dist/utils/toolRiskClassification.js.map +2 -2
  381. package/dist/version.js +2 -2
  382. package/dist/version.js.map +1 -1
  383. package/package.json +7 -6
@@ -1,7 +1,5 @@
1
1
  import React, { useState, useEffect } from "react";
2
- import { Box, Text, useInput, Newline } from "ink";
3
- import { getTheme } from "../../utils/theme.js";
4
- import { Select } from "../CustomSelect/select.js";
2
+ import { Text, useInput } from "ink";
5
3
  import { getModelManager } from "../../utils/model.js";
6
4
  import { useExitOnCtrlCD } from "../../hooks/useExitOnCtrlCD.js";
7
5
  import {
@@ -10,9 +8,7 @@ import {
10
8
  setModelPointer
11
9
  } from "../../utils/config.js";
12
10
  import models, { providers } from "../../constants/models.js";
13
- import TextInput from "../TextInput.js";
14
11
  import OpenAI from "openai";
15
- import chalk from "chalk";
16
12
  import { verifyApiKey } from "../../services/claude.js";
17
13
  import { fetchCustomModels } from "../../services/openai.js";
18
14
  import {
@@ -20,44 +16,32 @@ import {
20
16
  validateGPT5Config
21
17
  } from "../../services/gpt5ConnectionTest.js";
22
18
  import { SEMANTIC_COLORS } from "../../constants/colors.js";
23
- import { ScreenContainer } from "./ScreenContainer.js";
19
+ import { isOpenAICompatibleProvider } from "../../constants/providers.js";
20
+ import { getEffectiveCapabilities } from "../../constants/providerRegistry.js";
21
+ import { t } from "../../i18n/index.js";
22
+ import { SimpleSelector } from "../SimpleSelector/SimpleSelector.js";
23
+ import { InfoPanel } from "../InfoPanel/InfoPanel.js";
24
+ import { BrandTextInput } from "./BrandTextInput.js";
24
25
  import {
25
26
  CONTEXT_LENGTH_OPTIONS,
26
27
  DEFAULT_CONTEXT_LENGTH,
27
28
  MAX_TOKENS_OPTIONS,
28
29
  DEFAULT_MAX_TOKENS
29
30
  } from "./constants.js";
30
- import { useEscapeNavigation } from "./hooks/index.js";
31
- function printModelConfig() {
32
- const config = getGlobalConfig();
33
- const modelProfiles = config.modelProfiles || [];
34
- const activeProfiles = modelProfiles.filter((p) => p.isActive);
35
- if (activeProfiles.length === 0) {
36
- console.log(chalk.gray(" \u23BF No active model profiles configured"));
37
- return;
38
- }
39
- const profileSummary = activeProfiles.map((p) => `${p.name} (${p.provider}: ${p.modelName})`).join(" | ");
40
- console.log(chalk.gray(` \u23BF ${profileSummary}`));
41
- }
42
31
  function ModelSelector({
43
32
  onDone: onDoneProp,
44
33
  abortController,
45
34
  targetPointer,
46
35
  isOnboarding = false,
47
36
  onCancel,
48
- skipModelType = false
37
+ skipModelType = false,
38
+ editingModel
49
39
  }) {
50
40
  const config = getGlobalConfig();
51
- const theme = getTheme();
52
- const onDone = () => {
53
- printModelConfig();
54
- onDoneProp();
55
- };
56
- const exitState = useExitOnCtrlCD(() => process.exit(0));
57
- const getInitialScreen = () => {
58
- return "provider";
59
- };
60
- const [screenStack, setScreenStack] = useState([getInitialScreen()]);
41
+ const onDone = onDoneProp;
42
+ useExitOnCtrlCD(() => process.exit(0));
43
+ const isEditMode = !!editingModel;
44
+ const [screenStack, setScreenStack] = useState([isEditMode ? "testAndSave" : "provider"]);
61
45
  const currentScreen = screenStack[screenStack.length - 1];
62
46
  const navigateTo = (screen) => {
63
47
  setScreenStack((prev) => [...prev, screen]);
@@ -70,22 +54,26 @@ function ModelSelector({
70
54
  }
71
55
  };
72
56
  const [selectedProvider, setSelectedProvider] = useState(
73
- config.primaryProvider ?? "anthropic"
57
+ editingModel?.provider ?? config.primaryProvider ?? "anthropic"
74
58
  );
75
59
  const [anthropicProviderType, setAnthropicProviderType] = useState("official");
76
- const [selectedModel, setSelectedModel] = useState("");
77
- const [apiKey, setApiKey] = useState("");
60
+ const [selectedModel, setSelectedModel] = useState(
61
+ editingModel?.modelName ?? ""
62
+ );
63
+ const [apiKey, setApiKey] = useState(editingModel?.apiKey ?? "");
78
64
  const [maxTokens, setMaxTokens] = useState(
79
- config.maxTokens?.toString() || DEFAULT_MAX_TOKENS.toString()
65
+ editingModel?.maxTokens?.toString() ?? config.maxTokens?.toString() ?? DEFAULT_MAX_TOKENS.toString()
80
66
  );
81
67
  const [maxTokensMode, setMaxTokensMode] = useState(
82
68
  "preset"
83
69
  );
84
70
  const [selectedMaxTokensPreset, setSelectedMaxTokensPreset] = useState(config.maxTokens || DEFAULT_MAX_TOKENS);
85
- const [reasoningEffort, setReasoningEffort] = useState("medium");
86
- const [supportsReasoningEffort, setSupportsReasoningEffort] = useState(false);
71
+ const [reasoningEffort, setReasoningEffort] = useState(
72
+ editingModel?.reasoningEffort ?? "medium"
73
+ );
74
+ const [supportsReasoningEffort, setSupportsReasoningEffort] = useState(!!editingModel?.reasoningEffort);
87
75
  const [contextLength, setContextLength] = useState(
88
- DEFAULT_CONTEXT_LENGTH
76
+ editingModel?.contextLength ?? DEFAULT_CONTEXT_LENGTH
89
77
  );
90
78
  const [activeFieldIndex, setActiveFieldIndex] = useState(0);
91
79
  const [maxTokensCursorOffset, setMaxTokensCursorOffset] = useState(0);
@@ -95,12 +83,13 @@ function ModelSelector({
95
83
  const [modelSearchQuery, setModelSearchQuery] = useState("");
96
84
  const [modelSearchCursorOffset, setModelSearchCursorOffset] = useState(0);
97
85
  const [cursorOffset, setCursorOffset] = useState(0);
98
- const [apiKeyEdited, setApiKeyEdited] = useState(false);
86
+ const [apiKeyEdited, setApiKeyEdited] = useState(isEditMode);
99
87
  const [fetchRetryCount, setFetchRetryCount] = useState(0);
100
88
  const [isRetrying, setIsRetrying] = useState(false);
101
89
  const [isTestingConnection, setIsTestingConnection] = useState(false);
102
90
  const [connectionTestResult, setConnectionTestResult] = useState(null);
103
91
  const [validationError, setValidationError] = useState(null);
92
+ const [isManualModelEntry, setIsManualModelEntry] = useState(false);
104
93
  const [resourceName, setResourceName] = useState("");
105
94
  const [resourceNameCursorOffset, setResourceNameCursorOffset] = useState(0);
106
95
  const [customModelName, setCustomModelName] = useState("");
@@ -111,7 +100,9 @@ function ModelSelector({
111
100
  const [ollamaBaseUrlCursorOffset, setOllamaBaseUrlCursorOffset] = useState(0);
112
101
  const [customBaseUrl, setCustomBaseUrl] = useState("");
113
102
  const [customBaseUrlCursorOffset, setCustomBaseUrlCursorOffset] = useState(0);
114
- const [providerBaseUrl, setProviderBaseUrl] = useState("");
103
+ const [providerBaseUrl, setProviderBaseUrl] = useState(
104
+ editingModel?.baseURL ?? ""
105
+ );
115
106
  const [providerBaseUrlCursorOffset, setProviderBaseUrlCursorOffset] = useState(0);
116
107
  const reasoningEffortOptions = [
117
108
  { label: "Low - Faster responses, less thorough reasoning", value: "low" },
@@ -148,6 +139,11 @@ function ModelSelector({
148
139
  setContextLength(DEFAULT_CONTEXT_LENGTH);
149
140
  }
150
141
  }, [currentScreen, contextLength]);
142
+ useEffect(() => {
143
+ if (currentScreen === "testAndSave" && !isEditMode && !isTestingConnection && !connectionTestResult) {
144
+ handleConnectionTest();
145
+ }
146
+ }, [currentScreen]);
151
147
  const ourModelNames = new Set(
152
148
  (models[selectedProvider] || []).map(
153
149
  (model) => model.model
@@ -240,7 +236,8 @@ function ModelSelector({
240
236
  "anthropic-version": "2023-06-01",
241
237
  "Content-Type": "application/json",
242
238
  Authorization: `Bearer ${apiKey2}`
243
- }
239
+ },
240
+ signal: AbortSignal.timeout(3e4)
244
241
  });
245
242
  if (!response.ok) {
246
243
  if (response.status === 401) {
@@ -305,10 +302,6 @@ function ModelSelector({
305
302
  }));
306
303
  } catch (error) {
307
304
  lastError = error;
308
- console.log(
309
- `Anthropic API failed for ${provider}, trying OpenAI format:`,
310
- error
311
- );
312
305
  }
313
306
  try {
314
307
  const models2 = await fetchCustomModels(baseURL, apiKey);
@@ -322,10 +315,6 @@ function ModelSelector({
322
315
  }));
323
316
  } catch (error) {
324
317
  lastError = error;
325
- console.log(
326
- `OpenAI API failed for ${provider}, falling back to manual input:`,
327
- error
328
- );
329
318
  }
330
319
  let errorMessage = `Failed to fetch ${provider} models using both Anthropic and OpenAI API formats`;
331
320
  if (lastError) {
@@ -622,7 +611,8 @@ function ModelSelector({
622
611
  async function fetchGeminiModels() {
623
612
  try {
624
613
  const response = await fetch(
625
- `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`
614
+ `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`,
615
+ { signal: AbortSignal.timeout(3e4) }
626
616
  );
627
617
  if (!response.ok) {
628
618
  const errorData = await response.json();
@@ -650,7 +640,9 @@ function ModelSelector({
650
640
  }
651
641
  async function fetchOllamaModels() {
652
642
  try {
653
- const response = await fetch(`${ollamaBaseUrl}/models`);
643
+ const response = await fetch(`${ollamaBaseUrl}/models`, {
644
+ signal: AbortSignal.timeout(3e4)
645
+ });
654
646
  if (!response.ok) {
655
647
  throw new Error(`HTTP error ${response.status}: ${response.statusText}`);
656
648
  }
@@ -732,7 +724,8 @@ function ModelSelector({
732
724
  const showResp = await fetch(`${ollamaRoot}/api/show`, {
733
725
  method: "POST",
734
726
  headers: { "Content-Type": "application/json" },
735
- body: JSON.stringify({ name: m.model })
727
+ body: JSON.stringify({ name: m.model }),
728
+ signal: AbortSignal.timeout(3e4)
736
729
  });
737
730
  if (showResp.ok) {
738
731
  const showData = await showResp.json();
@@ -763,7 +756,6 @@ function ModelSelector({
763
756
  } else {
764
757
  setModelLoadError(`Error loading Ollama models: ${errorMessage}`);
765
758
  }
766
- console.error("Error fetching Ollama models:", error);
767
759
  return [];
768
760
  }
769
761
  }
@@ -787,7 +779,6 @@ function ModelSelector({
787
779
  return models2;
788
780
  } catch (error) {
789
781
  lastError = error instanceof Error ? error : new Error(String(error));
790
- console.log(`Model fetch attempt ${attempt} failed:`, lastError.message);
791
782
  if (attempt === MAX_RETRIES) {
792
783
  break;
793
784
  }
@@ -915,7 +906,6 @@ function ModelSelector({
915
906
  navigateTo("model");
916
907
  return fetchedModels;
917
908
  } catch (error) {
918
- console.error("Error fetching models:", error);
919
909
  throw error;
920
910
  } finally {
921
911
  setIsLoadingModels(false);
@@ -923,12 +913,15 @@ function ModelSelector({
923
913
  }
924
914
  function handleApiKeySubmit(key) {
925
915
  setApiKey(key);
916
+ if (isEditMode) {
917
+ goBack();
918
+ return;
919
+ }
926
920
  if (selectedProvider === "azure") {
927
921
  navigateTo("resourceName");
928
922
  return;
929
923
  }
930
- fetchModelsWithRetry().catch((error) => {
931
- console.error("Final error after retries:", error);
924
+ fetchModelsWithRetry().catch(() => {
932
925
  });
933
926
  }
934
927
  function handleResourceNameSubmit(name) {
@@ -946,11 +939,20 @@ function ModelSelector({
946
939
  function handleCustomBaseUrlSubmit(url) {
947
940
  const cleanUrl = url.replace(/\/+$/, "");
948
941
  setCustomBaseUrl(cleanUrl);
942
+ setProviderBaseUrl(cleanUrl);
943
+ if (isEditMode) {
944
+ goBack();
945
+ return;
946
+ }
949
947
  navigateTo("apiKey");
950
948
  }
951
949
  function handleProviderBaseUrlSubmit(url) {
952
950
  const cleanUrl = url.replace(/\/+$/, "");
953
951
  setProviderBaseUrl(cleanUrl);
952
+ if (isEditMode) {
953
+ goBack();
954
+ return;
955
+ }
954
956
  if (selectedProvider === "ollama") {
955
957
  setOllamaBaseUrl(cleanUrl);
956
958
  setIsLoadingModels(true);
@@ -980,6 +982,7 @@ function ModelSelector({
980
982
  function handleCustomModelSubmit(model) {
981
983
  setCustomModelName(model);
982
984
  setSelectedModel(model);
985
+ setIsManualModelEntry(true);
983
986
  setSupportsReasoningEffort(false);
984
987
  setReasoningEffort(null);
985
988
  setMaxTokensMode("preset");
@@ -991,6 +994,7 @@ function ModelSelector({
991
994
  }
992
995
  function handleModelSelection(model) {
993
996
  setSelectedModel(model);
997
+ setIsManualModelEntry(false);
994
998
  const modelInfo = availableModels.find((m) => m.model === model);
995
999
  setSupportsReasoningEffort(modelInfo?.supports_reasoning_effort || false);
996
1000
  if (!modelInfo?.supports_reasoning_effort) {
@@ -1025,6 +1029,17 @@ function ModelSelector({
1025
1029
  setActiveFieldIndex(0);
1026
1030
  }
1027
1031
  const handleModelParamsSubmit = () => {
1032
+ if (isEditMode) {
1033
+ goBack();
1034
+ return;
1035
+ }
1036
+ if (!isManualModelEntry) {
1037
+ const modelInfo = availableModels.find((m) => m.model === selectedModel);
1038
+ if (modelInfo?.context_length || modelInfo?.max_input_tokens) {
1039
+ navigateTo("testAndSave");
1040
+ return;
1041
+ }
1042
+ }
1028
1043
  if (!CONTEXT_LENGTH_OPTIONS.find((opt) => opt.value === contextLength)) {
1029
1044
  setContextLength(DEFAULT_CONTEXT_LENGTH);
1030
1045
  }
@@ -1040,26 +1055,9 @@ function ModelSelector({
1040
1055
  } else if (selectedProvider === "custom-openai") {
1041
1056
  testBaseURL = customBaseUrl;
1042
1057
  }
1043
- const isOpenAICompatible = [
1044
- "minimax",
1045
- "kimi",
1046
- "deepseek",
1047
- "siliconflow",
1048
- "qwen",
1049
- "glm",
1050
- "baidu-qianfan",
1051
- "openai",
1052
- "mistral",
1053
- "xai",
1054
- "groq",
1055
- "custom-openai"
1056
- ].includes(selectedProvider);
1057
- if (isOpenAICompatible) {
1058
+ if (isOpenAICompatibleProvider(selectedProvider)) {
1058
1059
  const isGPT5 = selectedModel?.toLowerCase().includes("gpt-5");
1059
1060
  if (isGPT5) {
1060
- console.log(
1061
- `\u{1F680} Using specialized GPT-5 connection test for model: ${selectedModel}`
1062
- );
1063
1061
  const configValidation = validateGPT5Config({
1064
1062
  model: selectedModel,
1065
1063
  apiKey,
@@ -1152,20 +1150,19 @@ function ModelSelector({
1152
1150
  temperature: 0,
1153
1151
  stream: false
1154
1152
  };
1155
- if (selectedModel && selectedModel.toLowerCase().includes("gpt-5")) {
1156
- console.log(`Applying GPT-5 parameter fix for model: ${selectedModel}`);
1157
- if (testPayload.max_tokens) {
1158
- testPayload.max_completion_tokens = testPayload.max_tokens;
1153
+ if (selectedModel && selectedProvider) {
1154
+ const caps = getEffectiveCapabilities(selectedProvider, selectedModel);
1155
+ if (caps.maxTokensField !== "max_tokens" && testPayload.max_tokens) {
1156
+ testPayload[caps.maxTokensField] = testPayload.max_tokens;
1159
1157
  delete testPayload.max_tokens;
1160
- console.log(
1161
- `Transformed max_tokens \u2192 max_completion_tokens: ${testPayload.max_completion_tokens}`
1162
- );
1163
1158
  }
1164
- if (testPayload.temperature !== void 0 && testPayload.temperature !== 1) {
1165
- console.log(
1166
- `Adjusting temperature from ${testPayload.temperature} to 1 for GPT-5`
1159
+ if (caps.temperatureRange === "omit") {
1160
+ delete testPayload.temperature;
1161
+ } else if (typeof caps.temperatureRange === "object") {
1162
+ testPayload.temperature = Math.min(
1163
+ testPayload.temperature,
1164
+ caps.temperatureRange.max
1167
1165
  );
1168
- testPayload.temperature = 1;
1169
1166
  }
1170
1167
  }
1171
1168
  const headers = {
@@ -1180,14 +1177,11 @@ function ModelSelector({
1180
1177
  const response = await fetch(testURL, {
1181
1178
  method: "POST",
1182
1179
  headers,
1183
- body: JSON.stringify(testPayload)
1180
+ body: JSON.stringify(testPayload),
1181
+ signal: AbortSignal.timeout(3e4)
1184
1182
  });
1185
1183
  if (response.ok) {
1186
1184
  const data = await response.json();
1187
- console.log(
1188
- "[DEBUG] Connection test response:",
1189
- JSON.stringify(data, null, 2)
1190
- );
1191
1185
  let responseContent = "";
1192
1186
  if (data.choices && data.choices.length > 0) {
1193
1187
  responseContent = data.choices[0]?.message?.content || "";
@@ -1196,7 +1190,6 @@ function ModelSelector({
1196
1190
  } else if (data.output) {
1197
1191
  responseContent = data.output?.text || data.output || "";
1198
1192
  }
1199
- console.log("[DEBUG] Extracted response content:", responseContent);
1200
1193
  const containsYes = responseContent.toLowerCase().includes("yes");
1201
1194
  if (containsYes) {
1202
1195
  return {
@@ -1251,9 +1244,6 @@ function ModelSelector({
1251
1244
  // Fast response for connection test
1252
1245
  }
1253
1246
  };
1254
- console.log(`\u{1F527} Testing GPT-5 Responses API for model: ${selectedModel}`);
1255
- console.log(`\u{1F527} Test URL: ${testURL}`);
1256
- console.log(`\u{1F527} Test payload:`, JSON.stringify(testPayload, null, 2));
1257
1247
  const headers = {
1258
1248
  "Content-Type": "application/json",
1259
1249
  Authorization: `Bearer ${apiKey}`
@@ -1262,21 +1252,17 @@ function ModelSelector({
1262
1252
  const response = await fetch(testURL, {
1263
1253
  method: "POST",
1264
1254
  headers,
1265
- body: JSON.stringify(testPayload)
1255
+ body: JSON.stringify(testPayload),
1256
+ signal: AbortSignal.timeout(3e4)
1266
1257
  });
1267
1258
  if (response.ok) {
1268
1259
  const data = await response.json();
1269
- console.log(
1270
- "[DEBUG] Responses API connection test response:",
1271
- JSON.stringify(data, null, 2)
1272
- );
1273
1260
  let responseContent = "";
1274
1261
  if (data.output_text) {
1275
1262
  responseContent = data.output_text;
1276
1263
  } else if (data.output) {
1277
1264
  responseContent = typeof data.output === "string" ? data.output : data.output.text || "";
1278
1265
  }
1279
- console.log("[DEBUG] Extracted response content:", responseContent);
1280
1266
  const containsYes = responseContent.toLowerCase().includes("yes");
1281
1267
  if (containsYes) {
1282
1268
  return {
@@ -1296,10 +1282,6 @@ function ModelSelector({
1296
1282
  } else {
1297
1283
  const errorData = await response.json().catch(() => null);
1298
1284
  const errorMessage = errorData?.error?.message || errorData?.message || response.statusText;
1299
- console.log(
1300
- `\u{1F6A8} GPT-5 Responses API Error (${response.status}):`,
1301
- errorData
1302
- );
1303
1285
  let details = `Responses API Error: ${errorMessage}`;
1304
1286
  if (response.status === 400 && errorMessage.includes("max_tokens")) {
1305
1287
  details += "\n\u{1F527} Note: This appears to be a parameter compatibility issue. The fallback to Chat Completions should handle this.";
@@ -1327,9 +1309,6 @@ function ModelSelector({
1327
1309
  async function testProviderSpecificEndpoint(baseURL) {
1328
1310
  if (selectedProvider === "anthropic" || selectedProvider === "bigdream") {
1329
1311
  try {
1330
- console.log(
1331
- `[DEBUG] Testing ${selectedProvider} connection using official Anthropic SDK...`
1332
- );
1333
1312
  let testBaseURL = void 0;
1334
1313
  if (selectedProvider === "bigdream") {
1335
1314
  testBaseURL = baseURL || "https://api-key.info";
@@ -1357,7 +1336,6 @@ function ModelSelector({
1357
1336
  };
1358
1337
  }
1359
1338
  } catch (error) {
1360
- console.log(`[DEBUG] ${selectedProvider} connection test error:`, error);
1361
1339
  return {
1362
1340
  success: false,
1363
1341
  message: `\u274C ${selectedProvider} connection failed`,
@@ -1375,14 +1353,13 @@ function ModelSelector({
1375
1353
  async function handleConnectionTest() {
1376
1354
  const result = await testConnection();
1377
1355
  setConnectionTestResult(result);
1378
- if (result.success) {
1379
- setTimeout(() => {
1380
- navigateTo("confirmation");
1381
- }, 2e3);
1382
- }
1383
1356
  }
1384
1357
  const handleContextLengthSubmit = () => {
1385
- navigateTo("connectionTest");
1358
+ if (isEditMode) {
1359
+ goBack();
1360
+ return;
1361
+ }
1362
+ navigateTo("testAndSave");
1386
1363
  };
1387
1364
  async function saveConfiguration(provider, model) {
1388
1365
  let baseURL = providerBaseUrl || providers[provider]?.baseURL || "";
@@ -1429,6 +1406,24 @@ function ModelSelector({
1429
1406
  }
1430
1407
  async function handleConfirmation() {
1431
1408
  setValidationError(null);
1409
+ if (isEditMode && editingModel) {
1410
+ try {
1411
+ const modelManager = getModelManager();
1412
+ modelManager.updateModel(editingModel.modelName, {
1413
+ baseURL: providerBaseUrl || editingModel.baseURL,
1414
+ apiKey: apiKey || editingModel.apiKey,
1415
+ maxTokens: parseInt(maxTokens) || editingModel.maxTokens,
1416
+ contextLength: contextLength || editingModel.contextLength,
1417
+ reasoningEffort
1418
+ });
1419
+ onDone();
1420
+ } catch (error) {
1421
+ setValidationError(
1422
+ error instanceof Error ? error.message : "Failed to update model"
1423
+ );
1424
+ }
1425
+ return;
1426
+ }
1432
1427
  const modelId = await saveConfiguration(selectedProvider, selectedModel);
1433
1428
  if (!modelId) {
1434
1429
  return;
@@ -1450,10 +1445,20 @@ function ModelSelector({
1450
1445
  onDone();
1451
1446
  }
1452
1447
  } else {
1448
+ if (currentScreen === "testAndSave") {
1449
+ setConnectionTestResult(null);
1450
+ setIsTestingConnection(false);
1451
+ setValidationError(null);
1452
+ }
1453
+ const prevScreen = screenStack[screenStack.length - 2];
1454
+ if (prevScreen === "modelParams") {
1455
+ const formFields = getFormFieldsForModelParams();
1456
+ const submitIndex = formFields.findIndex((f) => f.name === "submit");
1457
+ setActiveFieldIndex(Math.max(0, submitIndex - 1));
1458
+ }
1453
1459
  setScreenStack((prev) => prev.slice(0, -1));
1454
1460
  }
1455
1461
  };
1456
- useEscapeNavigation(handleBack, abortController);
1457
1462
  function handleCursorOffsetChange(offset) {
1458
1463
  setCursorOffset(offset);
1459
1464
  }
@@ -1468,7 +1473,13 @@ function ModelSelector({
1468
1473
  function handleModelSearchCursorOffsetChange(offset) {
1469
1474
  setModelSearchCursorOffset(offset);
1470
1475
  }
1476
+ const textScreens = ["apiKey", "baseUrl", "resourceName", "modelInput"];
1471
1477
  useInput((input, key) => {
1478
+ if (!textScreens.includes(currentScreen)) return;
1479
+ if (key.escape) {
1480
+ handleBack();
1481
+ return;
1482
+ }
1472
1483
  if (currentScreen === "apiKey" && key.return) {
1473
1484
  if (apiKey) {
1474
1485
  handleApiKeySubmit(apiKey);
@@ -1480,8 +1491,7 @@ function ModelSelector({
1480
1491
  navigateTo("modelInput");
1481
1492
  return;
1482
1493
  }
1483
- fetchModelsWithRetry().catch((error) => {
1484
- console.error("Final error after retries:", error);
1494
+ fetchModelsWithRetry().catch(() => {
1485
1495
  });
1486
1496
  return;
1487
1497
  }
@@ -1505,76 +1515,6 @@ function ModelSelector({
1505
1515
  }
1506
1516
  return;
1507
1517
  }
1508
- if (currentScreen === "confirmation" && key.return) {
1509
- handleConfirmation().catch((error) => {
1510
- console.error("Error in handleConfirmation:", error);
1511
- setValidationError(
1512
- error instanceof Error ? error.message : "Unexpected error occurred"
1513
- );
1514
- });
1515
- return;
1516
- }
1517
- if (currentScreen === "connectionTest") {
1518
- if (key.return) {
1519
- if (!isTestingConnection && !connectionTestResult) {
1520
- handleConnectionTest();
1521
- } else if (connectionTestResult && connectionTestResult.success) {
1522
- navigateTo("confirmation");
1523
- } else if (connectionTestResult && !connectionTestResult.success) {
1524
- handleConnectionTest();
1525
- }
1526
- return;
1527
- }
1528
- }
1529
- if (currentScreen === "contextLength") {
1530
- if (key.return) {
1531
- handleContextLengthSubmit();
1532
- return;
1533
- }
1534
- if (key.upArrow) {
1535
- const currentIndex = CONTEXT_LENGTH_OPTIONS.findIndex(
1536
- (opt) => opt.value === contextLength
1537
- );
1538
- const newIndex = currentIndex > 0 ? currentIndex - 1 : currentIndex === -1 ? CONTEXT_LENGTH_OPTIONS.findIndex(
1539
- (opt) => opt.value === DEFAULT_CONTEXT_LENGTH
1540
- ) || 0 : CONTEXT_LENGTH_OPTIONS.length - 1;
1541
- setContextLength(CONTEXT_LENGTH_OPTIONS[newIndex].value);
1542
- return;
1543
- }
1544
- if (key.downArrow) {
1545
- const currentIndex = CONTEXT_LENGTH_OPTIONS.findIndex(
1546
- (opt) => opt.value === contextLength
1547
- );
1548
- const newIndex = currentIndex === -1 ? CONTEXT_LENGTH_OPTIONS.findIndex(
1549
- (opt) => opt.value === DEFAULT_CONTEXT_LENGTH
1550
- ) || 0 : (currentIndex + 1) % CONTEXT_LENGTH_OPTIONS.length;
1551
- setContextLength(CONTEXT_LENGTH_OPTIONS[newIndex].value);
1552
- return;
1553
- }
1554
- }
1555
- if (currentScreen === "apiKey" && (key.ctrl && input === "v" || key.meta && input === "v")) {
1556
- setModelLoadError(
1557
- "Please use your terminal's paste functionality or type the API key manually"
1558
- );
1559
- return;
1560
- }
1561
- if (currentScreen === "modelParams" && key.tab) {
1562
- const formFields = getFormFieldsForModelParams();
1563
- setActiveFieldIndex((current) => (current + 1) % formFields.length);
1564
- return;
1565
- }
1566
- if (currentScreen === "modelParams" && key.return) {
1567
- const formFields = getFormFieldsForModelParams();
1568
- const currentField = formFields[activeFieldIndex];
1569
- if (currentField.name === "submit" || activeFieldIndex === formFields.length - 1) {
1570
- handleModelParamsSubmit();
1571
- } else if (currentField.component === "select") {
1572
- setActiveFieldIndex(
1573
- (current) => Math.min(current + 1, formFields.length - 1)
1574
- );
1575
- }
1576
- return;
1577
- }
1578
1518
  });
1579
1519
  function getFormFieldsForModelParams() {
1580
1520
  return [
@@ -1607,433 +1547,518 @@ function ModelSelector({
1607
1547
  ];
1608
1548
  }
1609
1549
  if (currentScreen === "apiKey") {
1610
- const modelTypeText = "this model profile";
1611
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1612
- Box,
1550
+ const providerLabel = getProviderLabel(selectedProvider, 0).split(" (")[0];
1551
+ const apiKeyUrls = {
1552
+ kimi: "https://platform.moonshot.cn/console/api-keys",
1553
+ deepseek: "https://platform.deepseek.com/api_keys",
1554
+ siliconflow: "https://cloud.siliconflow.cn/i/oJWsm6io",
1555
+ qwen: "https://bailian.console.aliyun.com/?tab=model#/api-key",
1556
+ glm: "https://open.bigmodel.cn (API Keys section)",
1557
+ minimax: "https://www.minimax.io/platform/user-center/basic-information",
1558
+ "baidu-qianfan": "https://console.bce.baidu.com/iam/#/iam/accesslist",
1559
+ openai: "https://platform.openai.com/api-keys"
1560
+ };
1561
+ let apiKeyUrl = apiKeyUrls[selectedProvider] || "";
1562
+ if (selectedProvider === "anthropic") {
1563
+ apiKeyUrl = anthropicProviderType === "official" ? "https://console.anthropic.com/settings/keys" : anthropicProviderType === "bigdream" ? "https://api-key.info/register?aff=MSl4" : anthropicProviderType === "opendev" ? "https://api.openai-next.com/register/?aff_code=4xo7" : "your custom API provider";
1564
+ }
1565
+ const supportsManualInput = selectedProvider === "anthropic" || selectedProvider === "kimi" || selectedProvider === "deepseek" || selectedProvider === "qwen" || selectedProvider === "glm" || selectedProvider === "minimax" || selectedProvider === "baidu-qianfan" || selectedProvider === "siliconflow" || selectedProvider === "custom-openai";
1566
+ const tabHintText = supportsManualInput ? t("modelSelector.apiKeyTabHintManual") : t("modelSelector.apiKeyTabHintSkip");
1567
+ const apiKeyHint = apiKeyUrl ? /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.info }, apiKeyUrl)) : void 0;
1568
+ return /* @__PURE__ */ React.createElement(
1569
+ BrandTextInput,
1613
1570
  {
1614
- flexDirection: "column",
1615
- gap: 1,
1616
- borderStyle: "round",
1617
- borderColor: theme.secondaryBorder,
1618
- paddingX: 2,
1619
- paddingY: 1
1620
- },
1621
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "API Key Setup", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1622
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Enter your ", getProviderLabel(selectedProvider, 0).split(" (")[0], " ", "API key for ", modelTypeText, ":"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "This key will be stored locally and used to access the", " ", selectedProvider, " API.", /* @__PURE__ */ React.createElement(Newline, null), "Your key is never sent to our servers.", /* @__PURE__ */ React.createElement(Newline, null), /* @__PURE__ */ React.createElement(Newline, null), selectedProvider === "kimi" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "https://platform.moonshot.cn/console/api-keys")), selectedProvider === "deepseek" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "https://platform.deepseek.com/api_keys")), selectedProvider === "siliconflow" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "https://cloud.siliconflow.cn/i/oJWsm6io")), selectedProvider === "qwen" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "https://bailian.console.aliyun.com/?tab=model#/api-key")), selectedProvider === "glm" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "https://open.bigmodel.cn (API Keys section)")), selectedProvider === "minimax" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "https://www.minimax.io/platform/user-center/basic-information")), selectedProvider === "baidu-qianfan" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "https://console.bce.baidu.com/iam/#/iam/accesslist")), selectedProvider === "anthropic" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, anthropicProviderType === "official" ? "https://console.anthropic.com/settings/keys" : anthropicProviderType === "bigdream" ? "https://api-key.info/register?aff=MSl4" : anthropicProviderType === "opendev" ? "https://api.openai-next.com/register/?aff_code=4xo7" : "your custom API provider")), selectedProvider === "openai" && /* @__PURE__ */ React.createElement(React.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "https://platform.openai.com/api-keys")))), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
1623
- TextInput,
1624
- {
1625
- placeholder: "sk-...",
1626
- value: apiKey,
1627
- onChange: handleApiKeyChange,
1628
- onSubmit: handleApiKeySubmit,
1629
- mask: "*",
1630
- columns: 500,
1631
- cursorOffset,
1632
- onChangeCursorOffset: handleCursorOffsetChange,
1633
- showCursor: true
1634
- }
1635
- )), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: apiKey ? theme.suggestion : SEMANTIC_COLORS.dim }, "[Submit API Key]"), /* @__PURE__ */ React.createElement(Text, null, " ", "- Press Enter or click to continue with this API key"))), isLoadingModels && /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Loading available models...")), modelLoadError && /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.error }, "Error: ", modelLoadError)), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Enter"), " to continue,", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Tab"), " to", " ", selectedProvider === "anthropic" || selectedProvider === "kimi" || selectedProvider === "deepseek" || selectedProvider === "qwen" || selectedProvider === "glm" || selectedProvider === "minimax" || selectedProvider === "baidu-qianfan" || selectedProvider === "siliconflow" || selectedProvider === "custom-openai" ? "skip to manual model input" : "skip using a key", ", or ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back")))
1636
- ));
1571
+ title: `${t("modelSelector.apiKeyTitle")} \u2014 ${providerLabel}`,
1572
+ description: t("modelSelector.apiKeyDesc"),
1573
+ placeholder: "sk-...",
1574
+ value: apiKey,
1575
+ onChange: handleApiKeyChange,
1576
+ onSubmit: handleApiKeySubmit,
1577
+ mask: "*",
1578
+ error: modelLoadError ? `Error: ${modelLoadError}` : null,
1579
+ isLoading: isLoadingModels,
1580
+ loadingText: "Loading available models...",
1581
+ hint: apiKeyHint,
1582
+ footerHint: `Enter continue \xB7 Tab ${tabHintText} \xB7 Esc back`,
1583
+ cursorOffset,
1584
+ onChangeCursorOffset: handleCursorOffsetChange
1585
+ }
1586
+ );
1637
1587
  }
1638
1588
  if (currentScreen === "model") {
1639
- const modelTypeText = "this model profile";
1640
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1641
- Box,
1589
+ const providerLabel = getProviderLabel(
1590
+ selectedProvider,
1591
+ availableModels.length
1592
+ ).split(" (")[0];
1593
+ const modelSelectorItems = modelOptions.map((opt) => ({
1594
+ id: opt.value,
1595
+ label: opt.label
1596
+ }));
1597
+ const subtitle = modelOptions.length > 0 ? `${modelOptions.length}/${availableModels.length} models` : availableModels.length > 0 ? t("modelSelector.noModelsMatch") : t("modelSelector.noModelsAvailable");
1598
+ return /* @__PURE__ */ React.createElement(
1599
+ SimpleSelector,
1642
1600
  {
1643
- flexDirection: "column",
1644
- gap: 1,
1645
- borderStyle: "round",
1646
- borderColor: theme.secondaryBorder,
1647
- paddingX: 2,
1648
- paddingY: 1
1649
- },
1650
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "Model Selection", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1651
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Select a model from", " ", getProviderLabel(
1652
- selectedProvider,
1653
- availableModels.length
1654
- ).split(" (")[0], " ", "for ", modelTypeText, ":"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "This model profile can be assigned to different pointers (main, task, reasoning, quick) for various use cases.")), /* @__PURE__ */ React.createElement(Box, { marginY: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Search models:"), /* @__PURE__ */ React.createElement(
1655
- TextInput,
1656
- {
1657
- placeholder: "Type to filter models...",
1658
- value: modelSearchQuery,
1659
- onChange: handleModelSearchChange,
1660
- columns: 100,
1661
- cursorOffset: modelSearchCursorOffset,
1662
- onChangeCursorOffset: handleModelSearchCursorOffsetChange,
1663
- showCursor: true,
1664
- focus: true
1665
- }
1666
- )), modelOptions.length > 0 ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1667
- Select,
1668
- {
1669
- options: modelOptions,
1670
- onChange: handleModelSelection
1671
- }
1672
- ), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Showing ", modelOptions.length, " of ", availableModels.length, " ", "models")) : /* @__PURE__ */ React.createElement(Box, null, availableModels.length > 0 ? /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.running }, "No models match your search. Try a different query.") : /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.running }, "No models available for this provider.")), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back to API key input")))
1673
- ));
1601
+ title: `${t("modelSelector.modelSelectionTitle")} \u2014 ${providerLabel}`,
1602
+ subtitle,
1603
+ items: modelSelectorItems,
1604
+ onSelect: (item) => handleModelSelection(item.id),
1605
+ onClose: handleBack
1606
+ }
1607
+ );
1674
1608
  }
1675
1609
  if (currentScreen === "modelParams") {
1676
1610
  const formFields = getFormFieldsForModelParams();
1677
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1678
- Box,
1679
- {
1680
- flexDirection: "column",
1681
- gap: 1,
1682
- borderStyle: "round",
1683
- borderColor: theme.secondaryBorder,
1684
- paddingX: 2,
1685
- paddingY: 1
1686
- },
1687
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "Model Parameters", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1688
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Configure parameters for ", selectedModel, ":"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Use ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Tab"), " to navigate between fields. Press", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Enter"), " to submit.")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, formFields.map((field, index) => /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1, key: field.name }, field.component !== "button" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1689
- Text,
1690
- {
1691
- bold: true,
1692
- color: activeFieldIndex === index ? theme.success : void 0
1693
- },
1694
- field.label
1695
- ), field.description && /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, field.description)) : /* @__PURE__ */ React.createElement(
1696
- Text,
1697
- {
1698
- bold: true,
1699
- color: activeFieldIndex === index ? theme.success : void 0
1700
- },
1701
- field.label
1702
- ), /* @__PURE__ */ React.createElement(Box, { marginY: 1 }, activeFieldIndex === index ? field.component === "select" ? field.name === "maxTokens" ? /* @__PURE__ */ React.createElement(
1703
- Select,
1611
+ const currentField = formFields[activeFieldIndex];
1612
+ if (currentField?.name === "maxTokens") {
1613
+ const maxTokensItems = MAX_TOKENS_OPTIONS.map((opt) => ({
1614
+ id: opt.value.toString(),
1615
+ label: opt.label,
1616
+ isCurrent: opt.value === parseInt(maxTokens)
1617
+ }));
1618
+ return /* @__PURE__ */ React.createElement(
1619
+ SimpleSelector,
1704
1620
  {
1705
- options: field.options || [],
1706
- onChange: (value) => {
1707
- const numValue = parseInt(value);
1621
+ title: `${t("modelSelector.modelParamsTitle")} \u2014 ${t("modelSelector.maxTokens")}`,
1622
+ subtitle: selectedModel,
1623
+ items: maxTokensItems,
1624
+ onSelect: (item) => {
1625
+ const numValue = parseInt(item.id);
1708
1626
  setMaxTokens(numValue.toString());
1709
1627
  setSelectedMaxTokensPreset(numValue);
1710
- setMaxTokensCursorOffset(
1711
- numValue.toString().length
1712
- );
1713
- setTimeout(() => {
1714
- setActiveFieldIndex(index + 1);
1715
- }, 100);
1628
+ setMaxTokensCursorOffset(numValue.toString().length);
1629
+ setActiveFieldIndex(activeFieldIndex + 1);
1716
1630
  },
1717
- defaultValue: field.defaultValue
1631
+ onClose: handleBack
1718
1632
  }
1719
- ) : /* @__PURE__ */ React.createElement(
1720
- Select,
1633
+ );
1634
+ }
1635
+ if (currentField?.name === "reasoningEffort") {
1636
+ const effortItems = reasoningEffortOptions.map((opt) => ({
1637
+ id: opt.value,
1638
+ label: opt.label,
1639
+ isCurrent: opt.value === reasoningEffort
1640
+ }));
1641
+ return /* @__PURE__ */ React.createElement(
1642
+ SimpleSelector,
1721
1643
  {
1722
- options: reasoningEffortOptions,
1723
- onChange: (value) => {
1724
- setReasoningEffort(value);
1725
- setTimeout(() => {
1726
- setActiveFieldIndex(index + 1);
1727
- }, 100);
1644
+ title: `${t("modelSelector.modelParamsTitle")} \u2014 ${t("modelSelector.reasoningEffort")}`,
1645
+ subtitle: selectedModel,
1646
+ items: effortItems,
1647
+ onSelect: (item) => {
1648
+ setReasoningEffort(item.id);
1649
+ setActiveFieldIndex(activeFieldIndex + 1);
1728
1650
  },
1729
- defaultValue: reasoningEffort
1651
+ onClose: () => {
1652
+ setActiveFieldIndex(0);
1653
+ }
1730
1654
  }
1731
- ) : null : field.name === "maxTokens" ? /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Current:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, MAX_TOKENS_OPTIONS.find(
1732
- (opt) => opt.value === parseInt(maxTokens)
1733
- )?.label || `${maxTokens} tokens`)) : field.name === "reasoningEffort" ? /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Current:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, reasoningEffort)) : null))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Tab"), " to navigate,", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Enter"), " to continue, or", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back"))))
1734
- ));
1655
+ );
1656
+ }
1657
+ handleModelParamsSubmit();
1658
+ return null;
1735
1659
  }
1736
1660
  if (currentScreen === "resourceName") {
1737
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1738
- Box,
1661
+ return /* @__PURE__ */ React.createElement(
1662
+ BrandTextInput,
1739
1663
  {
1740
- flexDirection: "column",
1741
- gap: 1,
1742
- borderStyle: "round",
1743
- borderColor: theme.secondaryBorder,
1744
- paddingX: 2,
1745
- paddingY: 1
1746
- },
1747
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "Azure Resource Setup", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1748
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Enter your Azure OpenAI resource name:"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "This is the name of your Azure OpenAI resource (without the full domain).", /* @__PURE__ */ React.createElement(Newline, null), 'For example, if your endpoint is "https://myresource.openai.azure.com", enter "myresource".')), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
1749
- TextInput,
1750
- {
1751
- placeholder: "myazureresource",
1752
- value: resourceName,
1753
- onChange: setResourceName,
1754
- onSubmit: handleResourceNameSubmit,
1755
- columns: 100,
1756
- cursorOffset: resourceNameCursorOffset,
1757
- onChangeCursorOffset: setResourceNameCursorOffset,
1758
- showCursor: true
1759
- }
1760
- )), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(
1761
- Text,
1762
- {
1763
- color: resourceName ? theme.suggestion : SEMANTIC_COLORS.dim
1764
- },
1765
- "[Submit Resource Name]"
1766
- ), /* @__PURE__ */ React.createElement(Text, null, " - Press Enter or click to continue"))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Enter"), " to continue or", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back")))
1767
- ));
1664
+ title: t("modelSelector.resourceNameTitle"),
1665
+ description: t("modelSelector.resourceNameDesc"),
1666
+ placeholder: "myazureresource",
1667
+ value: resourceName,
1668
+ onChange: setResourceName,
1669
+ onSubmit: handleResourceNameSubmit,
1670
+ footerHint: t("modelSelector.footerEnterContinue"),
1671
+ cursorOffset: resourceNameCursorOffset,
1672
+ onChangeCursorOffset: setResourceNameCursorOffset
1673
+ }
1674
+ );
1768
1675
  }
1769
1676
  if (currentScreen === "baseUrl") {
1770
1677
  const isCustomOpenAI = selectedProvider === "custom-openai";
1771
1678
  if (isCustomOpenAI) {
1772
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1773
- Box,
1679
+ return /* @__PURE__ */ React.createElement(
1680
+ BrandTextInput,
1774
1681
  {
1775
- flexDirection: "column",
1776
- gap: 1,
1777
- borderStyle: "round",
1778
- borderColor: theme.secondaryBorder,
1779
- paddingX: 2,
1780
- paddingY: 1
1781
- },
1782
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "Custom API Server Setup", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1783
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Enter your custom API URL:"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "This is the base URL for your OpenAI-compatible API.", /* @__PURE__ */ React.createElement(Newline, null), "For example: https://api.example.com/v1")), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
1784
- TextInput,
1785
- {
1786
- placeholder: "https://api.example.com/v1",
1787
- value: customBaseUrl,
1788
- onChange: setCustomBaseUrl,
1789
- onSubmit: handleCustomBaseUrlSubmit,
1790
- columns: 100,
1791
- cursorOffset: customBaseUrlCursorOffset,
1792
- onChangeCursorOffset: setCustomBaseUrlCursorOffset,
1793
- showCursor: !isLoadingModels,
1794
- focus: !isLoadingModels
1795
- }
1796
- )), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(
1797
- Text,
1798
- {
1799
- color: isLoadingModels ? theme.secondaryText : theme.suggestion
1800
- },
1801
- "[Submit Base URL]"
1802
- ), /* @__PURE__ */ React.createElement(Text, null, " - Press Enter or click to continue"))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Enter"), " to continue or ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back")))
1803
- ));
1682
+ title: t("modelSelector.customApiTitle"),
1683
+ description: t("modelSelector.customApiDesc"),
1684
+ placeholder: "https://api.example.com/v1",
1685
+ value: customBaseUrl,
1686
+ onChange: setCustomBaseUrl,
1687
+ onSubmit: handleCustomBaseUrlSubmit,
1688
+ isLoading: isLoadingModels,
1689
+ loadingText: "Connecting...",
1690
+ error: modelLoadError ? `Error: ${modelLoadError}` : null,
1691
+ footerHint: t("modelSelector.footerEnterContinue"),
1692
+ cursorOffset: customBaseUrlCursorOffset,
1693
+ onChangeCursorOffset: setCustomBaseUrlCursorOffset
1694
+ }
1695
+ );
1804
1696
  }
1805
1697
  const providerName = providers[selectedProvider]?.name || selectedProvider;
1806
1698
  const defaultUrl = providers[selectedProvider]?.baseURL || "";
1807
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1808
- Box,
1699
+ const desc = selectedProvider === "ollama" ? t("modelSelector.baseUrlOllamaDesc") : `${t("modelSelector.baseUrlDesc")} You can modify this URL or press Enter to use the default.`;
1700
+ return /* @__PURE__ */ React.createElement(
1701
+ BrandTextInput,
1809
1702
  {
1810
- flexDirection: "column",
1811
- gap: 1,
1812
- borderStyle: "round",
1813
- borderColor: theme.secondaryBorder,
1814
- paddingX: 2,
1815
- paddingY: 1
1816
- },
1817
- /* @__PURE__ */ React.createElement(Text, { bold: true }, providerName, " API Configuration", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1818
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Configure the API endpoint for ", providerName, ":"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, selectedProvider === "ollama" ? /* @__PURE__ */ React.createElement(React.Fragment, null, "This is the URL of your Ollama server.", /* @__PURE__ */ React.createElement(Newline, null), "Default is http://localhost:11434/v1 for local Ollama installations.") : /* @__PURE__ */ React.createElement(React.Fragment, null, "This is the base URL for the ", providerName, " API.", /* @__PURE__ */ React.createElement(Newline, null), "You can modify this URL or press Enter to use the default."))), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
1819
- TextInput,
1820
- {
1821
- placeholder: defaultUrl,
1822
- value: providerBaseUrl,
1823
- onChange: setProviderBaseUrl,
1824
- onSubmit: handleProviderBaseUrlSubmit,
1825
- columns: 100,
1826
- cursorOffset: providerBaseUrlCursorOffset,
1827
- onChangeCursorOffset: setProviderBaseUrlCursorOffset,
1828
- showCursor: !isLoadingModels,
1829
- focus: !isLoadingModels
1830
- }
1831
- )), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(
1832
- Text,
1833
- {
1834
- color: isLoadingModels ? theme.secondaryText : theme.suggestion
1835
- },
1836
- "[Submit Base URL]"
1837
- ), /* @__PURE__ */ React.createElement(Text, null, " - Press Enter or click to continue"))), isLoadingModels && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, selectedProvider === "ollama" ? "Connecting to Ollama server..." : `Connecting to ${providerName}...`)), modelLoadError && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.error }, "Error: ", modelLoadError)), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Enter"), " to continue or", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back")))
1838
- ));
1703
+ title: `${t("modelSelector.baseUrlTitle")} \u2014 ${providerName}`,
1704
+ description: desc,
1705
+ placeholder: defaultUrl,
1706
+ value: providerBaseUrl,
1707
+ onChange: setProviderBaseUrl,
1708
+ onSubmit: handleProviderBaseUrlSubmit,
1709
+ isLoading: isLoadingModels,
1710
+ loadingText: selectedProvider === "ollama" ? "Connecting to Ollama server..." : `Connecting to ${providerName}...`,
1711
+ error: modelLoadError ? `Error: ${modelLoadError}` : null,
1712
+ footerHint: t("modelSelector.footerEnterContinue"),
1713
+ cursorOffset: providerBaseUrlCursorOffset,
1714
+ onChangeCursorOffset: setProviderBaseUrlCursorOffset
1715
+ }
1716
+ );
1839
1717
  }
1840
1718
  if (currentScreen === "modelInput") {
1841
- const modelTypeText = "this model profile";
1842
- let screenTitle = "Manual Model Setup";
1843
- let description = "Enter the model name manually";
1844
- let placeholder = "gpt-4";
1845
- let examples = 'For example: "gpt-4", "gpt-3.5-turbo", etc.';
1846
- if (selectedProvider === "azure") {
1847
- screenTitle = "Azure Model Setup";
1848
- description = `Enter your Azure OpenAI deployment name for ${modelTypeText}:`;
1849
- examples = 'For example: "gpt-4", "gpt-35-turbo", etc.';
1850
- placeholder = "gpt-4";
1851
- } else if (selectedProvider === "anthropic") {
1852
- screenTitle = "Claude Model Setup";
1853
- description = `Enter the Claude model name for ${modelTypeText}:`;
1854
- examples = 'For example: "claude-3-5-sonnet-latest", "claude-3-5-haiku-latest", etc.';
1855
- placeholder = "claude-3-5-sonnet-latest";
1856
- } else if (selectedProvider === "bigdream") {
1857
- screenTitle = "BigDream Model Setup";
1858
- description = `Enter the BigDream model name for ${modelTypeText}:`;
1859
- examples = 'For example: "claude-3-5-sonnet-latest", "claude-3-5-haiku-latest", etc.';
1860
- placeholder = "claude-3-5-sonnet-latest";
1861
- } else if (selectedProvider === "kimi") {
1862
- screenTitle = "Kimi Model Setup";
1863
- description = `Enter the Kimi model name for ${modelTypeText}:`;
1864
- examples = 'For example: "kimi-k2-0711-preview"';
1865
- placeholder = "kimi-k2-0711-preview";
1866
- } else if (selectedProvider === "deepseek") {
1867
- screenTitle = "DeepSeek Model Setup";
1868
- description = `Enter the DeepSeek model name for ${modelTypeText}:`;
1869
- examples = 'For example: "deepseek-chat", "deepseek-coder", "deepseek-reasoner", etc.';
1870
- placeholder = "deepseek-chat";
1871
- } else if (selectedProvider === "siliconflow") {
1872
- screenTitle = "SiliconFlow Model Setup";
1873
- description = `Enter the SiliconFlow model name for ${modelTypeText}:`;
1874
- examples = 'For example: "Qwen/Qwen2.5-72B-Instruct", "meta-llama/Meta-Llama-3.1-8B-Instruct", etc.';
1875
- placeholder = "Qwen/Qwen2.5-72B-Instruct";
1876
- } else if (selectedProvider === "qwen") {
1877
- screenTitle = "Qwen Model Setup";
1878
- description = `Enter the Qwen model name for ${modelTypeText}:`;
1879
- examples = 'For example: "qwen-plus", "qwen-turbo", "qwen-max", etc.';
1880
- placeholder = "qwen-plus";
1881
- } else if (selectedProvider === "glm") {
1882
- screenTitle = "GLM Model Setup";
1883
- description = `Enter the GLM model name for ${modelTypeText}:`;
1884
- examples = 'For example: "glm-4", "glm-4v", "glm-3-turbo", etc.';
1885
- placeholder = "glm-4";
1886
- } else if (selectedProvider === "minimax") {
1887
- screenTitle = "MiniMax Model Setup";
1888
- description = `Enter the MiniMax model name for ${modelTypeText}:`;
1889
- examples = 'For example: "abab6.5s-chat", "abab6.5g-chat", "abab5.5s-chat", etc.';
1890
- placeholder = "abab6.5s-chat";
1891
- } else if (selectedProvider === "baidu-qianfan") {
1892
- screenTitle = "Baidu Qianfan Model Setup";
1893
- description = `Enter the Baidu Qianfan model name for ${modelTypeText}:`;
1894
- examples = 'For example: "ERNIE-4.0-8K", "ERNIE-3.5-8K", "ERNIE-Speed-128K", etc.';
1895
- placeholder = "ERNIE-4.0-8K";
1896
- } else if (selectedProvider === "custom-openai") {
1897
- screenTitle = "Custom API Model Setup";
1898
- description = `Enter the model name for ${modelTypeText}:`;
1899
- examples = "Enter the exact model name as supported by your API endpoint.";
1900
- placeholder = "model-name";
1901
- }
1902
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1903
- Box,
1904
- {
1905
- flexDirection: "column",
1906
- gap: 1,
1907
- borderStyle: "round",
1908
- borderColor: theme.secondaryBorder,
1909
- paddingX: 2,
1910
- paddingY: 1
1719
+ const modelInputConfig = {
1720
+ azure: {
1721
+ title: "Azure Model Setup",
1722
+ placeholder: "gpt-4",
1723
+ examples: 'e.g. "gpt-4", "gpt-35-turbo"'
1911
1724
  },
1912
- /* @__PURE__ */ React.createElement(Text, { bold: true }, screenTitle, " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1913
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, description), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, selectedProvider === "azure" ? "This is the deployment name you configured in your Azure OpenAI resource." : selectedProvider === "anthropic" ? "This should be a valid Claude model identifier from Claude." : selectedProvider === "bigdream" ? "This should be a valid Claude model identifier supported by BigDream." : selectedProvider === "kimi" ? "This should be a valid Kimi model identifier from Moonshot AI." : selectedProvider === "deepseek" ? "This should be a valid DeepSeek model identifier." : selectedProvider === "siliconflow" ? "This should be a valid SiliconFlow model identifier." : selectedProvider === "qwen" ? "This should be a valid Qwen model identifier from Alibaba Cloud." : selectedProvider === "glm" ? "This should be a valid GLM model identifier from Zhipu AI." : selectedProvider === "minimax" ? "This should be a valid MiniMax model identifier." : selectedProvider === "baidu-qianfan" ? "This should be a valid Baidu Qianfan model identifier." : "This should match the model name supported by your API endpoint.", /* @__PURE__ */ React.createElement(Newline, null), examples)), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
1914
- TextInput,
1915
- {
1916
- placeholder,
1917
- value: customModelName,
1918
- onChange: setCustomModelName,
1919
- onSubmit: handleCustomModelSubmit,
1920
- columns: 100,
1921
- cursorOffset: customModelNameCursorOffset,
1922
- onChangeCursorOffset: setCustomModelNameCursorOffset,
1923
- showCursor: true
1924
- }
1925
- )), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(
1926
- Text,
1927
- {
1928
- color: customModelName ? theme.suggestion : SEMANTIC_COLORS.dim
1929
- },
1930
- "[Submit Model Name]"
1931
- ), /* @__PURE__ */ React.createElement(Text, null, " - Press Enter or click to continue"))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Enter"), " to continue or", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back")))
1932
- ));
1933
- }
1934
- if (currentScreen === "contextLength") {
1935
- const selectedOption = CONTEXT_LENGTH_OPTIONS.find((opt) => opt.value === contextLength) || CONTEXT_LENGTH_OPTIONS[2];
1936
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1937
- Box,
1938
- {
1939
- flexDirection: "column",
1940
- gap: 1,
1941
- borderStyle: "round",
1942
- borderColor: theme.secondaryBorder,
1943
- paddingX: 2,
1944
- paddingY: 1
1725
+ anthropic: {
1726
+ title: "Claude Model Setup",
1727
+ placeholder: "claude-3-5-sonnet-latest",
1728
+ examples: 'e.g. "claude-3-5-sonnet-latest", "claude-3-5-haiku-latest"'
1945
1729
  },
1946
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "Context Length Configuration", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1947
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Choose the context window length for your model:"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "This determines how much conversation history and context the model can process at once. Higher values allow for longer conversations but may increase costs.")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, CONTEXT_LENGTH_OPTIONS.map((option, index) => {
1948
- const isSelected = option.value === contextLength;
1949
- return /* @__PURE__ */ React.createElement(Box, { key: option.value, flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: isSelected ? "blue" : void 0 }, isSelected ? "\u2192 " : " ", option.label, option.value === DEFAULT_CONTEXT_LENGTH ? " (recommended)" : ""));
1950
- })), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Selected:", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, selectedOption.label))))
1951
- ), /* @__PURE__ */ React.createElement(Box, { marginLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "\u2191/\u2193 to select \xB7 Enter to continue \xB7 Esc to go back")));
1730
+ bigdream: {
1731
+ title: "BigDream Model Setup",
1732
+ placeholder: "claude-3-5-sonnet-latest",
1733
+ examples: 'e.g. "claude-3-5-sonnet-latest", "claude-3-5-haiku-latest"'
1734
+ },
1735
+ kimi: {
1736
+ title: "Kimi Model Setup",
1737
+ placeholder: "kimi-k2-0711-preview",
1738
+ examples: 'e.g. "kimi-k2-0711-preview"'
1739
+ },
1740
+ deepseek: {
1741
+ title: "DeepSeek Model Setup",
1742
+ placeholder: "deepseek-chat",
1743
+ examples: 'e.g. "deepseek-chat", "deepseek-coder", "deepseek-reasoner"'
1744
+ },
1745
+ siliconflow: {
1746
+ title: "SiliconFlow Model Setup",
1747
+ placeholder: "Qwen/Qwen2.5-72B-Instruct",
1748
+ examples: 'e.g. "Qwen/Qwen2.5-72B-Instruct"'
1749
+ },
1750
+ qwen: {
1751
+ title: "Qwen Model Setup",
1752
+ placeholder: "qwen-plus",
1753
+ examples: 'e.g. "qwen-plus", "qwen-turbo", "qwen-max"'
1754
+ },
1755
+ glm: {
1756
+ title: "GLM Model Setup",
1757
+ placeholder: "glm-4",
1758
+ examples: 'e.g. "glm-4", "glm-4v", "glm-3-turbo"'
1759
+ },
1760
+ minimax: {
1761
+ title: "MiniMax Model Setup",
1762
+ placeholder: "abab6.5s-chat",
1763
+ examples: 'e.g. "abab6.5s-chat", "abab6.5g-chat"'
1764
+ },
1765
+ "baidu-qianfan": {
1766
+ title: "Baidu Qianfan Model Setup",
1767
+ placeholder: "ERNIE-4.0-8K",
1768
+ examples: 'e.g. "ERNIE-4.0-8K", "ERNIE-3.5-8K"'
1769
+ },
1770
+ "custom-openai": {
1771
+ title: "Custom API Model Setup",
1772
+ placeholder: "model-name",
1773
+ examples: "Enter the exact model name supported by your API endpoint."
1774
+ }
1775
+ };
1776
+ const cfg = modelInputConfig[selectedProvider] || {
1777
+ title: t("modelSelector.modelInputTitle"),
1778
+ placeholder: "gpt-4",
1779
+ examples: 'e.g. "gpt-4", "gpt-3.5-turbo"'
1780
+ };
1781
+ return /* @__PURE__ */ React.createElement(
1782
+ BrandTextInput,
1783
+ {
1784
+ title: cfg.title,
1785
+ description: cfg.examples,
1786
+ placeholder: cfg.placeholder,
1787
+ value: customModelName,
1788
+ onChange: setCustomModelName,
1789
+ onSubmit: handleCustomModelSubmit,
1790
+ footerHint: t("modelSelector.footerEnterContinue"),
1791
+ cursorOffset: customModelNameCursorOffset,
1792
+ onChangeCursorOffset: setCustomModelNameCursorOffset
1793
+ }
1794
+ );
1952
1795
  }
1953
- if (currentScreen === "connectionTest") {
1954
- const providerDisplayName = getProviderLabel(selectedProvider, 0).split(
1955
- " ("
1956
- )[0];
1957
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1958
- Box,
1796
+ if (currentScreen === "contextLength") {
1797
+ const contextItems = CONTEXT_LENGTH_OPTIONS.map((opt) => ({
1798
+ id: opt.value.toString(),
1799
+ label: opt.value === DEFAULT_CONTEXT_LENGTH ? `${opt.label} (${t("modelSelector.recommended")})` : opt.label,
1800
+ isCurrent: opt.value === contextLength
1801
+ }));
1802
+ return /* @__PURE__ */ React.createElement(
1803
+ SimpleSelector,
1959
1804
  {
1960
- flexDirection: "column",
1961
- gap: 1,
1962
- borderStyle: "round",
1963
- borderColor: theme.secondaryBorder,
1964
- paddingX: 2,
1965
- paddingY: 1
1966
- },
1967
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "Connection Test", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1968
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Testing connection to ", providerDisplayName, "..."), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "This will verify your configuration by sending a test request to the API.", selectedProvider === "minimax" && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Newline, null), "For MiniMax, we'll test both v2 and v1 endpoints to find the best one."))), !connectionTestResult && !isTestingConnection && /* @__PURE__ */ React.createElement(Box, { marginY: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Press Enter"), " to start the connection test")), isTestingConnection && /* @__PURE__ */ React.createElement(Box, { marginY: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "\u{1F504} Testing connection...")), connectionTestResult && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1, paddingX: 1 }, /* @__PURE__ */ React.createElement(
1969
- Text,
1970
- {
1971
- color: connectionTestResult.success ? theme.success : "red"
1805
+ title: t("modelSelector.contextLengthTitle"),
1806
+ subtitle: t("modelSelector.contextLengthDesc"),
1807
+ items: contextItems,
1808
+ onSelect: (item) => {
1809
+ setContextLength(parseInt(item.id));
1810
+ handleContextLengthSubmit();
1972
1811
  },
1973
- connectionTestResult.message
1974
- ), connectionTestResult.endpoint && /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Endpoint: ", connectionTestResult.endpoint), connectionTestResult.details && /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Details: ", connectionTestResult.details), connectionTestResult.success ? /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "\u2705 Automatically proceeding to confirmation...")) : /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Press Enter"), " to retry test, or ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back"))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back to context length")))
1975
- ));
1812
+ onClose: handleBack
1813
+ }
1814
+ );
1976
1815
  }
1977
- if (currentScreen === "confirmation") {
1816
+ if (currentScreen === "testAndSave") {
1978
1817
  const providerDisplayName = getProviderLabel(selectedProvider, 0).split(
1979
1818
  " ("
1980
1819
  )[0];
1981
1820
  const showsApiKey = selectedProvider !== "ollama";
1982
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
1983
- Box,
1821
+ const contextLengthLabel = CONTEXT_LENGTH_OPTIONS.find((opt) => opt.value === contextLength)?.label || `${contextLength.toLocaleString()} tokens`;
1822
+ if (isEditMode) {
1823
+ const editItems = [];
1824
+ if (showsApiKey) {
1825
+ editItems.push({
1826
+ id: "__edit_apiKey__",
1827
+ label: "API Key",
1828
+ description: apiKey ? `****${apiKey.slice(-4)}` : "(not set)",
1829
+ category: t("modelSelector.testAndSaveDesc")
1830
+ });
1831
+ }
1832
+ if (selectedProvider === "custom-openai" || providerBaseUrl !== providers[selectedProvider]?.baseURL) {
1833
+ editItems.push({
1834
+ id: "__edit_baseUrl__",
1835
+ label: "Base URL",
1836
+ description: providerBaseUrl || customBaseUrl || "(not set)",
1837
+ category: t("modelSelector.testAndSaveDesc")
1838
+ });
1839
+ }
1840
+ editItems.push({
1841
+ id: "__edit_modelParams__",
1842
+ label: "Max Tokens",
1843
+ description: maxTokens,
1844
+ category: t("modelSelector.testAndSaveDesc")
1845
+ });
1846
+ editItems.push({
1847
+ id: "__edit_contextLength__",
1848
+ label: "Context Length",
1849
+ description: contextLengthLabel,
1850
+ category: t("modelSelector.testAndSaveDesc")
1851
+ });
1852
+ if (connectionTestResult) {
1853
+ editItems.push({
1854
+ id: "__test_status__",
1855
+ label: "Connection",
1856
+ description: connectionTestResult.message,
1857
+ statusIcon: connectionTestResult.success ? "\u2713" : "\u2715",
1858
+ statusColor: connectionTestResult.success ? SEMANTIC_COLORS.success : SEMANTIC_COLORS.error,
1859
+ category: t("modelSelector.testAndSaveDesc")
1860
+ });
1861
+ }
1862
+ editItems.push({
1863
+ id: "__save__",
1864
+ label: t("modelSelector.confirmationSave"),
1865
+ category: t("commands.model.actionsCategory"),
1866
+ statusIcon: "\u2713",
1867
+ statusColor: SEMANTIC_COLORS.success
1868
+ });
1869
+ editItems.push({
1870
+ id: "__test__",
1871
+ label: t("modelSelector.connectionTestRetry"),
1872
+ category: t("commands.model.actionsCategory")
1873
+ });
1874
+ const editTitle = t("commands.model.editTitle").replace(
1875
+ "{model}",
1876
+ editingModel?.name ?? selectedModel
1877
+ );
1878
+ const editOverlay = isTestingConnection ? {
1879
+ type: "loading",
1880
+ message: t("modelSelector.autoTesting")
1881
+ } : validationError ? { type: "error", message: validationError } : null;
1882
+ return /* @__PURE__ */ React.createElement(
1883
+ SimpleSelector,
1884
+ {
1885
+ title: editTitle,
1886
+ items: editItems,
1887
+ groupByCategory: true,
1888
+ onSelect: (item) => {
1889
+ if (item.id === "__edit_apiKey__") {
1890
+ navigateTo("apiKey");
1891
+ } else if (item.id === "__edit_baseUrl__") {
1892
+ navigateTo("baseUrl");
1893
+ } else if (item.id === "__edit_modelParams__") {
1894
+ setActiveFieldIndex(0);
1895
+ navigateTo("modelParams");
1896
+ } else if (item.id === "__edit_contextLength__") {
1897
+ navigateTo("contextLength");
1898
+ } else if (item.id === "__save__") {
1899
+ handleConfirmation().catch((error) => {
1900
+ setValidationError(
1901
+ error instanceof Error ? error.message : "Unexpected error occurred"
1902
+ );
1903
+ });
1904
+ } else if (item.id === "__test__") {
1905
+ setConnectionTestResult(null);
1906
+ handleConnectionTest();
1907
+ }
1908
+ },
1909
+ onClose: handleBack,
1910
+ statusOverlay: editOverlay
1911
+ }
1912
+ );
1913
+ }
1914
+ const configItems = [
1915
+ { label: "Provider", value: providerDisplayName },
1916
+ ...selectedProvider === "azure" ? [{ label: "Resource Name", value: resourceName }] : [],
1917
+ ...selectedProvider === "ollama" ? [{ label: "Server URL", value: ollamaBaseUrl }] : [],
1918
+ ...selectedProvider === "custom-openai" ? [{ label: "API Base URL", value: customBaseUrl }] : [],
1919
+ { label: "Model", value: selectedModel },
1920
+ ...apiKey && showsApiKey ? [{ label: "API Key", value: `****${apiKey.slice(-4)}` }] : [],
1921
+ ...maxTokens ? [{ label: "Max Tokens", value: maxTokens }] : [],
1922
+ { label: "Context Length", value: contextLengthLabel },
1923
+ ...supportsReasoningEffort ? [{ label: "Reasoning Effort", value: reasoningEffort }] : []
1924
+ ];
1925
+ if (connectionTestResult) {
1926
+ configItems.push({
1927
+ label: "Connection",
1928
+ value: connectionTestResult.message,
1929
+ valueColor: connectionTestResult.success ? SEMANTIC_COLORS.success : SEMANTIC_COLORS.error
1930
+ });
1931
+ if (connectionTestResult.details && !connectionTestResult.success) {
1932
+ configItems.push({
1933
+ label: "Details",
1934
+ value: connectionTestResult.details
1935
+ });
1936
+ }
1937
+ }
1938
+ const testAndSaveSections = [
1984
1939
  {
1985
- flexDirection: "column",
1986
- gap: 1,
1987
- borderStyle: "round",
1988
- borderColor: theme.secondaryBorder,
1989
- paddingX: 2,
1990
- paddingY: 1
1991
- },
1992
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "Configuration Confirmation", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1993
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Confirm your model configuration:"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Please review your selections before saving.")), validationError && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1, paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.error, bold: true }, "\u26A0 Configuration Error:"), /* @__PURE__ */ React.createElement(Text, { color: theme.error }, validationError)), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginY: 1, paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Provider: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, providerDisplayName)), selectedProvider === "azure" && /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Resource Name: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, resourceName)), selectedProvider === "ollama" && /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Server URL: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, ollamaBaseUrl)), selectedProvider === "custom-openai" && /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "API Base URL: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, customBaseUrl)), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Model: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, selectedModel)), apiKey && showsApiKey && /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "API Key: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "****", apiKey.slice(-4))), maxTokens && /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Max Tokens: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, maxTokens)), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Context Length: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, CONTEXT_LENGTH_OPTIONS.find(
1994
- (opt) => opt.value === contextLength
1995
- )?.label || `${contextLength.toLocaleString()} tokens`)), supportsReasoningEffort && /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Reasoning Effort: "), /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, reasoningEffort))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back to model parameters or ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Enter"), " ", "to save configuration")))
1996
- ));
1940
+ title: t("modelSelector.testAndSaveDesc"),
1941
+ items: configItems
1942
+ }
1943
+ ];
1944
+ if (validationError) {
1945
+ testAndSaveSections.unshift({
1946
+ title: t("modelSelector.configError"),
1947
+ items: [
1948
+ {
1949
+ label: "Error",
1950
+ value: validationError,
1951
+ valueColor: SEMANTIC_COLORS.error
1952
+ }
1953
+ ]
1954
+ });
1955
+ }
1956
+ const testAndSaveActions = [];
1957
+ if (connectionTestResult?.success) {
1958
+ testAndSaveActions.push({
1959
+ key: "return",
1960
+ keyLabel: "Enter",
1961
+ description: t("modelSelector.confirmationSave"),
1962
+ onPress: () => {
1963
+ handleConfirmation().catch((error) => {
1964
+ setValidationError(
1965
+ error instanceof Error ? error.message : "Unexpected error occurred"
1966
+ );
1967
+ });
1968
+ }
1969
+ });
1970
+ } else if (connectionTestResult && !connectionTestResult.success) {
1971
+ testAndSaveActions.push({
1972
+ key: "return",
1973
+ keyLabel: "Enter",
1974
+ description: t("modelSelector.connectionTestRetry"),
1975
+ onPress: () => {
1976
+ setConnectionTestResult(null);
1977
+ handleConnectionTest();
1978
+ }
1979
+ });
1980
+ testAndSaveActions.push({
1981
+ key: "s",
1982
+ keyLabel: "s",
1983
+ description: t("modelSelector.saveAnyway"),
1984
+ onPress: () => {
1985
+ handleConfirmation().catch((error) => {
1986
+ setValidationError(
1987
+ error instanceof Error ? error.message : "Unexpected error occurred"
1988
+ );
1989
+ });
1990
+ }
1991
+ });
1992
+ }
1993
+ const testAndSaveOverlay = isTestingConnection ? {
1994
+ type: "loading",
1995
+ message: t("modelSelector.autoTesting")
1996
+ } : null;
1997
+ return /* @__PURE__ */ React.createElement(
1998
+ InfoPanel,
1999
+ {
2000
+ title: t("modelSelector.testAndSaveTitle"),
2001
+ sections: testAndSaveSections,
2002
+ actions: testAndSaveActions,
2003
+ onClose: handleBack,
2004
+ statusOverlay: testAndSaveOverlay
2005
+ }
2006
+ );
1997
2007
  }
1998
2008
  if (currentScreen === "anthropicSubMenu") {
1999
- const anthropicOptions = [
2000
- { label: "Official Anthropic API", value: "official" },
2001
- { label: "BigDream (Community Proxy)", value: "bigdream" },
2002
- { label: "OpenDev (Community Proxy)", value: "opendev" },
2003
- { label: "Custom Anthropic-Compatible API", value: "custom" }
2004
- ];
2005
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
2006
- Box,
2009
+ const anthropicItems = [
2007
2010
  {
2008
- flexDirection: "column",
2009
- gap: 1,
2010
- borderStyle: "round",
2011
- borderColor: theme.secondaryBorder,
2012
- paddingX: 2,
2013
- paddingY: 1
2011
+ id: "official",
2012
+ label: t("modelSelector.anthropicOfficial"),
2013
+ description: t("modelSelector.anthropicOfficialDesc")
2014
2014
  },
2015
- /* @__PURE__ */ React.createElement(Text, { bold: true }, "Claude Provider Selection", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
2016
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Choose your Anthropic API access method for this model profile:"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "\u2022 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "Official Anthropic API:"), " Direct access to Anthropic's official API", /* @__PURE__ */ React.createElement(Newline, null), "\u2022 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "BigDream:"), " Community proxy providing Claude access", /* @__PURE__ */ React.createElement(Newline, null), "\u2022 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "Custom:"), " Your own Anthropic-compatible API endpoint")), /* @__PURE__ */ React.createElement(
2017
- Select,
2018
- {
2019
- options: anthropicOptions,
2020
- onChange: handleAnthropicProviderSelection
2021
- }
2022
- ), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "Esc"), " to go back to provider selection")))
2023
- ));
2015
+ {
2016
+ id: "bigdream",
2017
+ label: t("modelSelector.anthropicBigdream"),
2018
+ description: t("modelSelector.anthropicBigdreamDesc")
2019
+ },
2020
+ {
2021
+ id: "opendev",
2022
+ label: t("modelSelector.anthropicOpendev"),
2023
+ description: t("modelSelector.anthropicOpendevDesc")
2024
+ },
2025
+ {
2026
+ id: "custom",
2027
+ label: t("modelSelector.anthropicCustom"),
2028
+ description: t("modelSelector.anthropicCustomDesc")
2029
+ }
2030
+ ];
2031
+ return /* @__PURE__ */ React.createElement(
2032
+ SimpleSelector,
2033
+ {
2034
+ title: t("modelSelector.anthropicSubMenu"),
2035
+ subtitle: t("modelSelector.anthropicSubMenuDesc"),
2036
+ items: anthropicItems,
2037
+ onSelect: (item) => handleAnthropicProviderSelection(
2038
+ item.id
2039
+ ),
2040
+ onClose: handleBack
2041
+ }
2042
+ );
2024
2043
  }
2044
+ const providerSelectorItems = providerOptions.map((opt) => ({
2045
+ id: opt.value,
2046
+ label: opt.label
2047
+ }));
2025
2048
  return /* @__PURE__ */ React.createElement(
2026
- ScreenContainer,
2049
+ SimpleSelector,
2027
2050
  {
2028
- title: "Provider Selection",
2029
- exitState,
2030
- children: /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Select your preferred AI provider for this model profile:"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Choose the provider you want to use for this model profile.", /* @__PURE__ */ React.createElement(Newline, null), "This will determine which models are available to you.")), /* @__PURE__ */ React.createElement(
2031
- Select,
2032
- {
2033
- options: providerOptions,
2034
- onChange: handleProviderSelection
2051
+ title: t("modelSelector.providerSelection"),
2052
+ subtitle: t("modelSelector.providerSelectionDesc"),
2053
+ items: providerSelectorItems,
2054
+ onSelect: (item) => handleProviderSelection(item.id),
2055
+ onClose: () => {
2056
+ if (onCancel) {
2057
+ onCancel();
2058
+ } else {
2059
+ onDone();
2035
2060
  }
2036
- ), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "You can change this later by running", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.suggestion }, "/model"), " again")))
2061
+ }
2037
2062
  }
2038
2063
  );
2039
2064
  }