@shareai-lab/kode 2.0.1 → 2.0.3

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 (343) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +649 -25
  3. package/README.zh-CN.md +579 -0
  4. package/cli-acp.js +3 -17
  5. package/cli.js +5 -7
  6. package/dist/chunks/Doctor-M3J7GRTJ.js +12 -0
  7. package/dist/chunks/LogList-ISWZ6DDD.js +121 -0
  8. package/dist/chunks/LogList-ISWZ6DDD.js.map +7 -0
  9. package/dist/chunks/REPL-RQ6LO6S7.js +56 -0
  10. package/dist/chunks/ResumeConversation-6DMVBEGH.js +56 -0
  11. package/dist/chunks/agentLoader-FCRG3TFJ.js +31 -0
  12. package/dist/{agentsValidate-7LH4HTNR.js → chunks/agentsValidate-PEWMYN4Q.js} +97 -69
  13. package/dist/chunks/agentsValidate-PEWMYN4Q.js.map +7 -0
  14. package/dist/{ask-3NHFFUQG.js → chunks/ask-D7SOHJ6Z.js} +36 -44
  15. package/dist/chunks/ask-D7SOHJ6Z.js.map +7 -0
  16. package/dist/chunks/autoUpdater-CNESBOKO.js +19 -0
  17. package/dist/{chunk-AFFSCMYS.js → chunks/chunk-2JN5MY67.js} +12 -14
  18. package/dist/chunks/chunk-2JN5MY67.js.map +7 -0
  19. package/dist/chunks/chunk-2QONJ5MG.js +14 -0
  20. package/dist/chunks/chunk-2QONJ5MG.js.map +7 -0
  21. package/dist/chunks/chunk-2WEXPKHH.js +903 -0
  22. package/dist/chunks/chunk-2WEXPKHH.js.map +7 -0
  23. package/dist/{chunk-ARZSBOAO.js → chunks/chunk-3BYE3ME6.js} +717 -792
  24. package/dist/chunks/chunk-3BYE3ME6.js.map +7 -0
  25. package/dist/chunks/chunk-3JDNWX7W.js +1264 -0
  26. package/dist/chunks/chunk-3JDNWX7W.js.map +7 -0
  27. package/dist/chunks/chunk-3OEJVB5A.js +906 -0
  28. package/dist/chunks/chunk-3OEJVB5A.js.map +7 -0
  29. package/dist/chunks/chunk-3TNIOEBO.js +369 -0
  30. package/dist/chunks/chunk-3TNIOEBO.js.map +7 -0
  31. package/dist/chunks/chunk-4A46ZXMJ.js +67 -0
  32. package/dist/chunks/chunk-4A46ZXMJ.js.map +7 -0
  33. package/dist/{chunk-UHYRLID6.js → chunks/chunk-4ATBQOFO.js} +107 -55
  34. package/dist/chunks/chunk-4ATBQOFO.js.map +7 -0
  35. package/dist/chunks/chunk-4CRUCZR4.js +0 -0
  36. package/dist/{chunk-YC6LJCDE.js → chunks/chunk-4EO6SIQY.js} +32 -75
  37. package/dist/chunks/chunk-4EO6SIQY.js.map +7 -0
  38. package/dist/chunks/chunk-53M46S5I.js +64 -0
  39. package/dist/chunks/chunk-53M46S5I.js.map +7 -0
  40. package/dist/{chunk-JC6NCUG5.js → chunks/chunk-54KOYG5C.js} +0 -2
  41. package/dist/{chunk-EZXMVTDU.js → chunks/chunk-6BAS4WY6.js} +29 -45
  42. package/dist/chunks/chunk-6BAS4WY6.js.map +7 -0
  43. package/dist/{chunk-3IN27HA5.js → chunks/chunk-6KRRFSDN.js} +4 -6
  44. package/dist/chunks/chunk-6KRRFSDN.js.map +7 -0
  45. package/dist/chunks/chunk-6LJNZK4K.js +39 -0
  46. package/dist/chunks/chunk-6LJNZK4K.js.map +7 -0
  47. package/dist/chunks/chunk-6ZWEOSEI.js +666 -0
  48. package/dist/chunks/chunk-6ZWEOSEI.js.map +7 -0
  49. package/dist/chunks/chunk-77XDJMBP.js +3326 -0
  50. package/dist/chunks/chunk-77XDJMBP.js.map +7 -0
  51. package/dist/chunks/chunk-7RRW4NTB.js +6454 -0
  52. package/dist/chunks/chunk-7RRW4NTB.js.map +7 -0
  53. package/dist/chunks/chunk-7X3TW4JB.js +4520 -0
  54. package/dist/chunks/chunk-7X3TW4JB.js.map +7 -0
  55. package/dist/chunks/chunk-B3MW3YGY.js +1409 -0
  56. package/dist/chunks/chunk-B3MW3YGY.js.map +7 -0
  57. package/dist/chunks/chunk-BBJFHTBC.js +28 -0
  58. package/dist/chunks/chunk-BBJFHTBC.js.map +7 -0
  59. package/dist/chunks/chunk-BHDHXOXB.js +24 -0
  60. package/dist/chunks/chunk-BHDHXOXB.js.map +7 -0
  61. package/dist/{chunk-73WGVYLQ.js → chunks/chunk-BTA7SZ26.js} +152 -223
  62. package/dist/chunks/chunk-BTA7SZ26.js.map +7 -0
  63. package/dist/chunks/chunk-CDGRYGPZ.js +103 -0
  64. package/dist/chunks/chunk-CDGRYGPZ.js.map +7 -0
  65. package/dist/{chunk-S6HRABTA.js → chunks/chunk-CP6E5UG6.js} +1 -4
  66. package/dist/chunks/chunk-CP6E5UG6.js.map +7 -0
  67. package/dist/{chunk-QVLYOPO5.js → chunks/chunk-DQ4JHXMT.js} +462 -424
  68. package/dist/chunks/chunk-DQ4JHXMT.js.map +7 -0
  69. package/dist/chunks/chunk-DXD76CMV.js +208 -0
  70. package/dist/chunks/chunk-DXD76CMV.js.map +7 -0
  71. package/dist/chunks/chunk-GCQCAXJZ.js +0 -0
  72. package/dist/chunks/chunk-GELCZWMB.js +42 -0
  73. package/dist/chunks/chunk-GELCZWMB.js.map +7 -0
  74. package/dist/{chunk-K2CWOTI2.js → chunks/chunk-HJYOH4HC.js} +23 -18
  75. package/dist/chunks/chunk-HJYOH4HC.js.map +7 -0
  76. package/dist/chunks/chunk-HPYNW6TT.js +744 -0
  77. package/dist/chunks/chunk-HPYNW6TT.js.map +7 -0
  78. package/dist/{chunk-RZWOUA25.js → chunks/chunk-HRJ3ICQK.js} +59 -55
  79. package/dist/chunks/chunk-HRJ3ICQK.js.map +7 -0
  80. package/dist/{chunk-DZE5YA7L.js → chunks/chunk-IFCIADS3.js} +571 -573
  81. package/dist/chunks/chunk-IFCIADS3.js.map +7 -0
  82. package/dist/chunks/chunk-IN7XZ7BC.js +27 -0
  83. package/dist/chunks/chunk-IN7XZ7BC.js.map +7 -0
  84. package/dist/chunks/chunk-L7P4M4KW.js +193 -0
  85. package/dist/chunks/chunk-L7P4M4KW.js.map +7 -0
  86. package/dist/chunks/chunk-LB6TCPDI.js +0 -0
  87. package/dist/{chunk-3RUXVV4S.js → chunks/chunk-LOCXPQNJ.js} +1 -4
  88. package/dist/{chunk-3RUXVV4S.js.map → chunks/chunk-LOCXPQNJ.js.map} +2 -2
  89. package/dist/{chunk-7M2YN6TU.js → chunks/chunk-LOD5ZHCI.js} +213 -208
  90. package/dist/chunks/chunk-LOD5ZHCI.js.map +7 -0
  91. package/dist/{chunk-S3J2TLV6.js → chunks/chunk-M7P3QNRU.js} +1 -4
  92. package/dist/{chunk-S3J2TLV6.js.map → chunks/chunk-M7P3QNRU.js.map} +2 -2
  93. package/dist/chunks/chunk-PPHLQVL7.js +4234 -0
  94. package/dist/chunks/chunk-PPHLQVL7.js.map +7 -0
  95. package/dist/{chunk-ABLVTESJ.js → chunks/chunk-QAXE37B5.js} +1 -4
  96. package/dist/chunks/chunk-QAXE37B5.js.map +7 -0
  97. package/dist/chunks/chunk-QHQOBUF6.js +60 -0
  98. package/dist/chunks/chunk-QHQOBUF6.js.map +7 -0
  99. package/dist/{chunk-W7GRKO7Q.js → chunks/chunk-RPJXO7GG.js} +241 -214
  100. package/dist/chunks/chunk-RPJXO7GG.js.map +7 -0
  101. package/dist/{chunk-NPFOMITO.js → chunks/chunk-SWQV4KSY.js} +1 -4
  102. package/dist/{chunk-NPFOMITO.js.map → chunks/chunk-SWQV4KSY.js.map} +2 -2
  103. package/dist/chunks/chunk-SZLAPULP.js +28 -0
  104. package/dist/chunks/chunk-SZLAPULP.js.map +7 -0
  105. package/dist/{chunk-7U7L4NMD.js → chunks/chunk-T7RB5V5J.js} +23 -25
  106. package/dist/chunks/chunk-T7RB5V5J.js.map +7 -0
  107. package/dist/{chunk-HN4E4UUQ.js → chunks/chunk-TI2CTTMA.js} +25 -17
  108. package/dist/chunks/chunk-TI2CTTMA.js.map +7 -0
  109. package/dist/{chunk-ZVDRDPII.js → chunks/chunk-TNGVRTO5.js} +45 -20
  110. package/dist/chunks/chunk-TNGVRTO5.js.map +7 -0
  111. package/dist/chunks/chunk-TNWB3U5Y.js +2077 -0
  112. package/dist/chunks/chunk-TNWB3U5Y.js.map +7 -0
  113. package/dist/chunks/chunk-U2IHWPCU.js +12 -0
  114. package/dist/chunks/chunk-U2IHWPCU.js.map +7 -0
  115. package/dist/{chunk-KAA5BGMQ.js → chunks/chunk-UNOY3VJ2.js} +1 -4
  116. package/dist/{chunk-KAA5BGMQ.js.map → chunks/chunk-UNOY3VJ2.js.map} +2 -2
  117. package/dist/{chunk-MWRSY4X6.js → chunks/chunk-UVDJL6ZZ.js} +97 -58
  118. package/dist/chunks/chunk-UVDJL6ZZ.js.map +7 -0
  119. package/dist/chunks/chunk-VNCW4C2Z.js +13452 -0
  120. package/dist/chunks/chunk-VNCW4C2Z.js.map +7 -0
  121. package/dist/chunks/chunk-W5EGGA44.js +15 -0
  122. package/dist/chunks/chunk-W5EGGA44.js.map +7 -0
  123. package/dist/chunks/chunk-XR2W3MAM.js +1533 -0
  124. package/dist/chunks/chunk-XR2W3MAM.js.map +7 -0
  125. package/dist/{chunk-STSX7GIX.js → chunks/chunk-YIO5EBMQ.js} +423 -377
  126. package/dist/chunks/chunk-YIO5EBMQ.js.map +7 -0
  127. package/dist/chunks/chunk-ZBVLKZ5V.js +1062 -0
  128. package/dist/chunks/chunk-ZBVLKZ5V.js.map +7 -0
  129. package/dist/{chunk-E6YNABER.js → chunks/chunk-ZCLTZIVP.js} +1 -4
  130. package/dist/chunks/chunk-ZCLTZIVP.js.map +7 -0
  131. package/dist/chunks/client-SILZNM5N.js +42 -0
  132. package/dist/{config-RUSD6G5Y.js → chunks/config-25HRTPSP.js} +48 -10
  133. package/dist/chunks/cost-tracker-Z2UZT2J5.js +28 -0
  134. package/dist/{customCommands-TOIJFZAL.js → chunks/customCommands-TYMYZRG5.js} +11 -8
  135. package/dist/chunks/engine-MRVF6FK6.js +39 -0
  136. package/dist/{env-XGKBLU3D.js → chunks/env-TJ5NOBEB.js} +7 -5
  137. package/dist/{kodeAgentSessionId-X6XWQW7B.js → chunks/kodeAgentSessionId-VTNISJ2L.js} +2 -4
  138. package/dist/chunks/kodeAgentSessionLoad-YB2RKBGJ.js +15 -0
  139. package/dist/chunks/kodeAgentSessionResume-DZSIVKVA.js +13 -0
  140. package/dist/chunks/kodeAgentStreamJson-X5PLS2S6.js +11 -0
  141. package/dist/{kodeAgentStreamJsonSession-UGEZJJEB.js → chunks/kodeAgentStreamJsonSession-RDXM4XYF.js} +38 -24
  142. package/dist/chunks/kodeAgentStreamJsonSession-RDXM4XYF.js.map +7 -0
  143. package/dist/{chunk-4RTX4AG4.js → chunks/kodeAgentStructuredStdio-SVGDSB4P.js} +14 -9
  144. package/dist/chunks/kodeAgentStructuredStdio-SVGDSB4P.js.map +7 -0
  145. package/dist/{kodeHooks-QWM36A3D.js → chunks/kodeHooks-RVKYRJHG.js} +11 -9
  146. package/dist/{llm-ZUQC4WYM.js → chunks/llm-62N6T5ZT.js} +1734 -1526
  147. package/dist/chunks/llm-62N6T5ZT.js.map +7 -0
  148. package/dist/chunks/llmLazy-ZUSSE3ZA.js +13 -0
  149. package/dist/{mentionProcessor-EE3XFHCJ.js → chunks/mentionProcessor-RJW5UPJD.js} +46 -16
  150. package/dist/chunks/mentionProcessor-RJW5UPJD.js.map +7 -0
  151. package/dist/{messages-EOYQKPGM.js → chunks/messages-EEWWLPHN.js} +2 -6
  152. package/dist/chunks/model-5TIEKQPD.js +37 -0
  153. package/dist/{openai-RRCWW33N.js → chunks/openai-XXK3YZG4.js} +13 -10
  154. package/dist/{outputStyles-62Q3VH2J.js → chunks/outputStyles-FAJTXN2A.js} +6 -9
  155. package/dist/chunks/permissions-HO7INPWM.js +27 -0
  156. package/dist/{pluginRuntime-6ETCZ2LL.js → chunks/pluginRuntime-C7K5ULK2.js} +31 -48
  157. package/dist/chunks/pluginRuntime-C7K5ULK2.js.map +7 -0
  158. package/dist/chunks/pluginValidation-DAM7WRTC.js +20 -0
  159. package/dist/chunks/registry-XYJXMOA5.js +60 -0
  160. package/dist/chunks/responsesStreaming-JNGE2P3D.js +8 -0
  161. package/dist/chunks/runNonTextPrintMode-SVBLCZQX.js +577 -0
  162. package/dist/chunks/runNonTextPrintMode-SVBLCZQX.js.map +7 -0
  163. package/dist/chunks/server-REXXF5IK.js +46 -0
  164. package/dist/{skillMarketplace-3RXQBVOL.js → chunks/skillMarketplace-N4HVHNST.js} +8 -6
  165. package/dist/chunks/src-OROQIWP3.js +44 -0
  166. package/dist/chunks/src-QXLGGMUW.js +1647 -0
  167. package/dist/chunks/src-QXLGGMUW.js.map +7 -0
  168. package/dist/{cli-DOPVY2CW.js → chunks/src-SSDT6MVP.js} +2659 -3384
  169. package/dist/chunks/src-SSDT6MVP.js.map +7 -0
  170. package/dist/chunks/theme-YBJUIMWK.js +10 -0
  171. package/dist/{toolPermissionContext-65L65VEZ.js → chunks/toolPermissionContext-MOCTRR7N.js} +2 -4
  172. package/dist/chunks/toolPermissionSettings-EV2EJAXL.js +18 -0
  173. package/dist/chunks/toolPermissionSettings-EV2EJAXL.js.map +7 -0
  174. package/dist/chunks/uuid-6577SO6X.js +7 -0
  175. package/dist/chunks/uuid-6577SO6X.js.map +7 -0
  176. package/dist/chunks/webOnlyMode-ALXX7UQY.js +66 -0
  177. package/dist/chunks/webOnlyMode-ALXX7UQY.js.map +7 -0
  178. package/dist/entrypoints/cli.js +10 -0
  179. package/dist/entrypoints/cli.js.map +7 -0
  180. package/dist/entrypoints/daemon.js +10 -0
  181. package/dist/entrypoints/daemon.js.map +7 -0
  182. package/dist/entrypoints/mcp.js +71 -0
  183. package/dist/entrypoints/mcp.js.map +7 -0
  184. package/dist/index.js +6 -7
  185. package/dist/index.js.map +3 -3
  186. package/dist/sdk/client.cjs +391 -0
  187. package/dist/sdk/client.cjs.map +7 -0
  188. package/dist/sdk/client.js +364 -0
  189. package/dist/sdk/client.js.map +7 -0
  190. package/dist/sdk/core.cjs +19932 -0
  191. package/dist/sdk/core.cjs.map +7 -0
  192. package/dist/sdk/core.js +19893 -0
  193. package/dist/sdk/core.js.map +7 -0
  194. package/dist/sdk/daemon-client.cjs +257 -0
  195. package/dist/sdk/daemon-client.cjs.map +7 -0
  196. package/dist/sdk/daemon-client.js +221 -0
  197. package/dist/sdk/daemon-client.js.map +7 -0
  198. package/dist/sdk/protocol.cjs +170 -0
  199. package/dist/sdk/protocol.cjs.map +7 -0
  200. package/dist/sdk/protocol.js +140 -0
  201. package/dist/sdk/protocol.js.map +7 -0
  202. package/dist/sdk/runtime-node.cjs +236 -0
  203. package/dist/sdk/runtime-node.cjs.map +7 -0
  204. package/dist/sdk/runtime-node.js +222 -0
  205. package/dist/sdk/runtime-node.js.map +7 -0
  206. package/dist/sdk/runtime.cjs +17 -0
  207. package/dist/sdk/runtime.cjs.map +7 -0
  208. package/dist/sdk/runtime.js +0 -0
  209. package/dist/sdk/runtime.js.map +7 -0
  210. package/dist/sdk/tools.cjs +30300 -0
  211. package/dist/sdk/tools.cjs.map +7 -0
  212. package/dist/sdk/tools.js +30282 -0
  213. package/dist/sdk/tools.js.map +7 -0
  214. package/dist/webui/assets/index-5hlfByVS.css +1 -0
  215. package/dist/webui/assets/index-BR9lm1lA.js +82 -0
  216. package/dist/webui/index.html +28 -0
  217. package/package.json +93 -22
  218. package/scripts/binary-utils.cjs +12 -4
  219. package/scripts/cli-acp-wrapper.cjs +3 -17
  220. package/scripts/cli-wrapper.cjs +5 -7
  221. package/scripts/postinstall.js +8 -4
  222. package/dist/REPL-CW7AYLVL.js +0 -42
  223. package/dist/acp-VEPJ74LT.js +0 -1357
  224. package/dist/acp-VEPJ74LT.js.map +0 -7
  225. package/dist/agentsValidate-7LH4HTNR.js.map +0 -7
  226. package/dist/ask-3NHFFUQG.js.map +0 -7
  227. package/dist/autoUpdater-ITPIHCOI.js +0 -17
  228. package/dist/chunk-3IN27HA5.js.map +0 -7
  229. package/dist/chunk-4FX3IVPT.js +0 -164
  230. package/dist/chunk-4FX3IVPT.js.map +0 -7
  231. package/dist/chunk-4RTX4AG4.js.map +0 -7
  232. package/dist/chunk-5PDP7R6N.js +0 -515
  233. package/dist/chunk-5PDP7R6N.js.map +0 -7
  234. package/dist/chunk-73WGVYLQ.js.map +0 -7
  235. package/dist/chunk-7M2YN6TU.js.map +0 -7
  236. package/dist/chunk-7U7L4NMD.js.map +0 -7
  237. package/dist/chunk-ABLVTESJ.js.map +0 -7
  238. package/dist/chunk-AFFSCMYS.js.map +0 -7
  239. package/dist/chunk-ARZSBOAO.js.map +0 -7
  240. package/dist/chunk-CIG63V4E.js +0 -72
  241. package/dist/chunk-CIG63V4E.js.map +0 -7
  242. package/dist/chunk-CM3EGTG6.js +0 -1609
  243. package/dist/chunk-CM3EGTG6.js.map +0 -7
  244. package/dist/chunk-DZE5YA7L.js.map +0 -7
  245. package/dist/chunk-E6YNABER.js.map +0 -7
  246. package/dist/chunk-EZXMVTDU.js.map +0 -7
  247. package/dist/chunk-F2SJXUDI.js +0 -148
  248. package/dist/chunk-F2SJXUDI.js.map +0 -7
  249. package/dist/chunk-FC5ZCKBI.js +0 -30167
  250. package/dist/chunk-FC5ZCKBI.js.map +0 -7
  251. package/dist/chunk-HCBELH4J.js +0 -145
  252. package/dist/chunk-HCBELH4J.js.map +0 -7
  253. package/dist/chunk-HN4E4UUQ.js.map +0 -7
  254. package/dist/chunk-IZVMU4S2.js +0 -654
  255. package/dist/chunk-IZVMU4S2.js.map +0 -7
  256. package/dist/chunk-K2CWOTI2.js.map +0 -7
  257. package/dist/chunk-LC4TVOCZ.js +0 -835
  258. package/dist/chunk-LC4TVOCZ.js.map +0 -7
  259. package/dist/chunk-MIW7N2MY.js +0 -2613
  260. package/dist/chunk-MIW7N2MY.js.map +0 -7
  261. package/dist/chunk-MWRSY4X6.js.map +0 -7
  262. package/dist/chunk-ND3XWFO6.js +0 -34
  263. package/dist/chunk-ND3XWFO6.js.map +0 -7
  264. package/dist/chunk-QVLYOPO5.js.map +0 -7
  265. package/dist/chunk-RZWOUA25.js.map +0 -7
  266. package/dist/chunk-S6HRABTA.js.map +0 -7
  267. package/dist/chunk-STSX7GIX.js.map +0 -7
  268. package/dist/chunk-UHYRLID6.js.map +0 -7
  269. package/dist/chunk-UKHTVRJM.js +0 -47
  270. package/dist/chunk-UKHTVRJM.js.map +0 -7
  271. package/dist/chunk-UYXEDKOZ.js +0 -24
  272. package/dist/chunk-UYXEDKOZ.js.map +0 -7
  273. package/dist/chunk-W7GRKO7Q.js.map +0 -7
  274. package/dist/chunk-WVHORZQ5.js +0 -17
  275. package/dist/chunk-WVHORZQ5.js.map +0 -7
  276. package/dist/chunk-WWUWDNWW.js +0 -49
  277. package/dist/chunk-WWUWDNWW.js.map +0 -7
  278. package/dist/chunk-YC6LJCDE.js.map +0 -7
  279. package/dist/chunk-YXYYDIMI.js +0 -2931
  280. package/dist/chunk-YXYYDIMI.js.map +0 -7
  281. package/dist/chunk-ZVDRDPII.js.map +0 -7
  282. package/dist/cli-DOPVY2CW.js.map +0 -7
  283. package/dist/commands-2BF2CJ3A.js +0 -46
  284. package/dist/context-6FXPETYH.js +0 -30
  285. package/dist/costTracker-6SL26FDB.js +0 -19
  286. package/dist/kodeAgentSessionLoad-MITZADPB.js +0 -18
  287. package/dist/kodeAgentSessionResume-GVRWB4WO.js +0 -16
  288. package/dist/kodeAgentStreamJson-NXFN7TXH.js +0 -13
  289. package/dist/kodeAgentStreamJsonSession-UGEZJJEB.js.map +0 -7
  290. package/dist/kodeAgentStructuredStdio-HGWJT7CU.js +0 -10
  291. package/dist/llm-ZUQC4WYM.js.map +0 -7
  292. package/dist/llmLazy-54QQHA54.js +0 -15
  293. package/dist/loader-FYHJQES5.js +0 -28
  294. package/dist/mcp-J332IKT3.js +0 -49
  295. package/dist/mentionProcessor-EE3XFHCJ.js.map +0 -7
  296. package/dist/model-FV3JDJKH.js +0 -30
  297. package/dist/pluginRuntime-6ETCZ2LL.js.map +0 -7
  298. package/dist/pluginValidation-I4YKUWGS.js +0 -17
  299. package/dist/prompts-ZLEKDD77.js +0 -48
  300. package/dist/query-VFRJPBGD.js +0 -50
  301. package/dist/responsesStreaming-AW344PQO.js +0 -10
  302. package/dist/ripgrep-3NTIKQYW.js +0 -17
  303. package/dist/state-P5G6CO5V.js +0 -16
  304. package/dist/theme-3LWP3BG7.js +0 -14
  305. package/dist/toolPermissionSettings-3ROBVTUK.js +0 -18
  306. package/dist/tools-RO7HSSE5.js +0 -47
  307. package/dist/userInput-JSBJRFSK.js +0 -311
  308. package/dist/userInput-JSBJRFSK.js.map +0 -7
  309. package/dist/uuid-QN2CNKKN.js +0 -9
  310. /package/dist/{REPL-CW7AYLVL.js.map → chunks/Doctor-M3J7GRTJ.js.map} +0 -0
  311. /package/dist/{autoUpdater-ITPIHCOI.js.map → chunks/REPL-RQ6LO6S7.js.map} +0 -0
  312. /package/dist/{chunk-JC6NCUG5.js.map → chunks/ResumeConversation-6DMVBEGH.js.map} +0 -0
  313. /package/dist/{commands-2BF2CJ3A.js.map → chunks/agentLoader-FCRG3TFJ.js.map} +0 -0
  314. /package/dist/{config-RUSD6G5Y.js.map → chunks/autoUpdater-CNESBOKO.js.map} +0 -0
  315. /package/dist/{context-6FXPETYH.js.map → chunks/chunk-4CRUCZR4.js.map} +0 -0
  316. /package/dist/{costTracker-6SL26FDB.js.map → chunks/chunk-54KOYG5C.js.map} +0 -0
  317. /package/dist/{customCommands-TOIJFZAL.js.map → chunks/chunk-GCQCAXJZ.js.map} +0 -0
  318. /package/dist/{env-XGKBLU3D.js.map → chunks/chunk-LB6TCPDI.js.map} +0 -0
  319. /package/dist/{kodeAgentSessionId-X6XWQW7B.js.map → chunks/client-SILZNM5N.js.map} +0 -0
  320. /package/dist/{kodeAgentSessionLoad-MITZADPB.js.map → chunks/config-25HRTPSP.js.map} +0 -0
  321. /package/dist/{kodeAgentSessionResume-GVRWB4WO.js.map → chunks/cost-tracker-Z2UZT2J5.js.map} +0 -0
  322. /package/dist/{kodeAgentStreamJson-NXFN7TXH.js.map → chunks/customCommands-TYMYZRG5.js.map} +0 -0
  323. /package/dist/{kodeAgentStructuredStdio-HGWJT7CU.js.map → chunks/engine-MRVF6FK6.js.map} +0 -0
  324. /package/dist/{kodeHooks-QWM36A3D.js.map → chunks/env-TJ5NOBEB.js.map} +0 -0
  325. /package/dist/{llmLazy-54QQHA54.js.map → chunks/kodeAgentSessionId-VTNISJ2L.js.map} +0 -0
  326. /package/dist/{loader-FYHJQES5.js.map → chunks/kodeAgentSessionLoad-YB2RKBGJ.js.map} +0 -0
  327. /package/dist/{mcp-J332IKT3.js.map → chunks/kodeAgentSessionResume-DZSIVKVA.js.map} +0 -0
  328. /package/dist/{messages-EOYQKPGM.js.map → chunks/kodeAgentStreamJson-X5PLS2S6.js.map} +0 -0
  329. /package/dist/{model-FV3JDJKH.js.map → chunks/kodeHooks-RVKYRJHG.js.map} +0 -0
  330. /package/dist/{openai-RRCWW33N.js.map → chunks/llmLazy-ZUSSE3ZA.js.map} +0 -0
  331. /package/dist/{outputStyles-62Q3VH2J.js.map → chunks/messages-EEWWLPHN.js.map} +0 -0
  332. /package/dist/{pluginValidation-I4YKUWGS.js.map → chunks/model-5TIEKQPD.js.map} +0 -0
  333. /package/dist/{prompts-ZLEKDD77.js.map → chunks/openai-XXK3YZG4.js.map} +0 -0
  334. /package/dist/{query-VFRJPBGD.js.map → chunks/outputStyles-FAJTXN2A.js.map} +0 -0
  335. /package/dist/{responsesStreaming-AW344PQO.js.map → chunks/permissions-HO7INPWM.js.map} +0 -0
  336. /package/dist/{ripgrep-3NTIKQYW.js.map → chunks/pluginValidation-DAM7WRTC.js.map} +0 -0
  337. /package/dist/{skillMarketplace-3RXQBVOL.js.map → chunks/registry-XYJXMOA5.js.map} +0 -0
  338. /package/dist/{state-P5G6CO5V.js.map → chunks/responsesStreaming-JNGE2P3D.js.map} +0 -0
  339. /package/dist/{theme-3LWP3BG7.js.map → chunks/server-REXXF5IK.js.map} +0 -0
  340. /package/dist/{toolPermissionContext-65L65VEZ.js.map → chunks/skillMarketplace-N4HVHNST.js.map} +0 -0
  341. /package/dist/{toolPermissionSettings-3ROBVTUK.js.map → chunks/src-OROQIWP3.js.map} +0 -0
  342. /package/dist/{tools-RO7HSSE5.js.map → chunks/theme-YBJUIMWK.js.map} +0 -0
  343. /package/dist/{uuid-QN2CNKKN.js.map → chunks/toolPermissionContext-MOCTRR7N.js.map} +0 -0
@@ -0,0 +1,4520 @@
1
+ import {
2
+ Select
3
+ } from "./chunk-3TNIOEBO.js";
4
+ import {
5
+ PressEnterToContinue
6
+ } from "./chunk-2QONJ5MG.js";
7
+ import {
8
+ models_default
9
+ } from "./chunk-2WEXPKHH.js";
10
+ import {
11
+ fetchCustomModels,
12
+ providers
13
+ } from "./chunk-IFCIADS3.js";
14
+ import {
15
+ normalizeLineEndings,
16
+ shouldAggregatePasteChunk,
17
+ shouldTreatAsSpecialPaste
18
+ } from "./chunk-7RRW4NTB.js";
19
+ import {
20
+ getTheme
21
+ } from "./chunk-T7RB5V5J.js";
22
+ import {
23
+ wrapText
24
+ } from "./chunk-GELCZWMB.js";
25
+ import {
26
+ verifyApiKey
27
+ } from "./chunk-BHDHXOXB.js";
28
+ import {
29
+ getModelManager
30
+ } from "./chunk-6ZWEOSEI.js";
31
+ import {
32
+ debug
33
+ } from "./chunk-YIO5EBMQ.js";
34
+ import {
35
+ ASCII_LOGO,
36
+ PRODUCT_NAME
37
+ } from "./chunk-3OEJVB5A.js";
38
+ import {
39
+ DEFAULT_GLOBAL_CONFIG,
40
+ getGlobalConfig,
41
+ saveGlobalConfig,
42
+ setAllPointersToModel,
43
+ setModelPointer,
44
+ testGPT5Connection,
45
+ validateGPT5Config
46
+ } from "./chunk-XR2W3MAM.js";
47
+ import {
48
+ __export
49
+ } from "./chunk-54KOYG5C.js";
50
+
51
+ // apps/cli/src/ui/hooks/useExitOnCtrlCD.ts
52
+ import { useInput } from "ink";
53
+
54
+ // apps/cli/src/ui/hooks/useDoublePress.ts
55
+ import { useRef } from "react";
56
+ var DOUBLE_PRESS_TIMEOUT_MS = 2e3;
57
+ function useDoublePress(setPending, onDoublePress, onFirstPress) {
58
+ const lastPressRef = useRef(0);
59
+ const timeoutRef = useRef(void 0);
60
+ return () => {
61
+ const now = Date.now();
62
+ const timeSinceLastPress = now - lastPressRef.current;
63
+ if (timeSinceLastPress <= DOUBLE_PRESS_TIMEOUT_MS && timeoutRef.current) {
64
+ if (timeoutRef.current) {
65
+ clearTimeout(timeoutRef.current);
66
+ timeoutRef.current = void 0;
67
+ }
68
+ onDoublePress();
69
+ setPending(false);
70
+ } else {
71
+ onFirstPress?.();
72
+ setPending(true);
73
+ timeoutRef.current = setTimeout(
74
+ () => setPending(false),
75
+ DOUBLE_PRESS_TIMEOUT_MS
76
+ );
77
+ }
78
+ lastPressRef.current = now;
79
+ };
80
+ }
81
+
82
+ // apps/cli/src/ui/hooks/useExitOnCtrlCD.ts
83
+ import { useState } from "react";
84
+ function useExitOnCtrlCD(onExit) {
85
+ const [exitState, setExitState] = useState({
86
+ pending: false,
87
+ keyName: null
88
+ });
89
+ const handleCtrlC = useDoublePress(
90
+ (pending) => setExitState({ pending, keyName: "Ctrl-C" }),
91
+ onExit
92
+ );
93
+ const handleCtrlD = useDoublePress(
94
+ (pending) => setExitState({ pending, keyName: "Ctrl-D" }),
95
+ onExit
96
+ );
97
+ useInput((input, key) => {
98
+ if (key.ctrl && input === "c") handleCtrlC();
99
+ if (key.ctrl && input === "d") handleCtrlD();
100
+ });
101
+ return exitState;
102
+ }
103
+
104
+ // apps/cli/src/utils/terminal.ts
105
+ function setTerminalTitle(title) {
106
+ if (process.platform === "win32") {
107
+ process.title = title ? `\u2733 ${title}` : title;
108
+ } else {
109
+ process.stdout.write(`\x1B]0;${title ? `\u2733 ${title}` : ""}\x07`);
110
+ }
111
+ }
112
+ function clearTerminal() {
113
+ return new Promise((resolve) => {
114
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H", () => {
115
+ resolve();
116
+ });
117
+ });
118
+ }
119
+
120
+ // apps/cli/src/ui/components/Onboarding.tsx
121
+ import React20, { useState as useState4 } from "react";
122
+ import { Box as Box17, Newline as Newline8, Text as Text18, useInput as useInput5 } from "ink";
123
+ import { OrderedList } from "@inkjs/ui";
124
+
125
+ // apps/cli/src/ui/components/Logo.tsx
126
+ import { Box, Text } from "ink";
127
+ import * as React from "react";
128
+ var MIN_LOGO_WIDTH = 60;
129
+ function Logo({
130
+ mcpClients,
131
+ updateBannerVersion,
132
+ terminalColumns
133
+ }) {
134
+ const theme = getTheme();
135
+ const connected = mcpClients.filter((c) => c.type === "connected");
136
+ const failed = mcpClients.filter((c) => c.type !== "connected");
137
+ const separatorWidth = Math.min(terminalColumns || 80, 80) - 16;
138
+ const separator = "\u2500".repeat(Math.max(separatorWidth, 20));
139
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, updateBannerVersion && /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "Update ", updateBannerVersion, " available: npm i -g @shareai-lab/kode@latest")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: theme.kode }, ASCII_LOGO)), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "/init", " ", "/help", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.bashBorder }, "!"), "shell", " ", /* @__PURE__ */ React.createElement(Text, { color: theme.notingBorder }, "#"), "note", " ", "@file", " ", "opt+m", " ", "opt+g")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginTop: 2 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "\u2500\u2500 MCP Servers ", separator), /* @__PURE__ */ React.createElement(Box, { marginTop: 1, paddingLeft: 3 }, mcpClients.length === 0 ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "No servers configured - run: kode mcp add ", "<name>") : /* @__PURE__ */ React.createElement(React.Fragment, null, connected.map((c) => /* @__PURE__ */ React.createElement(Text, { key: c.name }, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, c.name), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " "))), failed.map((c) => /* @__PURE__ */ React.createElement(Text, { key: c.name }, /* @__PURE__ */ React.createElement(Text, { color: theme.error }, c.name), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ")))))));
140
+ }
141
+
142
+ // apps/cli/src/ui/components/StructuredDiff.tsx
143
+ import { Box as Box2, Text as Text2 } from "ink";
144
+ import * as React2 from "react";
145
+ import { useMemo } from "react";
146
+ function StructuredDiff({
147
+ patch,
148
+ dim,
149
+ width,
150
+ overrideTheme
151
+ }) {
152
+ const diff = useMemo(
153
+ () => formatDiff(patch.lines, patch.oldStart, width, dim, overrideTheme),
154
+ [patch.lines, patch.oldStart, width, dim, overrideTheme]
155
+ );
156
+ return diff.map((_, i) => /* @__PURE__ */ React2.createElement(Box2, { key: i }, _));
157
+ }
158
+ function formatDiff(lines, startingLineNumber, width, dim, overrideTheme) {
159
+ const theme = getTheme(overrideTheme);
160
+ const ls = numberDiffLines(
161
+ lines.map((code) => {
162
+ if (code.startsWith("+")) {
163
+ return {
164
+ code: " " + code.slice(1),
165
+ i: 0,
166
+ type: "add"
167
+ };
168
+ }
169
+ if (code.startsWith("-")) {
170
+ return {
171
+ code: " " + code.slice(1),
172
+ i: 0,
173
+ type: "remove"
174
+ };
175
+ }
176
+ return { code, i: 0, type: "nochange" };
177
+ }),
178
+ startingLineNumber
179
+ );
180
+ const maxLineNumber = Math.max(...ls.map(({ i }) => i));
181
+ const maxWidth = maxLineNumber.toString().length;
182
+ return ls.flatMap(({ type, code, i }) => {
183
+ const wrappedLines = wrapText(code, width - maxWidth);
184
+ return wrappedLines.map((line, lineIndex) => {
185
+ const key = `${type}-${i}-${lineIndex}`;
186
+ switch (type) {
187
+ case "add":
188
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, { key }, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(
189
+ LineNumber,
190
+ {
191
+ i: lineIndex === 0 ? i : void 0,
192
+ width: maxWidth
193
+ }
194
+ ), /* @__PURE__ */ React2.createElement(
195
+ Text2,
196
+ {
197
+ color: overrideTheme ? theme.text : void 0,
198
+ backgroundColor: dim ? theme.diff.addedDimmed : theme.diff.added,
199
+ dimColor: dim
200
+ },
201
+ line
202
+ )));
203
+ case "remove":
204
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, { key }, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(
205
+ LineNumber,
206
+ {
207
+ i: lineIndex === 0 ? i : void 0,
208
+ width: maxWidth
209
+ }
210
+ ), /* @__PURE__ */ React2.createElement(
211
+ Text2,
212
+ {
213
+ color: overrideTheme ? theme.text : void 0,
214
+ backgroundColor: dim ? theme.diff.removedDimmed : theme.diff.removed,
215
+ dimColor: dim
216
+ },
217
+ line
218
+ )));
219
+ case "nochange":
220
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, { key }, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(
221
+ LineNumber,
222
+ {
223
+ i: lineIndex === 0 ? i : void 0,
224
+ width: maxWidth
225
+ }
226
+ ), /* @__PURE__ */ React2.createElement(
227
+ Text2,
228
+ {
229
+ color: overrideTheme ? theme.text : void 0,
230
+ dimColor: dim
231
+ },
232
+ line
233
+ )));
234
+ }
235
+ });
236
+ });
237
+ }
238
+ function LineNumber({
239
+ i,
240
+ width
241
+ }) {
242
+ return /* @__PURE__ */ React2.createElement(Text2, { color: getTheme().secondaryText }, i !== void 0 ? i.toString().padStart(width) : " ".repeat(width), " ");
243
+ }
244
+ function numberDiffLines(diff, startLine) {
245
+ let i = startLine;
246
+ const result = [];
247
+ const queue = [...diff];
248
+ while (queue.length > 0) {
249
+ const { code, type } = queue.shift();
250
+ const line = {
251
+ code,
252
+ type,
253
+ i
254
+ };
255
+ switch (type) {
256
+ case "nochange":
257
+ i++;
258
+ result.push(line);
259
+ break;
260
+ case "add":
261
+ i++;
262
+ result.push(line);
263
+ break;
264
+ case "remove": {
265
+ result.push(line);
266
+ let numRemoved = 0;
267
+ while (queue[0]?.type === "remove") {
268
+ i++;
269
+ const { code: code2, type: type2 } = queue.shift();
270
+ const line2 = {
271
+ code: code2,
272
+ type: type2,
273
+ i
274
+ };
275
+ result.push(line2);
276
+ numRemoved++;
277
+ }
278
+ i -= numRemoved;
279
+ break;
280
+ }
281
+ }
282
+ }
283
+ return result;
284
+ }
285
+
286
+ // apps/cli/src/ui/components/ModelSelector/ModelSelector.tsx
287
+ import * as React19 from "react";
288
+
289
+ // apps/cli/src/ui/components/ModelSelector/ModelSelectorView.tsx
290
+ import * as React18 from "react";
291
+ import { Box as Box16, Text as Text17 } from "ink";
292
+ import figures from "figures";
293
+
294
+ // apps/cli/src/ui/ui/components/model-selector/screens/ApiKeyScreen.tsx
295
+ import React5 from "react";
296
+ import { Box as Box3, Newline, Text as Text4 } from "ink";
297
+
298
+ // apps/cli/src/ui/components/TextInput.tsx
299
+ import React4 from "react";
300
+ import { Text as Text3, useInput as useInput2 } from "ink";
301
+ import chalk from "chalk";
302
+
303
+ // apps/cli/src/ui/hooks/useTextInput.ts
304
+ import { useState as useState2 } from "react";
305
+
306
+ // apps/cli/src/utils/Cursor/MeasuredText.ts
307
+ import wrapAnsi from "wrap-ansi";
308
+ var WrappedLine = class {
309
+ constructor(text, startOffset, isPrecededByNewline, endsWithNewline = false) {
310
+ this.text = text;
311
+ this.startOffset = startOffset;
312
+ this.isPrecededByNewline = isPrecededByNewline;
313
+ this.endsWithNewline = endsWithNewline;
314
+ }
315
+ equals(other) {
316
+ return this.text === other.text && this.startOffset === other.startOffset;
317
+ }
318
+ get length() {
319
+ return this.text.length + (this.endsWithNewline ? 1 : 0);
320
+ }
321
+ };
322
+ var MeasuredText = class {
323
+ constructor(text, columns) {
324
+ this.text = text;
325
+ this.columns = columns;
326
+ this.wrappedLines = this.measureWrappedText();
327
+ }
328
+ wrappedLines;
329
+ measureWrappedText() {
330
+ const wrappedText = wrapAnsi(this.text, this.columns, {
331
+ hard: true,
332
+ trim: false
333
+ });
334
+ const wrappedLines = [];
335
+ let searchOffset = 0;
336
+ let lastNewLinePos = -1;
337
+ const lines = wrappedText.split("\n");
338
+ for (let i = 0; i < lines.length; i++) {
339
+ const text = lines[i];
340
+ const isPrecededByNewline = (startOffset) => i == 0 || startOffset > 0 && this.text[startOffset - 1] === "\n";
341
+ if (text.length === 0) {
342
+ lastNewLinePos = this.text.indexOf("\n", lastNewLinePos + 1);
343
+ if (lastNewLinePos !== -1) {
344
+ const startOffset = lastNewLinePos;
345
+ const endsWithNewline = true;
346
+ wrappedLines.push(
347
+ new WrappedLine(
348
+ text,
349
+ startOffset,
350
+ isPrecededByNewline(startOffset),
351
+ endsWithNewline
352
+ )
353
+ );
354
+ } else {
355
+ const startOffset = this.text.length;
356
+ wrappedLines.push(
357
+ new WrappedLine(
358
+ text,
359
+ startOffset,
360
+ isPrecededByNewline(startOffset),
361
+ false
362
+ )
363
+ );
364
+ }
365
+ } else {
366
+ const startOffset = this.text.indexOf(text, searchOffset);
367
+ if (startOffset === -1) {
368
+ debug.error("CURSOR_WRAP_MISMATCH", {
369
+ currentText: text,
370
+ originalText: this.text,
371
+ searchOffset,
372
+ wrappedText
373
+ });
374
+ throw new Error("Failed to find wrapped line in original text");
375
+ }
376
+ searchOffset = startOffset + text.length;
377
+ const potentialNewlinePos = startOffset + text.length;
378
+ const endsWithNewline = potentialNewlinePos < this.text.length && this.text[potentialNewlinePos] === "\n";
379
+ if (endsWithNewline) {
380
+ lastNewLinePos = potentialNewlinePos;
381
+ }
382
+ wrappedLines.push(
383
+ new WrappedLine(
384
+ text,
385
+ startOffset,
386
+ isPrecededByNewline(startOffset),
387
+ endsWithNewline
388
+ )
389
+ );
390
+ }
391
+ }
392
+ return wrappedLines;
393
+ }
394
+ getWrappedText() {
395
+ return this.wrappedLines.map(
396
+ (line) => line.isPrecededByNewline ? line.text : line.text.trimStart()
397
+ );
398
+ }
399
+ getLine(line) {
400
+ return this.wrappedLines[Math.max(0, Math.min(line, this.wrappedLines.length - 1))];
401
+ }
402
+ getOffsetFromPosition(position) {
403
+ const wrappedLine = this.getLine(position.line);
404
+ const startOffsetPlusColumn = wrappedLine.startOffset + position.column;
405
+ if (wrappedLine.text.length === 0 && wrappedLine.endsWithNewline) {
406
+ return wrappedLine.startOffset;
407
+ }
408
+ const lineEnd = wrappedLine.startOffset + wrappedLine.text.length;
409
+ const maxOffset = wrappedLine.endsWithNewline ? lineEnd + 1 : lineEnd;
410
+ return Math.min(startOffsetPlusColumn, maxOffset);
411
+ }
412
+ getLineLength(line) {
413
+ const currentLine = this.getLine(line);
414
+ const nextLine = this.getLine(line + 1);
415
+ if (nextLine.equals(currentLine)) {
416
+ return this.text.length - currentLine.startOffset;
417
+ }
418
+ return nextLine.startOffset - currentLine.startOffset - 1;
419
+ }
420
+ getPositionFromOffset(offset) {
421
+ const lines = this.wrappedLines;
422
+ for (let line2 = 0; line2 < lines.length; line2++) {
423
+ const currentLine = lines[line2];
424
+ const nextLine = lines[line2 + 1];
425
+ if (offset >= currentLine.startOffset && (!nextLine || offset < nextLine.startOffset)) {
426
+ const leadingWhitepace = currentLine.isPrecededByNewline ? 0 : currentLine.text.length - currentLine.text.trimStart().length;
427
+ const column = Math.max(
428
+ 0,
429
+ Math.min(
430
+ offset - currentLine.startOffset - leadingWhitepace,
431
+ currentLine.text.length
432
+ )
433
+ );
434
+ return {
435
+ line: line2,
436
+ column
437
+ };
438
+ }
439
+ }
440
+ const line = lines.length - 1;
441
+ return {
442
+ line,
443
+ column: this.wrappedLines[line].text.length
444
+ };
445
+ }
446
+ get lineCount() {
447
+ return this.wrappedLines.length;
448
+ }
449
+ equals(other) {
450
+ return this.text === other.text && this.columns === other.columns;
451
+ }
452
+ };
453
+
454
+ // apps/cli/src/utils/Cursor.ts
455
+ var Cursor = class _Cursor {
456
+ constructor(measuredText, offset = 0, selection = 0) {
457
+ this.measuredText = measuredText;
458
+ this.selection = selection;
459
+ this.offset = Math.max(0, Math.min(this.measuredText.text.length, offset));
460
+ }
461
+ offset;
462
+ static fromText(text, columns, offset = 0, selection = 0) {
463
+ return new _Cursor(new MeasuredText(text, columns - 1), offset, selection);
464
+ }
465
+ render(cursorChar, mask, invert) {
466
+ const { line, column } = this.getPosition();
467
+ return this.measuredText.getWrappedText().map((text, currentLine, allLines) => {
468
+ let displayText = text;
469
+ if (mask && currentLine === allLines.length - 1) {
470
+ const lastSixStart = Math.max(0, text.length - 6);
471
+ displayText = mask.repeat(lastSixStart) + text.slice(lastSixStart);
472
+ }
473
+ if (line != currentLine) return displayText.trimEnd();
474
+ return displayText.slice(0, column) + invert(displayText[column] || cursorChar) + displayText.trimEnd().slice(column + 1);
475
+ }).join("\n");
476
+ }
477
+ left() {
478
+ return new _Cursor(this.measuredText, this.offset - 1);
479
+ }
480
+ right() {
481
+ return new _Cursor(this.measuredText, this.offset + 1);
482
+ }
483
+ up() {
484
+ const { line, column } = this.getPosition();
485
+ if (line == 0) {
486
+ return new _Cursor(this.measuredText, 0, 0);
487
+ }
488
+ const newOffset = this.getOffset({ line: line - 1, column });
489
+ return new _Cursor(this.measuredText, newOffset, 0);
490
+ }
491
+ down() {
492
+ const { line, column } = this.getPosition();
493
+ if (line >= this.measuredText.lineCount - 1) {
494
+ return new _Cursor(this.measuredText, this.text.length, 0);
495
+ }
496
+ const newOffset = this.getOffset({ line: line + 1, column });
497
+ return new _Cursor(this.measuredText, newOffset, 0);
498
+ }
499
+ startOfLine() {
500
+ const { line } = this.getPosition();
501
+ return new _Cursor(
502
+ this.measuredText,
503
+ this.getOffset({
504
+ line,
505
+ column: 0
506
+ }),
507
+ 0
508
+ );
509
+ }
510
+ endOfLine() {
511
+ const { line } = this.getPosition();
512
+ const column = this.measuredText.getLineLength(line);
513
+ const offset = this.getOffset({ line, column });
514
+ return new _Cursor(this.measuredText, offset, 0);
515
+ }
516
+ nextWord() {
517
+ let nextCursor = this;
518
+ while (nextCursor.isOverWordChar() && !nextCursor.isAtEnd()) {
519
+ nextCursor = nextCursor.right();
520
+ }
521
+ while (!nextCursor.isOverWordChar() && !nextCursor.isAtEnd()) {
522
+ nextCursor = nextCursor.right();
523
+ }
524
+ return nextCursor;
525
+ }
526
+ prevWord() {
527
+ let cursor = this;
528
+ if (!cursor.left().isOverWordChar()) {
529
+ cursor = cursor.left();
530
+ }
531
+ while (!cursor.isOverWordChar() && !cursor.isAtStart()) {
532
+ cursor = cursor.left();
533
+ }
534
+ if (cursor.isOverWordChar()) {
535
+ while (cursor.left().isOverWordChar() && !cursor.isAtStart()) {
536
+ cursor = cursor.left();
537
+ }
538
+ }
539
+ return cursor;
540
+ }
541
+ modifyText(end, insertString = "") {
542
+ const startOffset = this.offset;
543
+ const endOffset = end.offset;
544
+ const newText = this.text.slice(0, startOffset) + insertString + this.text.slice(endOffset);
545
+ return _Cursor.fromText(
546
+ newText,
547
+ this.columns,
548
+ startOffset + insertString.length
549
+ );
550
+ }
551
+ insert(insertString) {
552
+ const newCursor = this.modifyText(this, insertString);
553
+ return newCursor;
554
+ }
555
+ del() {
556
+ if (this.isAtEnd()) {
557
+ return this;
558
+ }
559
+ return this.modifyText(this.right());
560
+ }
561
+ backspace() {
562
+ if (this.isAtStart()) {
563
+ return this;
564
+ }
565
+ const currentOffset = this.offset;
566
+ const leftCursor = this.left();
567
+ const leftOffset = leftCursor.offset;
568
+ const newText = this.text.slice(0, leftOffset) + this.text.slice(currentOffset);
569
+ return _Cursor.fromText(newText, this.columns, leftOffset);
570
+ }
571
+ deleteToLineStart() {
572
+ return this.startOfLine().modifyText(this);
573
+ }
574
+ deleteToLineEnd() {
575
+ if (this.text[this.offset] === "\n") {
576
+ return this.modifyText(this.right());
577
+ }
578
+ return this.modifyText(this.endOfLine());
579
+ }
580
+ deleteWordBefore() {
581
+ if (this.isAtStart()) {
582
+ return this;
583
+ }
584
+ return this.prevWord().modifyText(this);
585
+ }
586
+ deleteWordAfter() {
587
+ if (this.isAtEnd()) {
588
+ return this;
589
+ }
590
+ return this.modifyText(this.nextWord());
591
+ }
592
+ isOverWordChar() {
593
+ const currentChar = this.text[this.offset] ?? "";
594
+ return /\w/.test(currentChar);
595
+ }
596
+ equals(other) {
597
+ return this.offset === other.offset && this.measuredText == other.measuredText;
598
+ }
599
+ isAtStart() {
600
+ return this.offset == 0;
601
+ }
602
+ isAtEnd() {
603
+ return this.offset == this.text.length;
604
+ }
605
+ get text() {
606
+ return this.measuredText.text;
607
+ }
608
+ get columns() {
609
+ return this.measuredText.columns + 1;
610
+ }
611
+ getPosition() {
612
+ return this.measuredText.getPositionFromOffset(this.offset);
613
+ }
614
+ getOffset(position) {
615
+ return this.measuredText.getOffsetFromPosition(position);
616
+ }
617
+ };
618
+
619
+ // apps/cli/src/ui/hooks/useTextInputMapping.ts
620
+ function mapInput(inputMap, defaultHandler) {
621
+ const handlers = new Map(inputMap);
622
+ return (input) => (handlers.get(input) ?? defaultHandler)(input);
623
+ }
624
+
625
+ // packages/core/src/utils/imagePaste.ts
626
+ import { execSync } from "child_process";
627
+ import { readFileSync } from "fs";
628
+ var SCREENSHOT_PATH = "/tmp/kode_cli_latest_screenshot.png";
629
+ var CLIPBOARD_ERROR_MESSAGE = "No image found in clipboard. Use Cmd + Ctrl + Shift + 4 to copy a screenshot to clipboard.";
630
+ function getImageFromClipboard() {
631
+ if (process.platform !== "darwin") {
632
+ return null;
633
+ }
634
+ try {
635
+ execSync(`osascript -e 'the clipboard as \xABclass PNGf\xBB'`, {
636
+ stdio: "ignore"
637
+ });
638
+ execSync(
639
+ `osascript -e 'set png_data to (the clipboard as \xABclass PNGf\xBB)' -e 'set fp to open for access POSIX file "${SCREENSHOT_PATH}" with write permission' -e 'write png_data to fp' -e 'close access fp'`,
640
+ { stdio: "ignore" }
641
+ );
642
+ const imageBuffer = readFileSync(SCREENSHOT_PATH);
643
+ const base64Image = imageBuffer.toString("base64");
644
+ execSync(`rm -f "${SCREENSHOT_PATH}"`, { stdio: "ignore" });
645
+ return base64Image;
646
+ } catch {
647
+ return null;
648
+ }
649
+ }
650
+
651
+ // apps/cli/src/ui/hooks/useTextInputTryImagePaste.ts
652
+ var IMAGE_PLACEHOLDER = "[Image pasted]";
653
+ function tryImagePaste({
654
+ cursor,
655
+ mask,
656
+ onImagePaste,
657
+ onMessage,
658
+ setImagePasteErrorTimeout,
659
+ clearImagePasteErrorTimeout
660
+ }) {
661
+ if (mask) {
662
+ return cursor;
663
+ }
664
+ const base64Image = getImageFromClipboard();
665
+ if (base64Image === null) {
666
+ if (process.platform !== "darwin") {
667
+ return cursor;
668
+ }
669
+ onMessage?.(true, CLIPBOARD_ERROR_MESSAGE);
670
+ clearImagePasteErrorTimeout();
671
+ setImagePasteErrorTimeout(
672
+ setTimeout(() => {
673
+ onMessage?.(false);
674
+ }, 4e3)
675
+ );
676
+ return cursor;
677
+ }
678
+ const placeholder = onImagePaste?.(base64Image);
679
+ return cursor.insert(
680
+ typeof placeholder === "string" ? placeholder : IMAGE_PLACEHOLDER
681
+ );
682
+ }
683
+
684
+ // apps/cli/src/ui/hooks/useTextInput.ts
685
+ function useTextInput({
686
+ value: originalValue,
687
+ onChange,
688
+ onSubmit,
689
+ onExit,
690
+ onExitMessage,
691
+ onMessage,
692
+ onHistoryUp,
693
+ onHistoryDown,
694
+ onHistoryReset,
695
+ mask = "",
696
+ multiline = false,
697
+ cursorChar,
698
+ invert,
699
+ columns,
700
+ onImagePaste,
701
+ disableCursorMovementForUpDownKeys = false,
702
+ externalOffset,
703
+ onOffsetChange
704
+ }) {
705
+ const offset = externalOffset;
706
+ const setOffset = onOffsetChange;
707
+ const cursor = Cursor.fromText(originalValue, columns, offset);
708
+ const [imagePasteErrorTimeout, setImagePasteErrorTimeout] = useState2(null);
709
+ function maybeClearImagePasteErrorTimeout() {
710
+ if (!imagePasteErrorTimeout) {
711
+ return;
712
+ }
713
+ clearTimeout(imagePasteErrorTimeout);
714
+ setImagePasteErrorTimeout(null);
715
+ onMessage?.(false);
716
+ }
717
+ function applyCursor(nextCursor) {
718
+ if (cursor.equals(nextCursor)) {
719
+ return;
720
+ }
721
+ setOffset(nextCursor.offset);
722
+ if (cursor.text !== nextCursor.text) {
723
+ onChange(nextCursor.text);
724
+ }
725
+ }
726
+ const handleCtrlC = useDoublePress(
727
+ (show) => {
728
+ maybeClearImagePasteErrorTimeout();
729
+ onExitMessage?.(show, "Ctrl-C");
730
+ },
731
+ () => onExit?.(),
732
+ () => {
733
+ if (originalValue) {
734
+ onChange("");
735
+ onHistoryReset?.();
736
+ }
737
+ }
738
+ );
739
+ const handleEscape = useDoublePress(
740
+ (show) => {
741
+ maybeClearImagePasteErrorTimeout();
742
+ onMessage?.(!!originalValue && show, `Press Escape again to clear`);
743
+ },
744
+ () => {
745
+ if (originalValue) {
746
+ onChange("");
747
+ }
748
+ }
749
+ );
750
+ function clear() {
751
+ return Cursor.fromText("", columns, 0);
752
+ }
753
+ const handleEmptyCtrlD = useDoublePress(
754
+ (show) => onExitMessage?.(show, "Ctrl-D"),
755
+ () => onExit?.()
756
+ );
757
+ function handleCtrlD() {
758
+ maybeClearImagePasteErrorTimeout();
759
+ if (cursor.text === "") {
760
+ handleEmptyCtrlD();
761
+ return cursor;
762
+ }
763
+ return cursor.del();
764
+ }
765
+ function handleImagePaste() {
766
+ return tryImagePaste({
767
+ cursor,
768
+ mask,
769
+ onImagePaste,
770
+ onMessage,
771
+ setImagePasteErrorTimeout,
772
+ clearImagePasteErrorTimeout: maybeClearImagePasteErrorTimeout
773
+ });
774
+ }
775
+ const handleCtrl = mapInput(
776
+ [
777
+ ["a", () => cursor.startOfLine()],
778
+ ["b", () => cursor.left()],
779
+ ["c", handleCtrlC],
780
+ ["d", handleCtrlD],
781
+ ["e", () => cursor.endOfLine()],
782
+ ["f", () => cursor.right()],
783
+ [
784
+ "h",
785
+ () => {
786
+ maybeClearImagePasteErrorTimeout();
787
+ return cursor.backspace();
788
+ }
789
+ ],
790
+ ["k", () => cursor.deleteToLineEnd()],
791
+ ["l", () => clear()],
792
+ ["n", () => downOrHistoryDown()],
793
+ ["p", () => upOrHistoryUp()],
794
+ ["u", () => cursor.deleteToLineStart()],
795
+ ["v", handleImagePaste],
796
+ ["w", () => cursor.deleteWordBefore()]
797
+ ],
798
+ () => void 0
799
+ );
800
+ const handleMeta = mapInput(
801
+ [
802
+ ["b", () => cursor.prevWord()],
803
+ ["f", () => cursor.nextWord()],
804
+ ["d", () => cursor.deleteWordAfter()]
805
+ ],
806
+ () => void 0
807
+ );
808
+ function handleEnter(key) {
809
+ if (!multiline) {
810
+ onSubmit?.(originalValue);
811
+ return;
812
+ }
813
+ const optionPressed = (() => {
814
+ if (!("option" in key)) return false;
815
+ const optionValue = key.option;
816
+ return optionValue === true;
817
+ })();
818
+ if (key.meta || optionPressed) {
819
+ return cursor.insert("\n");
820
+ }
821
+ onSubmit?.(originalValue);
822
+ }
823
+ function upOrHistoryUp() {
824
+ if (disableCursorMovementForUpDownKeys) {
825
+ onHistoryUp?.();
826
+ return cursor;
827
+ }
828
+ const cursorUp = cursor.up();
829
+ if (cursorUp.equals(cursor)) {
830
+ onHistoryUp?.();
831
+ }
832
+ return cursorUp;
833
+ }
834
+ function downOrHistoryDown() {
835
+ if (disableCursorMovementForUpDownKeys) {
836
+ onHistoryDown?.();
837
+ return cursor;
838
+ }
839
+ const cursorDown = cursor.down();
840
+ if (cursorDown.equals(cursor)) {
841
+ onHistoryDown?.();
842
+ }
843
+ return cursorDown;
844
+ }
845
+ function onInput(input, key) {
846
+ if (key.tab) {
847
+ return;
848
+ }
849
+ if (key.backspace || key.delete || input === "\b" || input === "\x7F" || input === "\b") {
850
+ applyCursor(cursor.backspace());
851
+ return;
852
+ }
853
+ if (!key.ctrl && !key.meta && input.length > 1) {
854
+ applyCursor(cursor.insert(normalizeLineEndings(input)));
855
+ return;
856
+ }
857
+ const nextCursor = mapKey(key)(input);
858
+ if (nextCursor) {
859
+ applyCursor(nextCursor);
860
+ }
861
+ }
862
+ function mapKey(key) {
863
+ if (key.backspace || key.delete) {
864
+ maybeClearImagePasteErrorTimeout();
865
+ return () => cursor.backspace();
866
+ }
867
+ switch (true) {
868
+ case key.escape:
869
+ return handleEscape;
870
+ case (key.leftArrow && (key.ctrl || key.meta || "fn" in key && key.fn)):
871
+ return () => cursor.prevWord();
872
+ case (key.rightArrow && (key.ctrl || key.meta || "fn" in key && key.fn)):
873
+ return () => cursor.nextWord();
874
+ case key.ctrl:
875
+ return handleCtrl;
876
+ case ("home" in key && key.home):
877
+ return () => cursor.startOfLine();
878
+ case ("end" in key && key.end):
879
+ return () => cursor.endOfLine();
880
+ case key.pageDown:
881
+ return () => cursor.endOfLine();
882
+ case key.pageUp:
883
+ return () => cursor.startOfLine();
884
+ case key.return:
885
+ return () => handleEnter(key);
886
+ case key.meta:
887
+ return handleMeta;
888
+ // Remove Tab handling - let completion system handle it
889
+ case key.upArrow:
890
+ return upOrHistoryUp;
891
+ case key.downArrow:
892
+ return downOrHistoryDown;
893
+ case key.leftArrow:
894
+ return () => cursor.left();
895
+ case key.rightArrow:
896
+ return () => cursor.right();
897
+ }
898
+ return function(input) {
899
+ switch (true) {
900
+ // Home key
901
+ case (input == "\x1B[H" || input == "\x1B[1~"):
902
+ return cursor.startOfLine();
903
+ // End key
904
+ case (input == "\x1B[F" || input == "\x1B[4~"):
905
+ return cursor.endOfLine();
906
+ // Handle backspace character explicitly - this is the key fix
907
+ case (input === "\b" || input === "\x7F" || input === "\b"):
908
+ maybeClearImagePasteErrorTimeout();
909
+ return cursor.backspace();
910
+ default:
911
+ return cursor.insert(input.replace(/\r/g, "\n"));
912
+ }
913
+ };
914
+ }
915
+ return {
916
+ onInput,
917
+ renderedValue: cursor.render(cursorChar, mask, invert),
918
+ offset,
919
+ setOffset
920
+ };
921
+ }
922
+
923
+ // apps/cli/src/ui/components/TextInputBracketedPaste.ts
924
+ import React3 from "react";
925
+ var BRACKETED_PASTE_ENABLE = "\x1B[?2004h";
926
+ var BRACKETED_PASTE_DISABLE = "\x1B[?2004l";
927
+ var BRACKETED_PASTE_START = "\x1B[200~";
928
+ var BRACKETED_PASTE_END = "\x1B[201~";
929
+ var BRACKETED_PASTE_START_NO_ESC = "[200~";
930
+ var BRACKETED_PASTE_END_NO_ESC = "[201~";
931
+ var bracketedPasteRefCount = 0;
932
+ function setBracketedPasteEnabled(enabled) {
933
+ if (!process.stdout?.isTTY) return;
934
+ process.stdout.write(
935
+ enabled ? BRACKETED_PASTE_ENABLE : BRACKETED_PASTE_DISABLE
936
+ );
937
+ }
938
+ function acquireBracketedPasteMode() {
939
+ if (bracketedPasteRefCount === 0) {
940
+ setBracketedPasteEnabled(true);
941
+ }
942
+ bracketedPasteRefCount++;
943
+ }
944
+ function releaseBracketedPasteMode() {
945
+ bracketedPasteRefCount = Math.max(0, bracketedPasteRefCount - 1);
946
+ if (bracketedPasteRefCount === 0) {
947
+ setBracketedPasteEnabled(false);
948
+ }
949
+ }
950
+ function useBracketedPasteMode() {
951
+ React3.useEffect(() => {
952
+ acquireBracketedPasteMode();
953
+ return () => releaseBracketedPasteMode();
954
+ }, []);
955
+ }
956
+ function longestSuffixPrefix(haystack, needle) {
957
+ const max = Math.min(haystack.length, needle.length - 1);
958
+ for (let len = max; len > 0; len--) {
959
+ if (haystack.endsWith(needle.slice(0, len))) return len;
960
+ }
961
+ return 0;
962
+ }
963
+ function findFirstMarker(haystack, markers) {
964
+ let best = null;
965
+ for (const marker of markers) {
966
+ const index = haystack.indexOf(marker);
967
+ if (index === -1) continue;
968
+ if (!best || index < best.index) {
969
+ best = { index, marker };
970
+ }
971
+ }
972
+ return best;
973
+ }
974
+ function getSuffixKeepLength(haystack, markers) {
975
+ let keep = 0;
976
+ for (const marker of markers) {
977
+ keep = Math.max(keep, longestSuffixPrefix(haystack, marker));
978
+ }
979
+ return keep;
980
+ }
981
+ function useBracketedPasteSequences({
982
+ insertText,
983
+ onPaste
984
+ }) {
985
+ const stateRef = React3.useRef({
986
+ mode: "normal",
987
+ incomplete: "",
988
+ buffer: ""
989
+ });
990
+ const flushBracketedPasteBuffer = React3.useCallback(
991
+ (rawText) => {
992
+ const normalized = normalizeLineEndings(rawText);
993
+ if (onPaste && shouldTreatAsSpecialPaste(normalized)) {
994
+ Promise.resolve().then(() => onPaste(normalized));
995
+ return;
996
+ }
997
+ insertText(normalized);
998
+ },
999
+ [insertText, onPaste]
1000
+ );
1001
+ return React3.useCallback(
1002
+ (input) => {
1003
+ const state = stateRef.current;
1004
+ let handledAny = false;
1005
+ let data = state.incomplete + input;
1006
+ state.incomplete = "";
1007
+ const startMarkers = [BRACKETED_PASTE_START, BRACKETED_PASTE_START_NO_ESC];
1008
+ const endMarkers = [BRACKETED_PASTE_END, BRACKETED_PASTE_END_NO_ESC];
1009
+ while (data) {
1010
+ if (state.mode === "normal") {
1011
+ const start = findFirstMarker(data, startMarkers);
1012
+ if (!start) {
1013
+ const keep = getSuffixKeepLength(data, startMarkers);
1014
+ if (keep === 0) {
1015
+ if (!handledAny) {
1016
+ return false;
1017
+ }
1018
+ insertText(data);
1019
+ return true;
1020
+ }
1021
+ const toInsert = data.slice(0, -keep);
1022
+ if (toInsert) {
1023
+ insertText(toInsert);
1024
+ }
1025
+ state.incomplete = data.slice(-keep);
1026
+ handledAny = true;
1027
+ return true;
1028
+ }
1029
+ const before = data.slice(0, start.index);
1030
+ if (before) {
1031
+ insertText(before);
1032
+ }
1033
+ data = data.slice(start.index + start.marker.length);
1034
+ state.mode = "in_paste";
1035
+ handledAny = true;
1036
+ continue;
1037
+ }
1038
+ const end = findFirstMarker(data, endMarkers);
1039
+ if (!end) {
1040
+ const keep = getSuffixKeepLength(data, endMarkers);
1041
+ const content = keep > 0 ? data.slice(0, -keep) : data;
1042
+ if (content) {
1043
+ state.buffer += content;
1044
+ }
1045
+ if (keep > 0) {
1046
+ state.incomplete = data.slice(-keep);
1047
+ }
1048
+ handledAny = true;
1049
+ return true;
1050
+ }
1051
+ state.buffer += data.slice(0, end.index);
1052
+ const completedPaste = state.buffer;
1053
+ state.buffer = "";
1054
+ state.mode = "normal";
1055
+ flushBracketedPasteBuffer(completedPaste);
1056
+ data = data.slice(end.index + end.marker.length);
1057
+ handledAny = true;
1058
+ continue;
1059
+ }
1060
+ return true;
1061
+ },
1062
+ [flushBracketedPasteBuffer, insertText]
1063
+ );
1064
+ }
1065
+
1066
+ // apps/cli/src/ui/components/TextInput.tsx
1067
+ function TextInput({
1068
+ value: originalValue,
1069
+ placeholder = "",
1070
+ focus = true,
1071
+ mask,
1072
+ multiline = false,
1073
+ highlightPastedText = false,
1074
+ showCursor = true,
1075
+ onChange,
1076
+ onSubmit,
1077
+ onExit,
1078
+ onHistoryUp,
1079
+ onHistoryDown,
1080
+ onExitMessage,
1081
+ onMessage,
1082
+ onHistoryReset,
1083
+ columns,
1084
+ onImagePaste,
1085
+ onPaste,
1086
+ isDimmed = false,
1087
+ disableCursorMovementForUpDownKeys = false,
1088
+ onSpecialKey,
1089
+ cursorOffset,
1090
+ onChangeCursorOffset
1091
+ }) {
1092
+ const { onInput, renderedValue } = useTextInput({
1093
+ value: originalValue,
1094
+ onChange,
1095
+ onSubmit,
1096
+ onExit,
1097
+ onExitMessage,
1098
+ onMessage,
1099
+ onHistoryReset,
1100
+ onHistoryUp,
1101
+ onHistoryDown,
1102
+ focus,
1103
+ mask,
1104
+ multiline,
1105
+ cursorChar: showCursor ? " " : "",
1106
+ highlightPastedText,
1107
+ invert: chalk.inverse,
1108
+ themeText: (text) => chalk.hex(getTheme().text)(text),
1109
+ columns,
1110
+ onImagePaste,
1111
+ disableCursorMovementForUpDownKeys,
1112
+ externalOffset: cursorOffset,
1113
+ onOffsetChange: onChangeCursorOffset
1114
+ });
1115
+ useBracketedPasteMode();
1116
+ const [pasteState, setPasteState] = React4.useState({ chunks: [], timeoutId: null });
1117
+ const handleBracketedPasteSequences = useBracketedPasteSequences({
1118
+ insertText: (text) => onInput(text, {}),
1119
+ onPaste
1120
+ });
1121
+ const resetPasteTimeout = (currentTimeoutId) => {
1122
+ if (currentTimeoutId) {
1123
+ clearTimeout(currentTimeoutId);
1124
+ }
1125
+ return setTimeout(() => {
1126
+ setPasteState(({ chunks }) => {
1127
+ const pastedText = chunks.join("");
1128
+ Promise.resolve().then(() => onPaste(pastedText));
1129
+ return { chunks: [], timeoutId: null };
1130
+ });
1131
+ }, 500);
1132
+ };
1133
+ const wrappedOnInput = (input, key) => {
1134
+ if (/^(?:\x1b)?\[13;2(?:u|~)$/.test(input)) {
1135
+ onInput("\r", { ...key, return: true, meta: false, shift: false });
1136
+ return;
1137
+ }
1138
+ if (/^(?:\x1b)?\[13;(?:3|4)(?:u|~)$/.test(input)) {
1139
+ onInput("\r", { ...key, return: true, meta: true });
1140
+ return;
1141
+ }
1142
+ if (input === "\n") {
1143
+ if (multiline) {
1144
+ onInput("\n", key);
1145
+ return;
1146
+ }
1147
+ onInput("\r", { ...key, return: true });
1148
+ return;
1149
+ }
1150
+ if (input === "\x1B\r" || input === "\x1B\n") {
1151
+ onInput("\r", {
1152
+ ...key,
1153
+ return: true,
1154
+ meta: true
1155
+ });
1156
+ return;
1157
+ }
1158
+ if (onSpecialKey && onSpecialKey(input, key)) {
1159
+ return;
1160
+ }
1161
+ if (key.backspace || key.delete || input === "\b" || input === "\x7F" || input === "\b") {
1162
+ onInput(input, {
1163
+ ...key,
1164
+ backspace: true
1165
+ });
1166
+ return;
1167
+ }
1168
+ if (input && handleBracketedPasteSequences(input)) {
1169
+ return;
1170
+ }
1171
+ if (onPaste && shouldAggregatePasteChunk(input, pasteState.timeoutId !== null)) {
1172
+ setPasteState(({ chunks, timeoutId }) => {
1173
+ return {
1174
+ chunks: [...chunks, input],
1175
+ timeoutId: resetPasteTimeout(timeoutId)
1176
+ };
1177
+ });
1178
+ return;
1179
+ }
1180
+ onInput(input, key);
1181
+ };
1182
+ useInput2(wrappedOnInput, { isActive: focus });
1183
+ let renderedPlaceholder = placeholder ? chalk.hex(getTheme().secondaryText)(placeholder) : void 0;
1184
+ if (showCursor && focus) {
1185
+ renderedPlaceholder = placeholder.length > 0 ? chalk.inverse(placeholder[0]) + chalk.hex(getTheme().secondaryText)(placeholder.slice(1)) : chalk.inverse(" ");
1186
+ }
1187
+ const showPlaceholder = originalValue.length == 0 && placeholder;
1188
+ return /* @__PURE__ */ React4.createElement(Text3, { wrap: "truncate-end", dimColor: isDimmed }, showPlaceholder ? renderedPlaceholder : renderedValue);
1189
+ }
1190
+
1191
+ // apps/cli/src/ui/ui/components/model-selector/screens/ApiKeyScreen.tsx
1192
+ function ApiKeyScreen({
1193
+ theme,
1194
+ exitState,
1195
+ selectedProvider,
1196
+ apiKey,
1197
+ cursorOffset,
1198
+ handleApiKeyChange,
1199
+ handleApiKeySubmit,
1200
+ handleCursorOffsetChange,
1201
+ apiKeyCleanedNotification,
1202
+ isLoadingModels,
1203
+ providerBaseUrl,
1204
+ modelLoadError,
1205
+ formatApiKeyDisplay,
1206
+ getProviderLabel
1207
+ }) {
1208
+ const modelTypeText = "this model profile";
1209
+ return /* @__PURE__ */ React5.createElement(Box3, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React5.createElement(
1210
+ Box3,
1211
+ {
1212
+ flexDirection: "column",
1213
+ gap: 1,
1214
+ borderStyle: "round",
1215
+ borderColor: theme.secondaryBorder,
1216
+ paddingX: 2,
1217
+ paddingY: 1
1218
+ },
1219
+ /* @__PURE__ */ React5.createElement(Text4, { bold: true }, "API Key Setup", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1220
+ /* @__PURE__ */ React5.createElement(Box3, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React5.createElement(Text4, { bold: true }, "Enter your ", getProviderLabel(selectedProvider, 0).split(" (")[0], " ", "API key for ", modelTypeText, ":"), /* @__PURE__ */ React5.createElement(Box3, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.secondaryText }, "This key will be stored locally and used to access the", " ", selectedProvider, " API.", /* @__PURE__ */ React5.createElement(Newline, null), "Your key is never sent to our servers.", /* @__PURE__ */ React5.createElement(Newline, null), /* @__PURE__ */ React5.createElement(Newline, null), selectedProvider === "kimi" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://platform.moonshot.cn/console/api-keys")), selectedProvider === "deepseek" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://platform.deepseek.com/api_keys")), selectedProvider === "siliconflow" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://cloud.siliconflow.cn/i/oJWsm6io")), selectedProvider === "qwen" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://bailian.console.aliyun.com/?tab=model#/api-key")), selectedProvider === "glm" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://open.bigmodel.cn (API Keys section)")), selectedProvider === "glm-coding" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} This is for GLM Coding Plan API.", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "Use the same API key as regular GLM"), /* @__PURE__ */ React5.createElement(Newline, null), /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, "Note: This uses a special endpoint for coding tasks.")), selectedProvider === "minimax" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://www.minimax.io/platform/user-center/basic-information")), selectedProvider === "minimax-coding" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your Coding Plan API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://platform.minimaxi.com/user-center/payment/coding-plan"), /* @__PURE__ */ React5.createElement(Newline, null), /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, "Note: This requires a MiniMax Coding Plan subscription.")), selectedProvider === "baidu-qianfan" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://console.bce.baidu.com/iam/#/iam/accesslist")), selectedProvider === "anthropic" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from your provider dashboard."), selectedProvider === "openai" && /* @__PURE__ */ React5.createElement(React5.Fragment, null, "\u{1F4A1} Get your API key from:", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "https://platform.openai.com/api-keys")))), /* @__PURE__ */ React5.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box3, null, /* @__PURE__ */ React5.createElement(
1221
+ TextInput,
1222
+ {
1223
+ placeholder: "Paste your API key here...",
1224
+ value: apiKey,
1225
+ onChange: handleApiKeyChange,
1226
+ onSubmit: handleApiKeySubmit,
1227
+ mask: "*",
1228
+ columns: 80,
1229
+ cursorOffset,
1230
+ onChangeCursorOffset: handleCursorOffsetChange,
1231
+ showCursor: true
1232
+ }
1233
+ )), apiKey && /* @__PURE__ */ React5.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.secondaryText }, "Key: ", formatApiKeyDisplay(apiKey), " (", apiKey.length, " chars)"))), apiKeyCleanedNotification && /* @__PURE__ */ React5.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.success }, "\u2713 API key cleaned: removed line breaks and trimmed whitespace")), /* @__PURE__ */ React5.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion, dimColor: !apiKey }, "[Submit API Key]"), /* @__PURE__ */ React5.createElement(Text4, null, " - Press Enter to validate and continue"))), isLoadingModels && /* @__PURE__ */ React5.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "Validating API key and fetching models..."), providerBaseUrl && /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, "Endpoint: ", providerBaseUrl, "/v1/models")), modelLoadError && /* @__PURE__ */ React5.createElement(Box3, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text4, { color: "red" }, "\u274C API Key Validation Failed"), /* @__PURE__ */ React5.createElement(Text4, { color: "red" }, modelLoadError), providerBaseUrl && /* @__PURE__ */ React5.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, "Attempted endpoint: ", providerBaseUrl, "/v1/models")), /* @__PURE__ */ React5.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.warning }, "Please check your API key and try again."))), /* @__PURE__ */ React5.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, "Press ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "Enter"), " to continue,", " ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "Tab"), " to", " ", selectedProvider === "anthropic" || selectedProvider === "kimi" || selectedProvider === "deepseek" || selectedProvider === "qwen" || selectedProvider === "glm" || selectedProvider === "glm-coding" || selectedProvider === "minimax" || selectedProvider === "minimax-coding" || selectedProvider === "baidu-qianfan" || selectedProvider === "siliconflow" || selectedProvider === "custom-openai" ? "skip to manual model input" : "skip using a key", ", or ", /* @__PURE__ */ React5.createElement(Text4, { color: theme.suggestion }, "Esc"), " to go back")))
1234
+ ));
1235
+ }
1236
+
1237
+ // apps/cli/src/ui/ui/components/model-selector/screens/BaseUrlScreen.tsx
1238
+ import React6 from "react";
1239
+ import { Box as Box4, Newline as Newline2, Text as Text5 } from "ink";
1240
+ function BaseUrlScreen({
1241
+ theme,
1242
+ exitState,
1243
+ selectedProvider,
1244
+ isLoadingModels,
1245
+ modelLoadError,
1246
+ customBaseUrl,
1247
+ setCustomBaseUrl,
1248
+ handleCustomBaseUrlSubmit,
1249
+ customBaseUrlCursorOffset,
1250
+ setCustomBaseUrlCursorOffset,
1251
+ providerBaseUrl,
1252
+ setProviderBaseUrl,
1253
+ handleProviderBaseUrlSubmit,
1254
+ providerBaseUrlCursorOffset,
1255
+ setProviderBaseUrlCursorOffset
1256
+ }) {
1257
+ const isCustomOpenAI = selectedProvider === "custom-openai";
1258
+ if (isCustomOpenAI) {
1259
+ return /* @__PURE__ */ React6.createElement(Box4, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React6.createElement(
1260
+ Box4,
1261
+ {
1262
+ flexDirection: "column",
1263
+ gap: 1,
1264
+ borderStyle: "round",
1265
+ borderColor: theme.secondaryBorder,
1266
+ paddingX: 2,
1267
+ paddingY: 1
1268
+ },
1269
+ /* @__PURE__ */ React6.createElement(Text5, { bold: true }, "Custom API Server Setup", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1270
+ /* @__PURE__ */ React6.createElement(Box4, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React6.createElement(Text5, { bold: true }, "Enter your custom API URL:"), /* @__PURE__ */ React6.createElement(Box4, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React6.createElement(Text5, { color: theme.secondaryText }, "This is the base URL for your OpenAI-compatible API.", /* @__PURE__ */ React6.createElement(Newline2, null), "For example: https://api.example.com/v1")), /* @__PURE__ */ React6.createElement(Box4, null, /* @__PURE__ */ React6.createElement(
1271
+ TextInput,
1272
+ {
1273
+ placeholder: "https://api.example.com/v1",
1274
+ value: customBaseUrl,
1275
+ onChange: setCustomBaseUrl,
1276
+ onSubmit: handleCustomBaseUrlSubmit,
1277
+ columns: 100,
1278
+ cursorOffset: customBaseUrlCursorOffset,
1279
+ onChangeCursorOffset: setCustomBaseUrlCursorOffset,
1280
+ showCursor: !isLoadingModels,
1281
+ focus: !isLoadingModels
1282
+ }
1283
+ )), /* @__PURE__ */ React6.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, null, /* @__PURE__ */ React6.createElement(
1284
+ Text5,
1285
+ {
1286
+ color: isLoadingModels ? theme.secondaryText : theme.suggestion
1287
+ },
1288
+ "[Submit Base URL]"
1289
+ ), /* @__PURE__ */ React6.createElement(Text5, null, " - Press Enter or click to continue"))), /* @__PURE__ */ React6.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, "Press ", /* @__PURE__ */ React6.createElement(Text5, { color: theme.suggestion }, "Enter"), " to continue or", " ", /* @__PURE__ */ React6.createElement(Text5, { color: theme.suggestion }, "Esc"), " to go back")))
1290
+ ));
1291
+ }
1292
+ const providerName = providers[selectedProvider]?.name || selectedProvider;
1293
+ const defaultUrl = providers[selectedProvider]?.baseURL || "";
1294
+ return /* @__PURE__ */ React6.createElement(Box4, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React6.createElement(
1295
+ Box4,
1296
+ {
1297
+ flexDirection: "column",
1298
+ gap: 1,
1299
+ borderStyle: "round",
1300
+ borderColor: theme.secondaryBorder,
1301
+ paddingX: 2,
1302
+ paddingY: 1
1303
+ },
1304
+ /* @__PURE__ */ React6.createElement(Text5, { bold: true }, providerName, " API Configuration", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1305
+ /* @__PURE__ */ React6.createElement(Box4, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React6.createElement(Text5, { bold: true }, "Configure the API endpoint for ", providerName, ":"), /* @__PURE__ */ React6.createElement(Box4, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React6.createElement(Text5, { color: theme.secondaryText }, selectedProvider === "ollama" ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, "This is the URL of your Ollama server.", /* @__PURE__ */ React6.createElement(Newline2, null), "Default is http://localhost:11434/v1 for local Ollama installations.") : /* @__PURE__ */ React6.createElement(React6.Fragment, null, "This is the base URL for the ", providerName, " API.", /* @__PURE__ */ React6.createElement(Newline2, null), "You can modify this URL or press Enter to use the default."))), /* @__PURE__ */ React6.createElement(Box4, null, /* @__PURE__ */ React6.createElement(
1306
+ TextInput,
1307
+ {
1308
+ placeholder: defaultUrl,
1309
+ value: providerBaseUrl,
1310
+ onChange: setProviderBaseUrl,
1311
+ onSubmit: handleProviderBaseUrlSubmit,
1312
+ columns: 100,
1313
+ cursorOffset: providerBaseUrlCursorOffset,
1314
+ onChangeCursorOffset: setProviderBaseUrlCursorOffset,
1315
+ showCursor: !isLoadingModels,
1316
+ focus: !isLoadingModels
1317
+ }
1318
+ )), /* @__PURE__ */ React6.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, null, /* @__PURE__ */ React6.createElement(
1319
+ Text5,
1320
+ {
1321
+ color: isLoadingModels ? theme.secondaryText : theme.suggestion
1322
+ },
1323
+ "[Submit Base URL]"
1324
+ ), /* @__PURE__ */ React6.createElement(Text5, null, " - Press Enter or click to continue"))), isLoadingModels && /* @__PURE__ */ React6.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, { color: theme.success }, selectedProvider === "ollama" ? "Connecting to Ollama server..." : `Connecting to ${providerName}...`)), modelLoadError && /* @__PURE__ */ React6.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, { color: "red" }, "Error: ", modelLoadError)), /* @__PURE__ */ React6.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, "Press ", /* @__PURE__ */ React6.createElement(Text5, { color: theme.suggestion }, "Enter"), " to continue or", " ", /* @__PURE__ */ React6.createElement(Text5, { color: theme.suggestion }, "Esc"), " to go back")))
1325
+ ));
1326
+ }
1327
+
1328
+ // apps/cli/src/ui/ui/components/model-selector/screens/ConfirmationScreen.tsx
1329
+ import React7 from "react";
1330
+ import { Box as Box5, Text as Text6 } from "ink";
1331
+
1332
+ // apps/cli/src/ui/ui/components/model-selector/options.ts
1333
+ var REASONING_EFFORT_OPTIONS = [
1334
+ { label: "Low - Faster responses, less thorough reasoning", value: "low" },
1335
+ { label: "Medium - Balanced speed and reasoning depth", value: "medium" },
1336
+ {
1337
+ label: "High - Slower responses, more thorough reasoning",
1338
+ value: "high"
1339
+ }
1340
+ ];
1341
+ var CONTEXT_LENGTH_OPTIONS = [
1342
+ { label: "32K tokens", value: 32e3 },
1343
+ { label: "64K tokens", value: 64e3 },
1344
+ { label: "128K tokens", value: 128e3 },
1345
+ { label: "200K tokens", value: 2e5 },
1346
+ { label: "256K tokens", value: 256e3 },
1347
+ { label: "300K tokens", value: 3e5 },
1348
+ { label: "512K tokens", value: 512e3 },
1349
+ { label: "1000K tokens", value: 1e6 },
1350
+ { label: "2000K tokens", value: 2e6 },
1351
+ { label: "3000K tokens", value: 3e6 },
1352
+ { label: "5000K tokens", value: 5e6 },
1353
+ { label: "10000K tokens", value: 1e7 }
1354
+ ];
1355
+ var DEFAULT_CONTEXT_LENGTH = 128e3;
1356
+ var MAX_TOKENS_OPTIONS = [
1357
+ { label: "1K tokens", value: 1024 },
1358
+ { label: "2K tokens", value: 2048 },
1359
+ { label: "4K tokens", value: 4096 },
1360
+ { label: "8K tokens (recommended)", value: 8192 },
1361
+ { label: "16K tokens", value: 16384 },
1362
+ { label: "32K tokens", value: 32768 },
1363
+ { label: "64K tokens", value: 65536 },
1364
+ { label: "128K tokens", value: 131072 }
1365
+ ];
1366
+ var DEFAULT_MAX_TOKENS = 8192;
1367
+
1368
+ // apps/cli/src/ui/ui/components/model-selector/screens/ConfirmationScreen.tsx
1369
+ function ConfirmationScreen({
1370
+ theme,
1371
+ exitState,
1372
+ selectedProvider,
1373
+ selectedModel,
1374
+ resourceName,
1375
+ ollamaBaseUrl,
1376
+ customBaseUrl,
1377
+ apiKey,
1378
+ maxTokens,
1379
+ contextLength,
1380
+ supportsReasoningEffort,
1381
+ reasoningEffort,
1382
+ validationError,
1383
+ formatApiKeyDisplay,
1384
+ getProviderLabel
1385
+ }) {
1386
+ const providerDisplayName = getProviderLabel(selectedProvider, 0).split(
1387
+ " ("
1388
+ )[0];
1389
+ const showsApiKey = selectedProvider !== "ollama";
1390
+ return /* @__PURE__ */ React7.createElement(Box5, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(
1391
+ Box5,
1392
+ {
1393
+ flexDirection: "column",
1394
+ gap: 1,
1395
+ borderStyle: "round",
1396
+ borderColor: theme.secondaryBorder,
1397
+ paddingX: 2,
1398
+ paddingY: 1
1399
+ },
1400
+ /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Configuration Confirmation", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1401
+ /* @__PURE__ */ React7.createElement(Box5, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Confirm your model configuration:"), /* @__PURE__ */ React7.createElement(Box5, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React7.createElement(Text6, { color: theme.secondaryText }, "Please review your selections before saving.")), validationError && /* @__PURE__ */ React7.createElement(Box5, { flexDirection: "column", marginY: 1, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text6, { color: theme.error, bold: true }, "\u26A0 Configuration Error:"), /* @__PURE__ */ React7.createElement(Text6, { color: theme.error }, validationError)), /* @__PURE__ */ React7.createElement(Box5, { flexDirection: "column", marginY: 1, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Provider: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, providerDisplayName)), selectedProvider === "azure" && /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Resource Name: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, resourceName)), selectedProvider === "ollama" && /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Server URL: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, ollamaBaseUrl)), selectedProvider === "custom-openai" && /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "API Base URL: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, customBaseUrl)), /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Model: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, selectedModel)), apiKey && showsApiKey && /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "API Key: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, formatApiKeyDisplay(apiKey))), maxTokens && /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Max Tokens: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, maxTokens)), /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Context Length: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, CONTEXT_LENGTH_OPTIONS.find((opt) => opt.value === contextLength)?.label || `${contextLength.toLocaleString()} tokens`)), supportsReasoningEffort && /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Reasoning Effort: "), /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, reasoningEffort))), /* @__PURE__ */ React7.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "Press ", /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, "Esc"), " to go back to model parameters or ", /* @__PURE__ */ React7.createElement(Text6, { color: theme.suggestion }, "Enter"), " to save configuration")))
1402
+ ));
1403
+ }
1404
+
1405
+ // apps/cli/src/ui/ui/components/model-selector/screens/ConnectionTestScreen.tsx
1406
+ import React8 from "react";
1407
+ import { Box as Box6, Newline as Newline3, Text as Text7 } from "ink";
1408
+ function ConnectionTestScreen({
1409
+ theme,
1410
+ exitState,
1411
+ selectedProvider,
1412
+ getProviderLabel,
1413
+ isTestingConnection,
1414
+ connectionTestResult
1415
+ }) {
1416
+ const providerDisplayName = getProviderLabel(selectedProvider, 0).split(
1417
+ " ("
1418
+ )[0];
1419
+ return /* @__PURE__ */ React8.createElement(Box6, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React8.createElement(
1420
+ Box6,
1421
+ {
1422
+ flexDirection: "column",
1423
+ gap: 1,
1424
+ borderStyle: "round",
1425
+ borderColor: theme.secondaryBorder,
1426
+ paddingX: 2,
1427
+ paddingY: 1
1428
+ },
1429
+ /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Connection Test", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1430
+ /* @__PURE__ */ React8.createElement(Box6, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Testing connection to ", providerDisplayName, "..."), /* @__PURE__ */ React8.createElement(Box6, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React8.createElement(Text7, { color: theme.secondaryText }, "This will verify your configuration by sending a test request to the API.", selectedProvider === "minimax" && /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(Newline3, null), "For MiniMax, we'll test both v2 and v1 endpoints to find the best one."))), !connectionTestResult && !isTestingConnection && /* @__PURE__ */ React8.createElement(Box6, { marginY: 1 }, /* @__PURE__ */ React8.createElement(Text7, null, /* @__PURE__ */ React8.createElement(Text7, { color: theme.suggestion }, "Press Enter"), " to start the connection test")), isTestingConnection && /* @__PURE__ */ React8.createElement(Box6, { marginY: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: theme.suggestion }, "\u{1F504} Testing connection...")), connectionTestResult && /* @__PURE__ */ React8.createElement(Box6, { flexDirection: "column", marginY: 1, paddingX: 1 }, /* @__PURE__ */ React8.createElement(
1431
+ Text7,
1432
+ {
1433
+ color: connectionTestResult.success ? theme.success : "red"
1434
+ },
1435
+ connectionTestResult.message
1436
+ ), connectionTestResult.endpoint && /* @__PURE__ */ React8.createElement(Text7, { color: theme.secondaryText }, "Endpoint: ", connectionTestResult.endpoint), connectionTestResult.details && /* @__PURE__ */ React8.createElement(Text7, { color: theme.secondaryText }, "Details: ", connectionTestResult.details), connectionTestResult.success ? /* @__PURE__ */ React8.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: theme.success }, "\u2705 Automatically proceeding to confirmation...")) : /* @__PURE__ */ React8.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, null, /* @__PURE__ */ React8.createElement(Text7, { color: theme.suggestion }, "Press Enter"), " to retry test, or ", /* @__PURE__ */ React8.createElement(Text7, { color: theme.suggestion }, "Esc"), " to go back"))), /* @__PURE__ */ React8.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Press ", /* @__PURE__ */ React8.createElement(Text7, { color: theme.suggestion }, "Esc"), " to go back to context length")))
1437
+ ));
1438
+ }
1439
+
1440
+ // apps/cli/src/ui/ui/components/model-selector/screens/ContextLengthScreen.tsx
1441
+ import React9 from "react";
1442
+ import { Box as Box7, Text as Text8 } from "ink";
1443
+ function ContextLengthScreen({
1444
+ theme,
1445
+ exitState,
1446
+ contextLength
1447
+ }) {
1448
+ const selectedOption = CONTEXT_LENGTH_OPTIONS.find((opt) => opt.value === contextLength) || CONTEXT_LENGTH_OPTIONS[2];
1449
+ return /* @__PURE__ */ React9.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React9.createElement(
1450
+ Box7,
1451
+ {
1452
+ flexDirection: "column",
1453
+ gap: 1,
1454
+ borderStyle: "round",
1455
+ borderColor: theme.secondaryBorder,
1456
+ paddingX: 2,
1457
+ paddingY: 1
1458
+ },
1459
+ /* @__PURE__ */ React9.createElement(Text8, { bold: true }, "Context Length Configuration", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1460
+ /* @__PURE__ */ React9.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true }, "Choose the context window length for your model:"), /* @__PURE__ */ React9.createElement(Box7, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React9.createElement(Text8, { 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__ */ React9.createElement(Box7, { flexDirection: "column", marginY: 1 }, CONTEXT_LENGTH_OPTIONS.map((option, index) => {
1461
+ const isSelected = option.value === contextLength;
1462
+ return /* @__PURE__ */ React9.createElement(Box7, { key: option.value, flexDirection: "row" }, /* @__PURE__ */ React9.createElement(Text8, { color: isSelected ? "blue" : void 0 }, isSelected ? "\u2192 " : " ", option.label, option.value === DEFAULT_CONTEXT_LENGTH ? " (recommended)" : ""));
1463
+ })), /* @__PURE__ */ React9.createElement(Box7, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Selected:", " ", /* @__PURE__ */ React9.createElement(Text8, { color: theme.suggestion }, selectedOption.label))))
1464
+ ), /* @__PURE__ */ React9.createElement(Box7, { marginLeft: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u2191/\u2193 to select \xB7 Enter to continue \xB7 Esc to go back")));
1465
+ }
1466
+
1467
+ // apps/cli/src/ui/ui/components/model-selector/screens/ModelInputScreen.tsx
1468
+ import React10 from "react";
1469
+ import { Box as Box8, Newline as Newline4, Text as Text9 } from "ink";
1470
+ function ModelInputScreen({
1471
+ theme,
1472
+ exitState,
1473
+ selectedProvider,
1474
+ customModelName,
1475
+ setCustomModelName,
1476
+ handleCustomModelSubmit,
1477
+ customModelNameCursorOffset,
1478
+ setCustomModelNameCursorOffset
1479
+ }) {
1480
+ const modelTypeText = "this model profile";
1481
+ let screenTitle = "Manual Model Setup";
1482
+ let description = "Enter the model name manually";
1483
+ let placeholder = "gpt-4";
1484
+ let examples = 'For example: "gpt-4", "gpt-3.5-turbo", etc.';
1485
+ if (selectedProvider === "azure") {
1486
+ screenTitle = "Azure Model Setup";
1487
+ description = `Enter your Azure OpenAI deployment name for ${modelTypeText}:`;
1488
+ examples = 'For example: "gpt-4", "gpt-35-turbo", etc.';
1489
+ placeholder = "gpt-4";
1490
+ } else if (selectedProvider === "anthropic") {
1491
+ screenTitle = "Model Setup";
1492
+ description = `Enter the model name for ${modelTypeText}:`;
1493
+ examples = 'For example: "claude-3-5-sonnet-latest", "claude-3-5-haiku-latest", etc.';
1494
+ placeholder = "claude-3-5-sonnet-latest";
1495
+ } else if (selectedProvider === "bigdream") {
1496
+ screenTitle = "BigDream Model Setup";
1497
+ description = `Enter the BigDream model name for ${modelTypeText}:`;
1498
+ examples = 'For example: "claude-3-5-sonnet-latest", "claude-3-5-haiku-latest", etc.';
1499
+ placeholder = "claude-3-5-sonnet-latest";
1500
+ } else if (selectedProvider === "kimi") {
1501
+ screenTitle = "Kimi Model Setup";
1502
+ description = `Enter the Kimi model name for ${modelTypeText}:`;
1503
+ examples = 'For example: "kimi-k2-0711-preview"';
1504
+ placeholder = "kimi-k2-0711-preview";
1505
+ } else if (selectedProvider === "deepseek") {
1506
+ screenTitle = "DeepSeek Model Setup";
1507
+ description = `Enter the DeepSeek model name for ${modelTypeText}:`;
1508
+ examples = 'For example: "deepseek-chat", "deepseek-coder", "deepseek-reasoner", etc.';
1509
+ placeholder = "deepseek-chat";
1510
+ } else if (selectedProvider === "siliconflow") {
1511
+ screenTitle = "SiliconFlow Model Setup";
1512
+ description = `Enter the SiliconFlow model name for ${modelTypeText}:`;
1513
+ examples = 'For example: "Qwen/Qwen2.5-72B-Instruct", "meta-llama/Meta-Llama-3.1-8B-Instruct", etc.';
1514
+ placeholder = "Qwen/Qwen2.5-72B-Instruct";
1515
+ } else if (selectedProvider === "qwen") {
1516
+ screenTitle = "Qwen Model Setup";
1517
+ description = `Enter the Qwen model name for ${modelTypeText}:`;
1518
+ examples = 'For example: "qwen-plus", "qwen-turbo", "qwen-max", etc.';
1519
+ placeholder = "qwen-plus";
1520
+ } else if (selectedProvider === "glm") {
1521
+ screenTitle = "GLM Model Setup";
1522
+ description = `Enter the GLM model name for ${modelTypeText}:`;
1523
+ examples = 'For example: "glm-4", "glm-4v", "glm-3-turbo", etc.';
1524
+ placeholder = "glm-4";
1525
+ } else if (selectedProvider === "glm-coding") {
1526
+ screenTitle = "GLM Coding Plan Model Setup";
1527
+ description = `Enter the GLM model name for ${modelTypeText}:`;
1528
+ examples = 'For Coding Plan, typically use: "GLM-4.6" or other GLM models';
1529
+ placeholder = "GLM-4.6";
1530
+ } else if (selectedProvider === "minimax") {
1531
+ screenTitle = "MiniMax Model Setup";
1532
+ description = `Enter the MiniMax model name for ${modelTypeText}:`;
1533
+ examples = 'For example: "abab6.5s-chat", "abab6.5g-chat", "abab5.5s-chat", etc.';
1534
+ placeholder = "abab6.5s-chat";
1535
+ } else if (selectedProvider === "minimax-coding") {
1536
+ screenTitle = "MiniMax Coding Plan Model Setup";
1537
+ description = `Enter the MiniMax model name for ${modelTypeText}:`;
1538
+ examples = 'For Coding Plan, use: "MiniMax-M2"';
1539
+ placeholder = "MiniMax-M2";
1540
+ } else if (selectedProvider === "baidu-qianfan") {
1541
+ screenTitle = "Baidu Qianfan Model Setup";
1542
+ description = `Enter the Baidu Qianfan model name for ${modelTypeText}:`;
1543
+ examples = 'For example: "ERNIE-4.0-8K", "ERNIE-3.5-8K", "ERNIE-Speed-128K", etc.';
1544
+ placeholder = "ERNIE-4.0-8K";
1545
+ } else if (selectedProvider === "custom-openai") {
1546
+ screenTitle = "Custom API Model Setup";
1547
+ description = `Enter the model name for ${modelTypeText}:`;
1548
+ examples = "Enter the exact model name as supported by your API endpoint.";
1549
+ placeholder = "model-name";
1550
+ }
1551
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React10.createElement(
1552
+ Box8,
1553
+ {
1554
+ flexDirection: "column",
1555
+ gap: 1,
1556
+ borderStyle: "round",
1557
+ borderColor: theme.secondaryBorder,
1558
+ paddingX: 2,
1559
+ paddingY: 1
1560
+ },
1561
+ /* @__PURE__ */ React10.createElement(Text9, { bold: true }, screenTitle, " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1562
+ /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, description), /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React10.createElement(Text9, { color: theme.secondaryText }, selectedProvider === "azure" ? "This is the deployment name you configured in your Azure OpenAI resource." : selectedProvider === "anthropic" ? "This should match a model identifier supported by your API endpoint." : selectedProvider === "bigdream" ? "This should be a valid 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__ */ React10.createElement(Newline4, null), examples)), /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(
1563
+ TextInput,
1564
+ {
1565
+ placeholder,
1566
+ value: customModelName,
1567
+ onChange: setCustomModelName,
1568
+ onSubmit: handleCustomModelSubmit,
1569
+ columns: 100,
1570
+ cursorOffset: customModelNameCursorOffset,
1571
+ onChangeCursorOffset: setCustomModelNameCursorOffset,
1572
+ showCursor: true
1573
+ }
1574
+ )), /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, null, /* @__PURE__ */ React10.createElement(Text9, { color: theme.suggestion, dimColor: !customModelName }, "[Submit Model Name]"), /* @__PURE__ */ React10.createElement(Text9, null, " - Press Enter or click to continue"))), /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Press ", /* @__PURE__ */ React10.createElement(Text9, { color: theme.suggestion }, "Enter"), " to continue or", " ", /* @__PURE__ */ React10.createElement(Text9, { color: theme.suggestion }, "Esc"), " to go back")))
1575
+ ));
1576
+ }
1577
+
1578
+ // apps/cli/src/ui/ui/components/model-selector/screens/ModelParamsScreen.tsx
1579
+ import React11 from "react";
1580
+ import { Box as Box9, Text as Text10 } from "ink";
1581
+ function isReasoningEffortOption(value) {
1582
+ return value === "low" || value === "medium" || value === "high";
1583
+ }
1584
+ function ModelParamsScreen({
1585
+ theme,
1586
+ exitState,
1587
+ selectedModel,
1588
+ formFields,
1589
+ activeFieldIndex,
1590
+ setActiveFieldIndex,
1591
+ maxTokens,
1592
+ setMaxTokens,
1593
+ setSelectedMaxTokensPreset,
1594
+ setMaxTokensCursorOffset,
1595
+ reasoningEffortOptions,
1596
+ reasoningEffort,
1597
+ setReasoningEffort
1598
+ }) {
1599
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React11.createElement(
1600
+ Box9,
1601
+ {
1602
+ flexDirection: "column",
1603
+ gap: 1,
1604
+ borderStyle: "round",
1605
+ borderColor: theme.secondaryBorder,
1606
+ paddingX: 2,
1607
+ paddingY: 1
1608
+ },
1609
+ /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "Model Parameters", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1610
+ /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "Configure parameters for ", selectedModel, ":"), /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React11.createElement(Text10, { color: theme.secondaryText }, "Use ", /* @__PURE__ */ React11.createElement(Text10, { color: theme.suggestion }, "Tab"), " to navigate between fields. Press ", /* @__PURE__ */ React11.createElement(Text10, { color: theme.suggestion }, "Enter"), " to submit.")), /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column" }, formFields.map((field, index) => /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginY: 1, key: field.name }, field.component !== "button" ? /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(
1611
+ Text10,
1612
+ {
1613
+ bold: true,
1614
+ color: activeFieldIndex === index ? theme.success : void 0
1615
+ },
1616
+ field.label
1617
+ ), field.description && /* @__PURE__ */ React11.createElement(Text10, { color: theme.secondaryText }, field.description)) : /* @__PURE__ */ React11.createElement(
1618
+ Text10,
1619
+ {
1620
+ bold: true,
1621
+ color: activeFieldIndex === index ? theme.success : void 0
1622
+ },
1623
+ field.label
1624
+ ), /* @__PURE__ */ React11.createElement(Box9, { marginY: 1 }, activeFieldIndex === index ? field.component === "select" ? field.name === "maxTokens" ? /* @__PURE__ */ React11.createElement(
1625
+ Select,
1626
+ {
1627
+ options: field.options || [],
1628
+ onChange: (value) => {
1629
+ const numValue = parseInt(value);
1630
+ setMaxTokens(numValue.toString());
1631
+ setSelectedMaxTokensPreset(numValue);
1632
+ setMaxTokensCursorOffset(numValue.toString().length);
1633
+ setTimeout(() => {
1634
+ setActiveFieldIndex(index + 1);
1635
+ }, 100);
1636
+ },
1637
+ defaultValue: field.defaultValue,
1638
+ visibleOptionCount: 10
1639
+ }
1640
+ ) : /* @__PURE__ */ React11.createElement(
1641
+ Select,
1642
+ {
1643
+ options: reasoningEffortOptions,
1644
+ onChange: (value) => {
1645
+ if (isReasoningEffortOption(value)) {
1646
+ setReasoningEffort(value);
1647
+ }
1648
+ setTimeout(() => {
1649
+ setActiveFieldIndex(index + 1);
1650
+ }, 100);
1651
+ },
1652
+ defaultValue: reasoningEffort ?? void 0,
1653
+ visibleOptionCount: 8
1654
+ }
1655
+ ) : null : field.name === "maxTokens" ? /* @__PURE__ */ React11.createElement(Text10, { color: theme.secondaryText }, "Current:", " ", /* @__PURE__ */ React11.createElement(Text10, { color: theme.suggestion }, MAX_TOKENS_OPTIONS.find(
1656
+ (opt) => opt.value === parseInt(maxTokens)
1657
+ )?.label || `${maxTokens} tokens`)) : field.name === "reasoningEffort" ? /* @__PURE__ */ React11.createElement(Text10, { color: theme.secondaryText }, "Current:", " ", /* @__PURE__ */ React11.createElement(Text10, { color: theme.suggestion }, reasoningEffort)) : null))), /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "Press ", /* @__PURE__ */ React11.createElement(Text10, { color: theme.suggestion }, "Tab"), " to navigate,", " ", /* @__PURE__ */ React11.createElement(Text10, { color: theme.suggestion }, "Enter"), " to continue, or", " ", /* @__PURE__ */ React11.createElement(Text10, { color: theme.suggestion }, "Esc"), " to go back"))))
1658
+ ));
1659
+ }
1660
+
1661
+ // apps/cli/src/ui/ui/components/model-selector/screens/ModelSelectionScreen.tsx
1662
+ import React12 from "react";
1663
+ import { Box as Box10, Text as Text11 } from "ink";
1664
+ function ModelSelectionScreen({
1665
+ theme,
1666
+ exitState,
1667
+ selectedProvider,
1668
+ availableModels,
1669
+ modelSearchQuery,
1670
+ modelSearchCursorOffset,
1671
+ handleModelSearchChange,
1672
+ handleModelSearchCursorOffsetChange,
1673
+ modelOptions,
1674
+ handleModelSelection,
1675
+ getProviderLabel
1676
+ }) {
1677
+ const modelTypeText = "this model profile";
1678
+ return /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React12.createElement(
1679
+ Box10,
1680
+ {
1681
+ flexDirection: "column",
1682
+ gap: 1,
1683
+ borderStyle: "round",
1684
+ borderColor: theme.secondaryBorder,
1685
+ paddingX: 2,
1686
+ paddingY: 1
1687
+ },
1688
+ /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Model Selection", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1689
+ /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Select a model from", " ", getProviderLabel(selectedProvider, availableModels.length).split(
1690
+ " ("
1691
+ )[0], " ", "for ", modelTypeText, ":"), /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React12.createElement(Text11, { color: theme.secondaryText }, "This model profile can be assigned to different pointers (main, task, compact, quick) for various use cases.")), /* @__PURE__ */ React12.createElement(Box10, { marginY: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Search models:"), /* @__PURE__ */ React12.createElement(
1692
+ TextInput,
1693
+ {
1694
+ placeholder: "Type to filter models...",
1695
+ value: modelSearchQuery,
1696
+ onChange: handleModelSearchChange,
1697
+ columns: 100,
1698
+ cursorOffset: modelSearchCursorOffset,
1699
+ onChangeCursorOffset: handleModelSearchCursorOffsetChange,
1700
+ showCursor: true,
1701
+ focus: true
1702
+ }
1703
+ )), modelOptions.length > 0 ? /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(
1704
+ Select,
1705
+ {
1706
+ options: modelOptions,
1707
+ onChange: handleModelSelection,
1708
+ visibleOptionCount: 15
1709
+ }
1710
+ ), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Showing ", modelOptions.length, " of ", availableModels.length, " models")) : /* @__PURE__ */ React12.createElement(Box10, null, availableModels.length > 0 ? /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "No models match your search. Try a different query.") : /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "No models available for this provider.")), /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Press ", /* @__PURE__ */ React12.createElement(Text11, { color: theme.suggestion }, "Esc"), " to go back to API key input")))
1711
+ ));
1712
+ }
1713
+
1714
+ // apps/cli/src/ui/ui/components/model-selector/screens/PartnerCodingPlansScreen.tsx
1715
+ import React13 from "react";
1716
+ import { Box as Box11, Newline as Newline5, Text as Text12 } from "ink";
1717
+ function PartnerCodingPlansScreen({
1718
+ theme,
1719
+ exitState,
1720
+ containerPaddingY,
1721
+ containerGap,
1722
+ tightLayout,
1723
+ compactLayout,
1724
+ codingPlanOptions,
1725
+ codingPlanFocusIndex,
1726
+ codingReservedLines,
1727
+ getSafeVisibleOptionCount,
1728
+ renderWindowedOptions
1729
+ }) {
1730
+ const footerMarginTop = tightLayout ? 0 : 1;
1731
+ return /* @__PURE__ */ React13.createElement(Box11, { flexDirection: "column", gap: containerGap }, /* @__PURE__ */ React13.createElement(
1732
+ Box11,
1733
+ {
1734
+ flexDirection: "column",
1735
+ gap: containerGap,
1736
+ borderStyle: "round",
1737
+ borderColor: theme.secondaryBorder,
1738
+ paddingX: 2,
1739
+ paddingY: containerPaddingY
1740
+ },
1741
+ /* @__PURE__ */ React13.createElement(Text12, { bold: true }, "Partner Coding Plans", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1742
+ /* @__PURE__ */ React13.createElement(Box11, { flexDirection: "column", gap: containerGap }, /* @__PURE__ */ React13.createElement(Text12, { bold: true }, "Select a partner coding plan for specialized programming assistance:"), /* @__PURE__ */ React13.createElement(Box11, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React13.createElement(Text12, { color: theme.secondaryText }, compactLayout ? "Specialized coding models from partners." : /* @__PURE__ */ React13.createElement(React13.Fragment, null, "These are specialized models optimized for coding and development tasks.", /* @__PURE__ */ React13.createElement(Newline5, null), "They require specific coding plan subscriptions from the respective providers."))), renderWindowedOptions(
1743
+ codingPlanOptions,
1744
+ codingPlanFocusIndex,
1745
+ getSafeVisibleOptionCount(
1746
+ 5,
1747
+ codingPlanOptions.length,
1748
+ codingReservedLines
1749
+ )
1750
+ ), /* @__PURE__ */ React13.createElement(Box11, { marginTop: footerMarginTop }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Press ", /* @__PURE__ */ React13.createElement(Text12, { color: theme.suggestion }, "Esc"), " to go back to main menu")))
1751
+ ));
1752
+ }
1753
+
1754
+ // apps/cli/src/ui/ui/components/model-selector/screens/PartnerProvidersScreen.tsx
1755
+ import React14 from "react";
1756
+ import { Box as Box12, Text as Text13 } from "ink";
1757
+ function PartnerProvidersScreen({
1758
+ theme,
1759
+ exitState,
1760
+ containerPaddingY,
1761
+ containerGap,
1762
+ compactLayout,
1763
+ tightLayout,
1764
+ partnerProviderOptions,
1765
+ partnerProviderFocusIndex,
1766
+ partnerReservedLines,
1767
+ getSafeVisibleOptionCount,
1768
+ renderWindowedOptions
1769
+ }) {
1770
+ const footerMarginTop = tightLayout ? 0 : 1;
1771
+ return /* @__PURE__ */ React14.createElement(Box12, { flexDirection: "column", gap: containerGap }, /* @__PURE__ */ React14.createElement(
1772
+ Box12,
1773
+ {
1774
+ flexDirection: "column",
1775
+ gap: containerGap,
1776
+ borderStyle: "round",
1777
+ borderColor: theme.secondaryBorder,
1778
+ paddingX: 2,
1779
+ paddingY: containerPaddingY
1780
+ },
1781
+ /* @__PURE__ */ React14.createElement(Text13, { bold: true }, "Partner Providers", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1782
+ /* @__PURE__ */ React14.createElement(Box12, { flexDirection: "column", gap: containerGap }, /* @__PURE__ */ React14.createElement(Text13, { bold: true }, "Select a partner AI provider for this model profile:"), /* @__PURE__ */ React14.createElement(Box12, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React14.createElement(Text13, { color: theme.secondaryText }, compactLayout ? "Choose an official partner provider." : "Choose from official partner providers to access their models and services.")), renderWindowedOptions(
1783
+ partnerProviderOptions,
1784
+ partnerProviderFocusIndex,
1785
+ getSafeVisibleOptionCount(
1786
+ 6,
1787
+ partnerProviderOptions.length,
1788
+ partnerReservedLines
1789
+ )
1790
+ ), /* @__PURE__ */ React14.createElement(Box12, { marginTop: footerMarginTop }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Press ", /* @__PURE__ */ React14.createElement(Text13, { color: theme.suggestion }, "Esc"), " to go back to main menu")))
1791
+ ));
1792
+ }
1793
+
1794
+ // apps/cli/src/ui/ui/components/model-selector/screens/ProviderSelectionScreen.tsx
1795
+ import React16 from "react";
1796
+ import { Box as Box14, Newline as Newline6, Text as Text15 } from "ink";
1797
+
1798
+ // apps/cli/src/ui/ui/components/model-selector/ScreenContainer.tsx
1799
+ import React15 from "react";
1800
+ import { Box as Box13, Text as Text14 } from "ink";
1801
+ function ScreenContainer({
1802
+ title,
1803
+ exitState,
1804
+ children,
1805
+ paddingY = 1,
1806
+ gap = 1
1807
+ }) {
1808
+ const theme = getTheme();
1809
+ return /* @__PURE__ */ React15.createElement(
1810
+ Box13,
1811
+ {
1812
+ flexDirection: "column",
1813
+ gap,
1814
+ borderStyle: "round",
1815
+ borderColor: theme.secondaryBorder,
1816
+ paddingX: 2,
1817
+ paddingY
1818
+ },
1819
+ /* @__PURE__ */ React15.createElement(Text14, { bold: true }, title, " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1820
+ children
1821
+ );
1822
+ }
1823
+
1824
+ // apps/cli/src/ui/ui/components/model-selector/screens/ProviderSelectionScreen.tsx
1825
+ function ProviderSelectionScreen({
1826
+ theme,
1827
+ exitState,
1828
+ containerPaddingY,
1829
+ containerGap,
1830
+ compactLayout,
1831
+ tightLayout,
1832
+ mainMenuOptions,
1833
+ providerFocusIndex,
1834
+ providerReservedLines,
1835
+ getSafeVisibleOptionCount,
1836
+ renderWindowedOptions
1837
+ }) {
1838
+ return /* @__PURE__ */ React16.createElement(
1839
+ ScreenContainer,
1840
+ {
1841
+ title: "Provider Selection",
1842
+ exitState,
1843
+ paddingY: containerPaddingY,
1844
+ gap: containerGap,
1845
+ children: /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", gap: containerGap }, /* @__PURE__ */ React16.createElement(Text15, { bold: true }, "Select your preferred AI provider for this model profile:"), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React16.createElement(Text15, { color: theme.secondaryText }, compactLayout ? "Choose the provider to use for this profile." : /* @__PURE__ */ React16.createElement(React16.Fragment, null, "Choose the provider you want to use for this model profile.", /* @__PURE__ */ React16.createElement(Newline6, null), "This will determine which models are available to you."))), renderWindowedOptions(
1846
+ mainMenuOptions,
1847
+ providerFocusIndex,
1848
+ getSafeVisibleOptionCount(
1849
+ 5,
1850
+ mainMenuOptions.length,
1851
+ providerReservedLines
1852
+ )
1853
+ ), /* @__PURE__ */ React16.createElement(Box14, { marginTop: tightLayout ? 0 : 1 }, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "You can change this later by running", " ", /* @__PURE__ */ React16.createElement(Text15, { color: theme.suggestion }, "/model"), " again")))
1854
+ }
1855
+ );
1856
+ }
1857
+
1858
+ // apps/cli/src/ui/ui/components/model-selector/screens/ResourceNameScreen.tsx
1859
+ import React17 from "react";
1860
+ import { Box as Box15, Newline as Newline7, Text as Text16 } from "ink";
1861
+ function ResourceNameScreen({
1862
+ theme,
1863
+ exitState,
1864
+ resourceName,
1865
+ setResourceName,
1866
+ handleResourceNameSubmit,
1867
+ resourceNameCursorOffset,
1868
+ setResourceNameCursorOffset
1869
+ }) {
1870
+ return /* @__PURE__ */ React17.createElement(Box15, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React17.createElement(
1871
+ Box15,
1872
+ {
1873
+ flexDirection: "column",
1874
+ gap: 1,
1875
+ borderStyle: "round",
1876
+ borderColor: theme.secondaryBorder,
1877
+ paddingX: 2,
1878
+ paddingY: 1
1879
+ },
1880
+ /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "Azure Resource Setup", " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""),
1881
+ /* @__PURE__ */ React17.createElement(Box15, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "Enter your Azure OpenAI resource name:"), /* @__PURE__ */ React17.createElement(Box15, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React17.createElement(Text16, { color: theme.secondaryText }, "This is the name of your Azure OpenAI resource (without the full domain).", /* @__PURE__ */ React17.createElement(Newline7, null), 'For example, if your endpoint is "https://myresource.openai.azure.com", enter "myresource".')), /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(
1882
+ TextInput,
1883
+ {
1884
+ placeholder: "myazureresource",
1885
+ value: resourceName,
1886
+ onChange: setResourceName,
1887
+ onSubmit: handleResourceNameSubmit,
1888
+ columns: 100,
1889
+ cursorOffset: resourceNameCursorOffset,
1890
+ onChangeCursorOffset: setResourceNameCursorOffset,
1891
+ showCursor: true
1892
+ }
1893
+ )), /* @__PURE__ */ React17.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text16, null, /* @__PURE__ */ React17.createElement(Text16, { color: theme.suggestion, dimColor: !resourceName }, "[Submit Resource Name]"), /* @__PURE__ */ React17.createElement(Text16, null, " - Press Enter or click to continue"))), /* @__PURE__ */ React17.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, "Press ", /* @__PURE__ */ React17.createElement(Text16, { color: theme.suggestion }, "Enter"), " to continue or", " ", /* @__PURE__ */ React17.createElement(Text16, { color: theme.suggestion }, "Esc"), " to go back")))
1894
+ ));
1895
+ }
1896
+
1897
+ // apps/cli/src/ui/components/ModelSelector/ModelSelectorView.tsx
1898
+ function ModelSelectorView(props) {
1899
+ function getSafeVisibleOptionCount(requestedCount, optionLength, reservedLines = 10) {
1900
+ const rows = props.terminalRows;
1901
+ const available = Math.max(1, rows - reservedLines);
1902
+ return Math.max(1, Math.min(requestedCount, optionLength, available));
1903
+ }
1904
+ function renderWindowedOptions(options, focusedIndex, maxVisible) {
1905
+ if (options.length === 0) {
1906
+ return /* @__PURE__ */ React18.createElement(Text17, { color: props.theme.secondaryText }, "No options available.");
1907
+ }
1908
+ const visibleCount = Math.max(1, Math.min(maxVisible, options.length));
1909
+ const half = Math.floor(visibleCount / 2);
1910
+ const start = Math.max(
1911
+ 0,
1912
+ Math.min(focusedIndex - half, Math.max(0, options.length - visibleCount))
1913
+ );
1914
+ const end = Math.min(options.length, start + visibleCount);
1915
+ const showUp = start > 0;
1916
+ const showDown = end < options.length;
1917
+ return /* @__PURE__ */ React18.createElement(Box16, { flexDirection: "column", gap: 0 }, showUp && /* @__PURE__ */ React18.createElement(Text17, { color: props.theme.secondaryText }, figures.arrowUp, " More"), options.slice(start, end).map((opt, idx) => {
1918
+ const absoluteIndex = start + idx;
1919
+ const isFocused = absoluteIndex === focusedIndex;
1920
+ return /* @__PURE__ */ React18.createElement(Box16, { key: opt.value, flexDirection: "row" }, /* @__PURE__ */ React18.createElement(
1921
+ Text17,
1922
+ {
1923
+ color: isFocused ? props.theme.kode : props.theme.secondaryText
1924
+ },
1925
+ isFocused ? figures.pointer : " "
1926
+ ), /* @__PURE__ */ React18.createElement(
1927
+ Text17,
1928
+ {
1929
+ color: isFocused ? props.theme.text : props.theme.secondaryText,
1930
+ bold: isFocused
1931
+ },
1932
+ " ",
1933
+ opt.label
1934
+ ));
1935
+ }), showDown && /* @__PURE__ */ React18.createElement(Text17, { color: props.theme.secondaryText }, figures.arrowDown, " More"));
1936
+ }
1937
+ if (props.currentScreen === "apiKey") {
1938
+ return /* @__PURE__ */ React18.createElement(
1939
+ ApiKeyScreen,
1940
+ {
1941
+ theme: props.theme,
1942
+ exitState: props.exitState,
1943
+ selectedProvider: props.selectedProvider,
1944
+ apiKey: props.apiKey,
1945
+ cursorOffset: props.cursorOffset,
1946
+ handleApiKeyChange: props.handleApiKeyChange,
1947
+ handleApiKeySubmit: props.handleApiKeySubmit,
1948
+ handleCursorOffsetChange: props.handleCursorOffsetChange,
1949
+ apiKeyCleanedNotification: props.apiKeyCleanedNotification,
1950
+ isLoadingModels: props.isLoadingModels,
1951
+ providerBaseUrl: props.providerBaseUrl,
1952
+ modelLoadError: props.modelLoadError,
1953
+ formatApiKeyDisplay: props.formatApiKeyDisplay,
1954
+ getProviderLabel: props.getProviderLabel
1955
+ }
1956
+ );
1957
+ }
1958
+ if (props.currentScreen === "model") {
1959
+ return /* @__PURE__ */ React18.createElement(
1960
+ ModelSelectionScreen,
1961
+ {
1962
+ theme: props.theme,
1963
+ exitState: props.exitState,
1964
+ selectedProvider: props.selectedProvider,
1965
+ availableModels: props.availableModels,
1966
+ modelSearchQuery: props.modelSearchQuery,
1967
+ modelSearchCursorOffset: props.modelSearchCursorOffset,
1968
+ handleModelSearchChange: props.handleModelSearchChange,
1969
+ handleModelSearchCursorOffsetChange: props.handleModelSearchCursorOffsetChange,
1970
+ modelOptions: props.modelOptions,
1971
+ handleModelSelection: props.handleModelSelection,
1972
+ getProviderLabel: props.getProviderLabel
1973
+ }
1974
+ );
1975
+ }
1976
+ if (props.currentScreen === "modelParams") {
1977
+ const formFields = props.getFormFieldsForModelParams();
1978
+ return /* @__PURE__ */ React18.createElement(
1979
+ ModelParamsScreen,
1980
+ {
1981
+ theme: props.theme,
1982
+ exitState: props.exitState,
1983
+ selectedModel: props.selectedModel,
1984
+ formFields,
1985
+ activeFieldIndex: props.activeFieldIndex,
1986
+ setActiveFieldIndex: props.setActiveFieldIndex,
1987
+ maxTokens: props.maxTokens,
1988
+ setMaxTokens: props.setMaxTokens,
1989
+ setSelectedMaxTokensPreset: props.setSelectedMaxTokensPreset,
1990
+ setMaxTokensCursorOffset: props.setMaxTokensCursorOffset,
1991
+ reasoningEffortOptions: props.reasoningEffortOptions,
1992
+ reasoningEffort: props.reasoningEffort,
1993
+ setReasoningEffort: props.setReasoningEffort
1994
+ }
1995
+ );
1996
+ }
1997
+ if (props.currentScreen === "resourceName") {
1998
+ return /* @__PURE__ */ React18.createElement(
1999
+ ResourceNameScreen,
2000
+ {
2001
+ theme: props.theme,
2002
+ exitState: props.exitState,
2003
+ resourceName: props.resourceName,
2004
+ setResourceName: props.setResourceName,
2005
+ handleResourceNameSubmit: props.handleResourceNameSubmit,
2006
+ resourceNameCursorOffset: props.resourceNameCursorOffset,
2007
+ setResourceNameCursorOffset: props.setResourceNameCursorOffset
2008
+ }
2009
+ );
2010
+ }
2011
+ if (props.currentScreen === "baseUrl") {
2012
+ return /* @__PURE__ */ React18.createElement(
2013
+ BaseUrlScreen,
2014
+ {
2015
+ theme: props.theme,
2016
+ exitState: props.exitState,
2017
+ selectedProvider: props.selectedProvider,
2018
+ isLoadingModels: props.isLoadingModels,
2019
+ modelLoadError: props.modelLoadError,
2020
+ customBaseUrl: props.customBaseUrl,
2021
+ setCustomBaseUrl: props.setCustomBaseUrl,
2022
+ handleCustomBaseUrlSubmit: props.handleCustomBaseUrlSubmit,
2023
+ customBaseUrlCursorOffset: props.customBaseUrlCursorOffset,
2024
+ setCustomBaseUrlCursorOffset: props.setCustomBaseUrlCursorOffset,
2025
+ providerBaseUrl: props.providerBaseUrl,
2026
+ setProviderBaseUrl: props.setProviderBaseUrl,
2027
+ handleProviderBaseUrlSubmit: props.handleProviderBaseUrlSubmit,
2028
+ providerBaseUrlCursorOffset: props.providerBaseUrlCursorOffset,
2029
+ setProviderBaseUrlCursorOffset: props.setProviderBaseUrlCursorOffset
2030
+ }
2031
+ );
2032
+ }
2033
+ if (props.currentScreen === "modelInput") {
2034
+ return /* @__PURE__ */ React18.createElement(
2035
+ ModelInputScreen,
2036
+ {
2037
+ theme: props.theme,
2038
+ exitState: props.exitState,
2039
+ selectedProvider: props.selectedProvider,
2040
+ customModelName: props.customModelName,
2041
+ setCustomModelName: props.setCustomModelName,
2042
+ handleCustomModelSubmit: props.handleCustomModelSubmit,
2043
+ customModelNameCursorOffset: props.customModelNameCursorOffset,
2044
+ setCustomModelNameCursorOffset: props.setCustomModelNameCursorOffset
2045
+ }
2046
+ );
2047
+ }
2048
+ if (props.currentScreen === "contextLength") {
2049
+ return /* @__PURE__ */ React18.createElement(
2050
+ ContextLengthScreen,
2051
+ {
2052
+ theme: props.theme,
2053
+ exitState: props.exitState,
2054
+ contextLength: props.contextLength
2055
+ }
2056
+ );
2057
+ }
2058
+ if (props.currentScreen === "connectionTest") {
2059
+ return /* @__PURE__ */ React18.createElement(
2060
+ ConnectionTestScreen,
2061
+ {
2062
+ theme: props.theme,
2063
+ exitState: props.exitState,
2064
+ selectedProvider: props.selectedProvider,
2065
+ getProviderLabel: props.getProviderLabel,
2066
+ isTestingConnection: props.isTestingConnection,
2067
+ connectionTestResult: props.connectionTestResult
2068
+ }
2069
+ );
2070
+ }
2071
+ if (props.currentScreen === "confirmation") {
2072
+ return /* @__PURE__ */ React18.createElement(
2073
+ ConfirmationScreen,
2074
+ {
2075
+ theme: props.theme,
2076
+ exitState: props.exitState,
2077
+ selectedProvider: props.selectedProvider,
2078
+ selectedModel: props.selectedModel,
2079
+ resourceName: props.resourceName,
2080
+ ollamaBaseUrl: props.ollamaBaseUrl,
2081
+ customBaseUrl: props.customBaseUrl,
2082
+ apiKey: props.apiKey,
2083
+ maxTokens: props.maxTokens,
2084
+ contextLength: props.contextLength,
2085
+ supportsReasoningEffort: props.supportsReasoningEffort,
2086
+ reasoningEffort: props.reasoningEffort,
2087
+ validationError: props.validationError,
2088
+ formatApiKeyDisplay: props.formatApiKeyDisplay,
2089
+ getProviderLabel: props.getProviderLabel
2090
+ }
2091
+ );
2092
+ }
2093
+ if (props.currentScreen === "partnerProviders") {
2094
+ return /* @__PURE__ */ React18.createElement(
2095
+ PartnerProvidersScreen,
2096
+ {
2097
+ theme: props.theme,
2098
+ exitState: props.exitState,
2099
+ containerPaddingY: props.containerPaddingY,
2100
+ containerGap: props.containerGap,
2101
+ compactLayout: props.compactLayout,
2102
+ tightLayout: props.tightLayout,
2103
+ partnerProviderOptions: props.partnerProviderOptions,
2104
+ partnerProviderFocusIndex: props.partnerProviderFocusIndex,
2105
+ partnerReservedLines: props.partnerReservedLines,
2106
+ getSafeVisibleOptionCount,
2107
+ renderWindowedOptions
2108
+ }
2109
+ );
2110
+ }
2111
+ if (props.currentScreen === "partnerCodingPlans") {
2112
+ return /* @__PURE__ */ React18.createElement(
2113
+ PartnerCodingPlansScreen,
2114
+ {
2115
+ theme: props.theme,
2116
+ exitState: props.exitState,
2117
+ containerPaddingY: props.containerPaddingY,
2118
+ containerGap: props.containerGap,
2119
+ tightLayout: props.tightLayout,
2120
+ compactLayout: props.compactLayout,
2121
+ codingPlanOptions: props.codingPlanOptions,
2122
+ codingPlanFocusIndex: props.codingPlanFocusIndex,
2123
+ codingReservedLines: props.codingReservedLines,
2124
+ getSafeVisibleOptionCount,
2125
+ renderWindowedOptions
2126
+ }
2127
+ );
2128
+ }
2129
+ return /* @__PURE__ */ React18.createElement(
2130
+ ProviderSelectionScreen,
2131
+ {
2132
+ theme: props.theme,
2133
+ exitState: props.exitState,
2134
+ containerPaddingY: props.containerPaddingY,
2135
+ containerGap: props.containerGap,
2136
+ compactLayout: props.compactLayout,
2137
+ tightLayout: props.tightLayout,
2138
+ mainMenuOptions: props.mainMenuOptions,
2139
+ providerFocusIndex: props.providerFocusIndex,
2140
+ providerReservedLines: props.providerReservedLines,
2141
+ getSafeVisibleOptionCount,
2142
+ renderWindowedOptions
2143
+ }
2144
+ );
2145
+ }
2146
+
2147
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorController.tsx
2148
+ import { useEffect as useEffect2, useMemo as useMemo4 } from "react";
2149
+ import { useStdout } from "ink";
2150
+
2151
+ // apps/cli/src/ui/ui/components/model-selector/printModelConfig.ts
2152
+ import chalk2 from "chalk";
2153
+ function printModelConfig() {
2154
+ const config = getGlobalConfig();
2155
+ const modelProfiles = config.modelProfiles || [];
2156
+ const activeProfiles = modelProfiles.filter((p) => p.isActive);
2157
+ if (activeProfiles.length === 0) {
2158
+ process.stdout.write(
2159
+ `${chalk2.gray(" \u23BF No active model profiles configured")}
2160
+ `
2161
+ );
2162
+ return;
2163
+ }
2164
+ const profileSummary = activeProfiles.map((p) => `${p.name} (${p.provider}: ${p.modelName})`).join(" | ");
2165
+ process.stdout.write(`${chalk2.gray(` \u23BF ${profileSummary}`)}
2166
+ `);
2167
+ }
2168
+
2169
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorInput.ts
2170
+ import { useInput as useInput3 } from "ink";
2171
+ function useModelSelectorInput(args) {
2172
+ useInput3((input, key) => {
2173
+ if (args.currentScreen === "provider") {
2174
+ if (key.upArrow) {
2175
+ args.setProviderFocusIndex(
2176
+ (prev) => args.mainMenuOptions.length === 0 ? 0 : (prev - 1 + args.mainMenuOptions.length) % args.mainMenuOptions.length
2177
+ );
2178
+ return;
2179
+ }
2180
+ if (key.downArrow) {
2181
+ args.setProviderFocusIndex(
2182
+ (prev) => args.mainMenuOptions.length === 0 ? 0 : (prev + 1) % args.mainMenuOptions.length
2183
+ );
2184
+ return;
2185
+ }
2186
+ if (key.return) {
2187
+ const opt = args.mainMenuOptions[args.providerFocusIndex];
2188
+ if (opt) {
2189
+ args.handleProviderSelection(opt.value);
2190
+ }
2191
+ return;
2192
+ }
2193
+ }
2194
+ if (args.currentScreen === "partnerProviders") {
2195
+ if (key.upArrow) {
2196
+ args.setPartnerProviderFocusIndex(
2197
+ (prev) => args.partnerProviderOptions.length === 0 ? 0 : (prev - 1 + args.partnerProviderOptions.length) % args.partnerProviderOptions.length
2198
+ );
2199
+ return;
2200
+ }
2201
+ if (key.downArrow) {
2202
+ args.setPartnerProviderFocusIndex(
2203
+ (prev) => args.partnerProviderOptions.length === 0 ? 0 : (prev + 1) % args.partnerProviderOptions.length
2204
+ );
2205
+ return;
2206
+ }
2207
+ if (key.return) {
2208
+ const opt = args.partnerProviderOptions[args.partnerProviderFocusIndex];
2209
+ if (opt) {
2210
+ args.handleProviderSelection(opt.value);
2211
+ }
2212
+ return;
2213
+ }
2214
+ }
2215
+ if (args.currentScreen === "partnerCodingPlans") {
2216
+ if (key.upArrow) {
2217
+ args.setCodingPlanFocusIndex(
2218
+ (prev) => args.codingPlanOptions.length === 0 ? 0 : (prev - 1 + args.codingPlanOptions.length) % args.codingPlanOptions.length
2219
+ );
2220
+ return;
2221
+ }
2222
+ if (key.downArrow) {
2223
+ args.setCodingPlanFocusIndex(
2224
+ (prev) => args.codingPlanOptions.length === 0 ? 0 : (prev + 1) % args.codingPlanOptions.length
2225
+ );
2226
+ return;
2227
+ }
2228
+ if (key.return) {
2229
+ const opt = args.codingPlanOptions[args.codingPlanFocusIndex];
2230
+ if (opt) {
2231
+ args.handleProviderSelection(opt.value);
2232
+ }
2233
+ return;
2234
+ }
2235
+ }
2236
+ if (args.currentScreen === "apiKey" && key.return) {
2237
+ if (args.apiKey) {
2238
+ void args.handleApiKeySubmit(args.apiKey);
2239
+ }
2240
+ return;
2241
+ }
2242
+ if (args.currentScreen === "apiKey" && key.tab) {
2243
+ if (args.selectedProvider === "anthropic" || args.selectedProvider === "kimi" || args.selectedProvider === "deepseek" || args.selectedProvider === "qwen" || args.selectedProvider === "glm" || args.selectedProvider === "glm-coding" || args.selectedProvider === "minimax" || args.selectedProvider === "minimax-coding" || args.selectedProvider === "baidu-qianfan" || args.selectedProvider === "siliconflow" || args.selectedProvider === "custom-openai") {
2244
+ args.navigateTo("modelInput");
2245
+ return;
2246
+ }
2247
+ void args.fetchModelsWithRetry().catch((error) => {
2248
+ console.error("Final error after retries:", error);
2249
+ });
2250
+ return;
2251
+ }
2252
+ if (args.currentScreen === "resourceName" && key.return) {
2253
+ if (args.resourceName) {
2254
+ args.handleResourceNameSubmit(args.resourceName);
2255
+ }
2256
+ return;
2257
+ }
2258
+ if (args.currentScreen === "baseUrl" && key.return) {
2259
+ if (args.selectedProvider === "custom-openai") {
2260
+ args.handleCustomBaseUrlSubmit(args.customBaseUrl);
2261
+ } else {
2262
+ args.handleProviderBaseUrlSubmit(args.providerBaseUrl);
2263
+ }
2264
+ return;
2265
+ }
2266
+ if (args.currentScreen === "modelInput" && key.return) {
2267
+ if (args.customModelName) {
2268
+ args.handleCustomModelSubmit(args.customModelName);
2269
+ }
2270
+ return;
2271
+ }
2272
+ if (args.currentScreen === "confirmation" && key.return) {
2273
+ void args.handleConfirmation().catch((error) => {
2274
+ console.error("Error in handleConfirmation:", error);
2275
+ args.setValidationError(
2276
+ error instanceof Error ? error.message : "Unexpected error occurred"
2277
+ );
2278
+ });
2279
+ return;
2280
+ }
2281
+ if (args.currentScreen === "connectionTest") {
2282
+ if (key.return) {
2283
+ if (!args.isTestingConnection && !args.connectionTestResult) {
2284
+ args.handleConnectionTest();
2285
+ } else if (args.connectionTestResult && args.connectionTestResult.success) {
2286
+ args.navigateTo("confirmation");
2287
+ } else if (args.connectionTestResult && !args.connectionTestResult.success) {
2288
+ args.handleConnectionTest();
2289
+ }
2290
+ return;
2291
+ }
2292
+ }
2293
+ if (args.currentScreen === "contextLength") {
2294
+ if (key.return) {
2295
+ args.handleContextLengthSubmit();
2296
+ return;
2297
+ }
2298
+ if (key.upArrow) {
2299
+ const currentIndex = CONTEXT_LENGTH_OPTIONS.findIndex(
2300
+ (opt) => opt.value === args.contextLength
2301
+ );
2302
+ const newIndex = currentIndex > 0 ? currentIndex - 1 : currentIndex === -1 ? CONTEXT_LENGTH_OPTIONS.findIndex(
2303
+ (opt) => opt.value === DEFAULT_CONTEXT_LENGTH
2304
+ ) || 0 : CONTEXT_LENGTH_OPTIONS.length - 1;
2305
+ args.setContextLength(CONTEXT_LENGTH_OPTIONS[newIndex].value);
2306
+ return;
2307
+ }
2308
+ if (key.downArrow) {
2309
+ const currentIndex = CONTEXT_LENGTH_OPTIONS.findIndex(
2310
+ (opt) => opt.value === args.contextLength
2311
+ );
2312
+ const newIndex = currentIndex === -1 ? CONTEXT_LENGTH_OPTIONS.findIndex(
2313
+ (opt) => opt.value === DEFAULT_CONTEXT_LENGTH
2314
+ ) || 0 : (currentIndex + 1) % CONTEXT_LENGTH_OPTIONS.length;
2315
+ args.setContextLength(CONTEXT_LENGTH_OPTIONS[newIndex].value);
2316
+ return;
2317
+ }
2318
+ }
2319
+ if (args.currentScreen === "apiKey" && (key.ctrl && input === "v" || key.meta && input === "v")) {
2320
+ args.setModelLoadError(
2321
+ "Please use your terminal's paste functionality or type the API key manually"
2322
+ );
2323
+ return;
2324
+ }
2325
+ if (args.currentScreen === "modelParams" && key.tab) {
2326
+ const formFields = args.getFormFieldsForModelParams();
2327
+ args.setActiveFieldIndex((current) => (current + 1) % formFields.length);
2328
+ return;
2329
+ }
2330
+ if (args.currentScreen === "modelParams" && key.return) {
2331
+ const formFields = args.getFormFieldsForModelParams();
2332
+ const currentField = formFields[args.activeFieldIndex];
2333
+ if (currentField?.name === "submit" || args.activeFieldIndex === formFields.length - 1) {
2334
+ args.handleModelParamsSubmit();
2335
+ } else if (currentField?.component === "select") {
2336
+ args.setActiveFieldIndex(
2337
+ (current) => Math.min(current + 1, formFields.length - 1)
2338
+ );
2339
+ }
2340
+ return;
2341
+ }
2342
+ });
2343
+ }
2344
+
2345
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorMenus.ts
2346
+ import { useEffect, useMemo as useMemo2 } from "react";
2347
+ function useModelSelectorMenus(args) {
2348
+ function getProviderLabel(provider, modelCount) {
2349
+ if (providers[provider]) {
2350
+ const wipTag = "(WIP)";
2351
+ return `${providers[provider].name} ${providers[provider].status === "wip" ? wipTag : ""}`;
2352
+ }
2353
+ return `${provider}`;
2354
+ }
2355
+ const mainMenuOptions = useMemo2(
2356
+ () => [
2357
+ { value: "custom-openai", label: "Custom OpenAI-Compatible API" },
2358
+ { value: "custom-anthropic", label: "Custom Messages API (v1/messages)" },
2359
+ { value: "partnerProviders", label: "Partner Providers \u2192" },
2360
+ { value: "partnerCodingPlans", label: "Partner Coding Plans \u2192" },
2361
+ {
2362
+ value: "ollama",
2363
+ label: getProviderLabel("ollama", models_default.ollama?.length || 0)
2364
+ }
2365
+ ],
2366
+ []
2367
+ );
2368
+ const rankedProviders = useMemo2(
2369
+ () => [
2370
+ "openai",
2371
+ "anthropic",
2372
+ "gemini",
2373
+ "glm",
2374
+ "kimi",
2375
+ "minimax",
2376
+ "qwen",
2377
+ "deepseek",
2378
+ "openrouter",
2379
+ "burncloud",
2380
+ "siliconflow",
2381
+ "baidu-qianfan",
2382
+ "mistral",
2383
+ "xai",
2384
+ "groq",
2385
+ "azure"
2386
+ ],
2387
+ []
2388
+ );
2389
+ const partnerProviders = useMemo2(
2390
+ () => rankedProviders.filter(
2391
+ (provider) => providers[provider] && !provider.includes("coding") && provider !== "custom-openai" && provider !== "ollama"
2392
+ ),
2393
+ [rankedProviders]
2394
+ );
2395
+ const codingPlanProviders = useMemo2(
2396
+ () => Object.keys(providers).filter((provider) => provider.includes("coding")),
2397
+ []
2398
+ );
2399
+ const partnerProviderOptions = useMemo2(
2400
+ () => partnerProviders.map((provider) => {
2401
+ const modelCount = models_default[provider]?.length || 0;
2402
+ return {
2403
+ label: getProviderLabel(provider, modelCount),
2404
+ value: provider
2405
+ };
2406
+ }),
2407
+ [partnerProviders]
2408
+ );
2409
+ const codingPlanOptions = useMemo2(
2410
+ () => codingPlanProviders.map((provider) => {
2411
+ const modelCount = models_default[provider]?.length || 0;
2412
+ return {
2413
+ label: getProviderLabel(provider, modelCount),
2414
+ value: provider
2415
+ };
2416
+ }),
2417
+ [codingPlanProviders]
2418
+ );
2419
+ const providerReservedLines = 8 + args.containerPaddingY * 2 + args.containerGap * 2;
2420
+ const partnerReservedLines = 10 + args.containerPaddingY * 2 + args.containerGap * 3;
2421
+ const codingReservedLines = partnerReservedLines;
2422
+ const clampIndex = (index, length) => length === 0 ? 0 : Math.max(0, Math.min(index, length - 1));
2423
+ useEffect(() => {
2424
+ args.setProviderFocusIndex((prev) => clampIndex(prev, mainMenuOptions.length));
2425
+ }, [args, mainMenuOptions.length]);
2426
+ useEffect(() => {
2427
+ args.setPartnerProviderFocusIndex(
2428
+ (prev) => clampIndex(prev, partnerProviderOptions.length)
2429
+ );
2430
+ }, [args, partnerProviderOptions.length]);
2431
+ useEffect(() => {
2432
+ args.setCodingPlanFocusIndex(
2433
+ (prev) => clampIndex(prev, codingPlanOptions.length)
2434
+ );
2435
+ }, [args, codingPlanOptions.length]);
2436
+ return {
2437
+ mainMenuOptions,
2438
+ partnerProviderOptions,
2439
+ codingPlanOptions,
2440
+ providerReservedLines,
2441
+ partnerReservedLines,
2442
+ codingReservedLines,
2443
+ getProviderLabel
2444
+ };
2445
+ }
2446
+
2447
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorModelOptions.ts
2448
+ import { useMemo as useMemo3 } from "react";
2449
+ function formatNumber(num) {
2450
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
2451
+ if (num >= 1e3) return `${(num / 1e3).toFixed(0)}K`;
2452
+ return num.toString();
2453
+ }
2454
+ function getModelDetails(model) {
2455
+ const details = [];
2456
+ if (model.context_length) {
2457
+ details.push(`${formatNumber(model.context_length)} tokens`);
2458
+ } else if (model.max_tokens) {
2459
+ details.push(`${formatNumber(model.max_tokens)} tokens`);
2460
+ }
2461
+ if (model.supports_vision) details.push("vision");
2462
+ if (model.supports_function_calling) details.push("tools");
2463
+ return details.length > 0 ? ` (${details.join(", ")})` : "";
2464
+ }
2465
+ function sortModelsByPriority(models) {
2466
+ const priorityKeywords = [
2467
+ "claude",
2468
+ "kimi",
2469
+ "deepseek",
2470
+ "minimax",
2471
+ "o3",
2472
+ "gpt",
2473
+ "qwen"
2474
+ ];
2475
+ return models.sort((a, b) => {
2476
+ const aModelLower = a.model?.toLowerCase() || "";
2477
+ const bModelLower = b.model?.toLowerCase() || "";
2478
+ const aHasPriority = priorityKeywords.some(
2479
+ (keyword) => aModelLower.includes(keyword)
2480
+ );
2481
+ const bHasPriority = priorityKeywords.some(
2482
+ (keyword) => bModelLower.includes(keyword)
2483
+ );
2484
+ if (aHasPriority && !bHasPriority) return -1;
2485
+ if (!aHasPriority && bHasPriority) return 1;
2486
+ return a.model.localeCompare(b.model);
2487
+ });
2488
+ }
2489
+ function useModelSelectorModelOptions(args) {
2490
+ const ourModelNames = useMemo3(() => {
2491
+ return new Set(
2492
+ (models_default[args.selectedProvider] || []).map(
2493
+ (model) => model.model
2494
+ )
2495
+ );
2496
+ }, [args.selectedProvider]);
2497
+ const filteredModels = useMemo3(() => {
2498
+ return args.modelSearchQuery ? args.availableModels.filter(
2499
+ (model) => model.model?.toLowerCase().includes(args.modelSearchQuery.toLowerCase())
2500
+ ) : args.availableModels;
2501
+ }, [args.availableModels, args.modelSearchQuery]);
2502
+ const sortedFilteredModels = useMemo3(
2503
+ () => sortModelsByPriority([...filteredModels]),
2504
+ [filteredModels]
2505
+ );
2506
+ const modelOptions = useMemo3(
2507
+ () => sortedFilteredModels.map((model) => {
2508
+ const _isInOurModels = ourModelNames.has(model.model);
2509
+ return {
2510
+ label: `${model.model}${getModelDetails(model)}`,
2511
+ value: model.model
2512
+ };
2513
+ }),
2514
+ [ourModelNames, sortedFilteredModels]
2515
+ );
2516
+ return { modelOptions };
2517
+ }
2518
+
2519
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorState.tsx
2520
+ import { useState as useState3 } from "react";
2521
+
2522
+ // apps/cli/src/ui/ui/components/model-selector/state.ts
2523
+ function createInitialScreenStack(_opts) {
2524
+ return ["provider"];
2525
+ }
2526
+ function getCurrentScreen(stack) {
2527
+ return stack[stack.length - 1] ?? "provider";
2528
+ }
2529
+ function pushScreen(stack, screen) {
2530
+ return [...stack, screen];
2531
+ }
2532
+ function handleBackNavigation(stack) {
2533
+ const currentScreen = getCurrentScreen(stack);
2534
+ if (currentScreen === "partnerProviders" || currentScreen === "partnerCodingPlans") {
2535
+ return { stack: ["provider"], effect: { type: "resetProviderFocus" } };
2536
+ }
2537
+ if (currentScreen === "provider") {
2538
+ return { stack, effect: { type: "exit" } };
2539
+ }
2540
+ if (stack.length > 1) {
2541
+ return { stack: stack.slice(0, -1), effect: null };
2542
+ }
2543
+ return { stack: ["provider"], effect: { type: "resetProviderFocus" } };
2544
+ }
2545
+
2546
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorState.tsx
2547
+ function useModelSelectorState(opts) {
2548
+ const config = getGlobalConfig();
2549
+ const [screenStack, setScreenStack] = useState3(
2550
+ () => createInitialScreenStack({ skipModelType: opts.skipModelType })
2551
+ );
2552
+ const currentScreen = getCurrentScreen(screenStack);
2553
+ const navigateTo = (screen) => {
2554
+ setScreenStack((prev) => pushScreen(prev, screen));
2555
+ };
2556
+ const [selectedProvider, setSelectedProvider] = useState3(
2557
+ config.primaryProvider ?? "anthropic"
2558
+ );
2559
+ const [selectedModel, setSelectedModel] = useState3("");
2560
+ const [apiKey, setApiKey] = useState3("");
2561
+ const [maxTokens, setMaxTokens] = useState3(
2562
+ config.maxTokens?.toString() || DEFAULT_MAX_TOKENS.toString()
2563
+ );
2564
+ const [maxTokensMode, setMaxTokensMode] = useState3(
2565
+ "preset"
2566
+ );
2567
+ const [selectedMaxTokensPreset, setSelectedMaxTokensPreset] = useState3(config.maxTokens || DEFAULT_MAX_TOKENS);
2568
+ const [reasoningEffort, setReasoningEffort] = useState3("medium");
2569
+ const [supportsReasoningEffort, setSupportsReasoningEffort] = useState3(false);
2570
+ const [contextLength, setContextLength] = useState3(
2571
+ DEFAULT_CONTEXT_LENGTH
2572
+ );
2573
+ const [activeFieldIndex, setActiveFieldIndex] = useState3(0);
2574
+ const [maxTokensCursorOffset, setMaxTokensCursorOffset] = useState3(0);
2575
+ const [apiKeyCleanedNotification, setApiKeyCleanedNotification] = useState3(false);
2576
+ const [availableModels, setAvailableModels] = useState3([]);
2577
+ const [isLoadingModels, setIsLoadingModels] = useState3(false);
2578
+ const [modelLoadError, setModelLoadError] = useState3(null);
2579
+ const [modelSearchQuery, setModelSearchQuery] = useState3("");
2580
+ const [modelSearchCursorOffset, setModelSearchCursorOffset] = useState3(0);
2581
+ const [cursorOffset, setCursorOffset] = useState3(0);
2582
+ const [apiKeyEdited, setApiKeyEdited] = useState3(false);
2583
+ const [providerFocusIndex, setProviderFocusIndex] = useState3(0);
2584
+ const [partnerProviderFocusIndex, setPartnerProviderFocusIndex] = useState3(0);
2585
+ const [codingPlanFocusIndex, setCodingPlanFocusIndex] = useState3(0);
2586
+ const [fetchRetryCount, setFetchRetryCount] = useState3(0);
2587
+ const [isRetrying, setIsRetrying] = useState3(false);
2588
+ const [isTestingConnection, setIsTestingConnection] = useState3(false);
2589
+ const [connectionTestResult, setConnectionTestResult] = useState3(null);
2590
+ const [validationError, setValidationError] = useState3(null);
2591
+ const [resourceName, setResourceName] = useState3("");
2592
+ const [resourceNameCursorOffset, setResourceNameCursorOffset] = useState3(0);
2593
+ const [customModelName, setCustomModelName] = useState3("");
2594
+ const [customModelNameCursorOffset, setCustomModelNameCursorOffset] = useState3(0);
2595
+ const [ollamaBaseUrl, setOllamaBaseUrl] = useState3(
2596
+ "http://localhost:11434/v1"
2597
+ );
2598
+ const [customBaseUrl, setCustomBaseUrl] = useState3("");
2599
+ const [customBaseUrlCursorOffset, setCustomBaseUrlCursorOffset] = useState3(0);
2600
+ const [providerBaseUrl, setProviderBaseUrl] = useState3("");
2601
+ const [providerBaseUrlCursorOffset, setProviderBaseUrlCursorOffset] = useState3(0);
2602
+ return {
2603
+ screenStack,
2604
+ setScreenStack,
2605
+ currentScreen,
2606
+ navigateTo,
2607
+ selectedProvider,
2608
+ setSelectedProvider,
2609
+ selectedModel,
2610
+ setSelectedModel,
2611
+ apiKey,
2612
+ setApiKey,
2613
+ maxTokens,
2614
+ setMaxTokens,
2615
+ maxTokensMode,
2616
+ setMaxTokensMode,
2617
+ selectedMaxTokensPreset,
2618
+ setSelectedMaxTokensPreset,
2619
+ reasoningEffort,
2620
+ setReasoningEffort,
2621
+ supportsReasoningEffort,
2622
+ setSupportsReasoningEffort,
2623
+ contextLength,
2624
+ setContextLength,
2625
+ activeFieldIndex,
2626
+ setActiveFieldIndex,
2627
+ maxTokensCursorOffset,
2628
+ setMaxTokensCursorOffset,
2629
+ apiKeyCleanedNotification,
2630
+ setApiKeyCleanedNotification,
2631
+ availableModels,
2632
+ setAvailableModels,
2633
+ isLoadingModels,
2634
+ setIsLoadingModels,
2635
+ modelLoadError,
2636
+ setModelLoadError,
2637
+ modelSearchQuery,
2638
+ setModelSearchQuery,
2639
+ modelSearchCursorOffset,
2640
+ setModelSearchCursorOffset,
2641
+ cursorOffset,
2642
+ setCursorOffset,
2643
+ apiKeyEdited,
2644
+ setApiKeyEdited,
2645
+ providerFocusIndex,
2646
+ setProviderFocusIndex,
2647
+ partnerProviderFocusIndex,
2648
+ setPartnerProviderFocusIndex,
2649
+ codingPlanFocusIndex,
2650
+ setCodingPlanFocusIndex,
2651
+ fetchRetryCount,
2652
+ setFetchRetryCount,
2653
+ isRetrying,
2654
+ setIsRetrying,
2655
+ isTestingConnection,
2656
+ setIsTestingConnection,
2657
+ connectionTestResult,
2658
+ setConnectionTestResult,
2659
+ validationError,
2660
+ setValidationError,
2661
+ resourceName,
2662
+ setResourceName,
2663
+ resourceNameCursorOffset,
2664
+ setResourceNameCursorOffset,
2665
+ customModelName,
2666
+ setCustomModelName,
2667
+ customModelNameCursorOffset,
2668
+ setCustomModelNameCursorOffset,
2669
+ ollamaBaseUrl,
2670
+ setOllamaBaseUrl,
2671
+ customBaseUrl,
2672
+ setCustomBaseUrl,
2673
+ customBaseUrlCursorOffset,
2674
+ setCustomBaseUrlCursorOffset,
2675
+ providerBaseUrl,
2676
+ setProviderBaseUrl,
2677
+ providerBaseUrlCursorOffset,
2678
+ setProviderBaseUrlCursorOffset
2679
+ };
2680
+ }
2681
+
2682
+ // apps/cli/src/ui/ui/components/model-selector/actions/connectionTest/openAICompatibility.ts
2683
+ var OPENAI_COMPATIBLE_PROVIDERS = /* @__PURE__ */ new Set([
2684
+ "minimax",
2685
+ "kimi",
2686
+ "deepseek",
2687
+ "siliconflow",
2688
+ "qwen",
2689
+ "glm",
2690
+ "baidu-qianfan",
2691
+ "openai",
2692
+ "mistral",
2693
+ "xai",
2694
+ "groq",
2695
+ "custom-openai"
2696
+ ]);
2697
+ function isOpenAICompatibleProvider(provider) {
2698
+ return OPENAI_COMPATIBLE_PROVIDERS.has(provider);
2699
+ }
2700
+
2701
+ // apps/cli/src/ui/ui/components/model-selector/actions/connectionTest/testChatEndpoint.ts
2702
+ function asRecord(value) {
2703
+ if (!value || typeof value !== "object") return null;
2704
+ if (Array.isArray(value)) return null;
2705
+ return value;
2706
+ }
2707
+ async function testChatEndpoint({
2708
+ baseURL,
2709
+ endpointPath,
2710
+ endpointName,
2711
+ selectedProvider,
2712
+ selectedModel,
2713
+ apiKey,
2714
+ maxTokens
2715
+ }) {
2716
+ const testURL = `${baseURL.replace(/\/+$/, "")}${endpointPath}`;
2717
+ const testPayload = {
2718
+ model: selectedModel,
2719
+ messages: [
2720
+ {
2721
+ role: "user",
2722
+ content: 'Please respond with exactly "YES" (in capital letters) to confirm this connection is working.'
2723
+ }
2724
+ ],
2725
+ max_tokens: Math.max(parseInt(maxTokens) || 8192, 8192),
2726
+ temperature: 0,
2727
+ stream: false
2728
+ };
2729
+ if (selectedModel && selectedModel.toLowerCase().includes("gpt-5")) {
2730
+ debug.api("GPT5_PARAMETER_FIX_APPLY", { model: selectedModel });
2731
+ if (typeof testPayload.max_tokens === "number") {
2732
+ testPayload.max_completion_tokens = testPayload.max_tokens;
2733
+ delete testPayload.max_tokens;
2734
+ debug.api("GPT5_PARAMETER_FIX_MAX_TOKENS", {
2735
+ model: selectedModel,
2736
+ max_completion_tokens: testPayload.max_completion_tokens
2737
+ });
2738
+ }
2739
+ if (typeof testPayload.temperature === "number" && testPayload.temperature !== 1) {
2740
+ debug.api("GPT5_PARAMETER_FIX_TEMPERATURE", {
2741
+ model: selectedModel,
2742
+ from: testPayload.temperature,
2743
+ to: 1
2744
+ });
2745
+ testPayload.temperature = 1;
2746
+ }
2747
+ }
2748
+ const headers = {
2749
+ "Content-Type": "application/json"
2750
+ };
2751
+ if (selectedProvider === "azure") {
2752
+ headers["api-key"] = apiKey;
2753
+ } else {
2754
+ headers["Authorization"] = `Bearer ${apiKey}`;
2755
+ }
2756
+ try {
2757
+ const response = await fetch(testURL, {
2758
+ method: "POST",
2759
+ headers,
2760
+ body: JSON.stringify(testPayload)
2761
+ });
2762
+ if (response.ok) {
2763
+ const data = await response.json();
2764
+ debug.api("CONNECTION_TEST_RESPONSE", {
2765
+ provider: selectedProvider,
2766
+ endpoint: endpointPath,
2767
+ ok: true
2768
+ });
2769
+ let responseContent = "";
2770
+ const record = data;
2771
+ if (record && Array.isArray(record.choices) && record.choices.length > 0) {
2772
+ const firstChoice = record.choices[0];
2773
+ const message = firstChoice?.message ?? null;
2774
+ responseContent = String(message?.content ?? "");
2775
+ } else if (record && typeof record.reply === "string") {
2776
+ responseContent = record.reply;
2777
+ } else if (record && record.output) {
2778
+ const output = record.output;
2779
+ responseContent = String(output?.text ?? record.output ?? "");
2780
+ }
2781
+ debug.api("CONNECTION_TEST_RESPONSE_PARSED", {
2782
+ provider: selectedProvider,
2783
+ endpoint: endpointPath,
2784
+ contentLength: responseContent.length
2785
+ });
2786
+ const containsYes = responseContent.toLowerCase().includes("yes");
2787
+ if (containsYes) {
2788
+ return {
2789
+ success: true,
2790
+ message: `\u2705 Connection test passed with ${endpointName}`,
2791
+ endpoint: endpointPath,
2792
+ details: `Model responded correctly: "${responseContent.trim()}"`
2793
+ };
2794
+ }
2795
+ return {
2796
+ success: false,
2797
+ message: `\u26A0\uFE0F ${endpointName} connected but model response unexpected`,
2798
+ endpoint: endpointPath,
2799
+ details: `Expected "YES" but got: "${responseContent.trim() || "(empty response)"}"`
2800
+ };
2801
+ }
2802
+ const errorData = await response.json().catch(() => null);
2803
+ const errorRecord = asRecord(errorData);
2804
+ const nestedErrorMessage = (() => {
2805
+ const nestedError = asRecord(errorRecord?.error);
2806
+ const nestedMessage = nestedError?.message;
2807
+ return typeof nestedMessage === "string" ? nestedMessage : null;
2808
+ })();
2809
+ const errorMessage = nestedErrorMessage || (typeof errorRecord?.message === "string" ? errorRecord.message : null) || response.statusText;
2810
+ return {
2811
+ success: false,
2812
+ message: `\u274C ${endpointName} failed (${response.status})`,
2813
+ endpoint: endpointPath,
2814
+ details: `Error: ${errorMessage}`
2815
+ };
2816
+ } catch (error) {
2817
+ return {
2818
+ success: false,
2819
+ message: `\u274C ${endpointName} connection failed`,
2820
+ endpoint: endpointPath,
2821
+ details: error instanceof Error ? error.message : String(error)
2822
+ };
2823
+ }
2824
+ }
2825
+
2826
+ // apps/cli/src/ui/ui/components/model-selector/actions/connectionTest/testProviderSpecificEndpoint.ts
2827
+ async function testProviderSpecificEndpoint({
2828
+ baseURL,
2829
+ selectedProvider,
2830
+ apiKey
2831
+ }) {
2832
+ if (selectedProvider === "anthropic" || selectedProvider === "bigdream") {
2833
+ try {
2834
+ debug.api("PROVIDER_CONNECTION_TEST_NATIVE_SDK", {
2835
+ provider: selectedProvider
2836
+ });
2837
+ let testBaseURL = void 0;
2838
+ if (selectedProvider === "bigdream") {
2839
+ testBaseURL = baseURL || "https://api-key.info";
2840
+ } else if (selectedProvider === "anthropic") {
2841
+ testBaseURL = baseURL && baseURL !== "https://api.anthropic.com" ? baseURL : void 0;
2842
+ }
2843
+ const isValid = await verifyApiKey(apiKey, testBaseURL, selectedProvider);
2844
+ if (isValid) {
2845
+ return {
2846
+ success: true,
2847
+ message: `\u2705 ${selectedProvider} connection test passed`,
2848
+ endpoint: "/messages",
2849
+ details: "API key verified using native SDK"
2850
+ };
2851
+ }
2852
+ return {
2853
+ success: false,
2854
+ message: `\u274C ${selectedProvider} API key verification failed`,
2855
+ endpoint: "/messages",
2856
+ details: "Invalid API key. Please check your API key and try again."
2857
+ };
2858
+ } catch (error) {
2859
+ debug.warn("PROVIDER_CONNECTION_TEST_NATIVE_SDK_ERROR", {
2860
+ provider: selectedProvider,
2861
+ error: error instanceof Error ? error.message : String(error)
2862
+ });
2863
+ return {
2864
+ success: false,
2865
+ message: `\u274C ${selectedProvider} connection failed`,
2866
+ endpoint: "/messages",
2867
+ details: error instanceof Error ? error.message : String(error)
2868
+ };
2869
+ }
2870
+ }
2871
+ return {
2872
+ success: true,
2873
+ message: `\u2705 Configuration saved for ${selectedProvider}`,
2874
+ details: "Provider-specific testing not implemented yet"
2875
+ };
2876
+ }
2877
+
2878
+ // apps/cli/src/ui/ui/components/model-selector/actions/connectionTest/performConnectionTest.ts
2879
+ async function performConnectionTest({
2880
+ selectedProvider,
2881
+ selectedModel,
2882
+ apiKey,
2883
+ maxTokens,
2884
+ providerBaseUrl,
2885
+ customBaseUrl,
2886
+ resourceName
2887
+ }) {
2888
+ try {
2889
+ let testBaseURL = providerBaseUrl || providers[selectedProvider]?.baseURL || "";
2890
+ if (selectedProvider === "azure") {
2891
+ testBaseURL = `https://${resourceName}.openai.azure.com/openai/deployments/${selectedModel}`;
2892
+ } else if (selectedProvider === "custom-openai") {
2893
+ testBaseURL = customBaseUrl;
2894
+ }
2895
+ const isOpenAICompatible = isOpenAICompatibleProvider(selectedProvider);
2896
+ if (isOpenAICompatible) {
2897
+ const isGPT5 = selectedModel?.toLowerCase().includes("gpt-5");
2898
+ if (isGPT5) {
2899
+ debug.api("GPT5_CONNECTION_TEST_USING_SPECIALIZED", {
2900
+ model: selectedModel,
2901
+ provider: selectedProvider
2902
+ });
2903
+ const configValidation = validateGPT5Config({
2904
+ model: selectedModel,
2905
+ apiKey,
2906
+ baseURL: testBaseURL,
2907
+ maxTokens: parseInt(maxTokens) || 8192,
2908
+ provider: selectedProvider
2909
+ });
2910
+ if (!configValidation.valid) {
2911
+ return {
2912
+ success: false,
2913
+ message: "\u274C GPT-5 configuration validation failed",
2914
+ details: configValidation.errors.join("\n")
2915
+ };
2916
+ }
2917
+ return await testGPT5Connection({
2918
+ model: selectedModel,
2919
+ apiKey,
2920
+ baseURL: testBaseURL,
2921
+ maxTokens: parseInt(maxTokens) || 8192,
2922
+ provider: selectedProvider
2923
+ });
2924
+ }
2925
+ const endpointsToTry = [];
2926
+ if (selectedProvider === "minimax") {
2927
+ endpointsToTry.push(
2928
+ { path: "/text/chatcompletion_v2", name: "MiniMax v2 (recommended)" },
2929
+ { path: "/chat/completions", name: "Standard OpenAI" }
2930
+ );
2931
+ } else {
2932
+ endpointsToTry.push({
2933
+ path: "/chat/completions",
2934
+ name: "Standard OpenAI"
2935
+ });
2936
+ }
2937
+ let lastError = null;
2938
+ for (const endpoint of endpointsToTry) {
2939
+ try {
2940
+ const testResult = await testChatEndpoint({
2941
+ baseURL: testBaseURL,
2942
+ endpointPath: endpoint.path,
2943
+ endpointName: endpoint.name,
2944
+ selectedProvider,
2945
+ selectedModel,
2946
+ apiKey,
2947
+ maxTokens
2948
+ });
2949
+ if (testResult.success) {
2950
+ return testResult;
2951
+ }
2952
+ lastError = testResult;
2953
+ } catch (error) {
2954
+ lastError = {
2955
+ success: false,
2956
+ message: `Failed to test ${endpoint.name}`,
2957
+ endpoint: endpoint.path,
2958
+ details: error instanceof Error ? error.message : String(error)
2959
+ };
2960
+ }
2961
+ }
2962
+ return lastError || {
2963
+ success: false,
2964
+ message: "All endpoints failed",
2965
+ details: "No endpoints could be reached"
2966
+ };
2967
+ }
2968
+ return await testProviderSpecificEndpoint({
2969
+ baseURL: testBaseURL,
2970
+ selectedProvider,
2971
+ apiKey
2972
+ });
2973
+ } catch (error) {
2974
+ return {
2975
+ success: false,
2976
+ message: "Connection test failed",
2977
+ details: error instanceof Error ? error.message : String(error)
2978
+ };
2979
+ }
2980
+ }
2981
+
2982
+ // apps/cli/src/ui/ui/components/model-selector/actions/connectionTest/runConnectionTestFlow.ts
2983
+ async function runConnectionTestFlow({
2984
+ params,
2985
+ navigateTo,
2986
+ setTimeoutFn,
2987
+ performConnectionTestFn
2988
+ }) {
2989
+ const result = await (performConnectionTestFn ?? performConnectionTest)(
2990
+ params
2991
+ );
2992
+ if (result.success) {
2993
+ const schedule = setTimeoutFn ?? ((callback, delayMs) => setTimeout(callback, delayMs));
2994
+ schedule(() => {
2995
+ navigateTo("confirmation");
2996
+ }, 2e3);
2997
+ }
2998
+ return result;
2999
+ }
3000
+
3001
+ // apps/cli/src/ui/ui/components/model-selector/actions/providerSelection.ts
3002
+ function handleProviderSelection(provider, deps) {
3003
+ const {
3004
+ navigateTo,
3005
+ setPartnerProviderFocusIndex,
3006
+ setCodingPlanFocusIndex,
3007
+ setSelectedProvider,
3008
+ setProviderBaseUrl,
3009
+ saveConfiguration,
3010
+ onDone,
3011
+ selectedModel
3012
+ } = deps;
3013
+ if (provider === "partnerProviders") {
3014
+ setPartnerProviderFocusIndex(0);
3015
+ navigateTo("partnerProviders");
3016
+ return;
3017
+ } else if (provider === "partnerCodingPlans") {
3018
+ setCodingPlanFocusIndex(0);
3019
+ navigateTo("partnerCodingPlans");
3020
+ return;
3021
+ } else if (provider === "custom-anthropic") {
3022
+ setSelectedProvider("anthropic");
3023
+ setProviderBaseUrl("");
3024
+ navigateTo("baseUrl");
3025
+ return;
3026
+ }
3027
+ const providerType = provider;
3028
+ setSelectedProvider(providerType);
3029
+ if (provider === "custom") {
3030
+ saveConfiguration(providerType, selectedModel || "");
3031
+ onDone();
3032
+ } else if (provider === "custom-openai" || provider === "ollama") {
3033
+ const defaultBaseUrl = providers[providerType]?.baseURL || "";
3034
+ setProviderBaseUrl(defaultBaseUrl);
3035
+ navigateTo("baseUrl");
3036
+ } else {
3037
+ const defaultBaseUrl = providers[providerType]?.baseURL || "";
3038
+ setProviderBaseUrl(defaultBaseUrl);
3039
+ navigateTo("apiKey");
3040
+ }
3041
+ }
3042
+
3043
+ // apps/cli/src/ui/ui/components/model-selector/actions/saveConfiguration.ts
3044
+ async function saveModelConfiguration({
3045
+ provider,
3046
+ model,
3047
+ providerBaseUrl,
3048
+ resourceName,
3049
+ customBaseUrl,
3050
+ apiKey,
3051
+ maxTokens,
3052
+ contextLength,
3053
+ reasoningEffort,
3054
+ getModelManagerFn
3055
+ }) {
3056
+ let baseURL = providerBaseUrl || providers[provider]?.baseURL || "";
3057
+ let actualProvider = provider;
3058
+ if (provider === "anthropic") {
3059
+ actualProvider = "anthropic";
3060
+ baseURL = baseURL || "https://api.anthropic.com";
3061
+ }
3062
+ if (provider === "azure") {
3063
+ baseURL = `https://${resourceName}.openai.azure.com/openai/deployments/${model}`;
3064
+ } else if (provider === "custom-openai") {
3065
+ baseURL = customBaseUrl;
3066
+ }
3067
+ const modelManager = (getModelManagerFn ?? getModelManager)();
3068
+ const displayModel = model || "default";
3069
+ const modelDisplayName = `${providers[actualProvider]?.name || actualProvider} ${displayModel}`.trim();
3070
+ const modelConfig = {
3071
+ name: modelDisplayName,
3072
+ provider: actualProvider,
3073
+ modelName: model || actualProvider,
3074
+ // Use provider name if no specific model
3075
+ baseURL,
3076
+ apiKey: apiKey || "",
3077
+ maxTokens: parseInt(maxTokens) || DEFAULT_MAX_TOKENS,
3078
+ contextLength: contextLength || DEFAULT_CONTEXT_LENGTH,
3079
+ reasoningEffort
3080
+ };
3081
+ return await modelManager.addModel(modelConfig);
3082
+ }
3083
+ function applyPointersForNewModel({
3084
+ modelId,
3085
+ isOnboarding,
3086
+ targetPointer,
3087
+ setModelPointerFn,
3088
+ setAllPointersToModelFn
3089
+ }) {
3090
+ const setModelPointerImpl = setModelPointerFn ?? setModelPointer;
3091
+ const setAllPointersImpl = setAllPointersToModelFn ?? setAllPointersToModel;
3092
+ setModelPointerImpl("main", modelId);
3093
+ if (isOnboarding) {
3094
+ setAllPointersImpl(modelId);
3095
+ } else if (targetPointer && targetPointer !== "main") {
3096
+ setModelPointerImpl(targetPointer, modelId);
3097
+ }
3098
+ }
3099
+
3100
+ // apps/cli/src/ui/ui/components/model-selector/actions/fetchModels.ts
3101
+ import OpenAI from "openai";
3102
+ async function fetchModelsForProvider({
3103
+ selectedProvider,
3104
+ apiKey,
3105
+ providerBaseUrl,
3106
+ customBaseUrl,
3107
+ modelFetchers,
3108
+ setIsLoadingModels,
3109
+ setModelLoadError,
3110
+ setAvailableModels,
3111
+ navigateTo
3112
+ }) {
3113
+ setIsLoadingModels(true);
3114
+ setModelLoadError(null);
3115
+ try {
3116
+ if (selectedProvider === "anthropic") {
3117
+ const anthropicModels = await modelFetchers.fetchAnthropicCompatibleProviderModels({
3118
+ apiKey,
3119
+ providerBaseUrl,
3120
+ setModelLoadError
3121
+ });
3122
+ setAvailableModels(anthropicModels);
3123
+ navigateTo("model");
3124
+ return anthropicModels;
3125
+ }
3126
+ if (selectedProvider === "custom-openai") {
3127
+ const customModels = await modelFetchers.fetchCustomOpenAIModels({
3128
+ apiKey,
3129
+ customBaseUrl,
3130
+ setModelLoadError
3131
+ });
3132
+ setAvailableModels(customModels);
3133
+ navigateTo("model");
3134
+ return customModels;
3135
+ }
3136
+ if (selectedProvider === "gemini") {
3137
+ const geminiModels = await modelFetchers.fetchGeminiModels({
3138
+ apiKey,
3139
+ setModelLoadError
3140
+ });
3141
+ setAvailableModels(geminiModels);
3142
+ navigateTo("model");
3143
+ return geminiModels;
3144
+ }
3145
+ if (selectedProvider === "kimi") {
3146
+ const kimiModels = await modelFetchers.fetchKimiModels({
3147
+ apiKey,
3148
+ providerBaseUrl,
3149
+ setModelLoadError
3150
+ });
3151
+ setAvailableModels(kimiModels);
3152
+ navigateTo("model");
3153
+ return kimiModels;
3154
+ }
3155
+ if (selectedProvider === "deepseek") {
3156
+ const deepseekModels = await modelFetchers.fetchDeepSeekModels({
3157
+ apiKey,
3158
+ providerBaseUrl,
3159
+ setModelLoadError
3160
+ });
3161
+ setAvailableModels(deepseekModels);
3162
+ navigateTo("model");
3163
+ return deepseekModels;
3164
+ }
3165
+ if (selectedProvider === "siliconflow") {
3166
+ const siliconflowModels = await modelFetchers.fetchSiliconFlowModels({
3167
+ apiKey,
3168
+ providerBaseUrl,
3169
+ setModelLoadError
3170
+ });
3171
+ setAvailableModels(siliconflowModels);
3172
+ navigateTo("model");
3173
+ return siliconflowModels;
3174
+ }
3175
+ if (selectedProvider === "qwen") {
3176
+ const qwenModels = await modelFetchers.fetchQwenModels({
3177
+ apiKey,
3178
+ providerBaseUrl,
3179
+ setModelLoadError
3180
+ });
3181
+ setAvailableModels(qwenModels);
3182
+ navigateTo("model");
3183
+ return qwenModels;
3184
+ }
3185
+ if (selectedProvider === "glm") {
3186
+ const glmModels = await modelFetchers.fetchGLMModels({
3187
+ apiKey,
3188
+ providerBaseUrl,
3189
+ setModelLoadError
3190
+ });
3191
+ setAvailableModels(glmModels);
3192
+ navigateTo("model");
3193
+ return glmModels;
3194
+ }
3195
+ if (selectedProvider === "baidu-qianfan") {
3196
+ const baiduModels = await modelFetchers.fetchBaiduQianfanModels({
3197
+ apiKey,
3198
+ providerBaseUrl,
3199
+ setModelLoadError
3200
+ });
3201
+ setAvailableModels(baiduModels);
3202
+ navigateTo("model");
3203
+ return baiduModels;
3204
+ }
3205
+ if (selectedProvider === "azure") {
3206
+ navigateTo("modelInput");
3207
+ return [];
3208
+ }
3209
+ let baseURL = providerBaseUrl || providers[selectedProvider]?.baseURL;
3210
+ if (selectedProvider === "custom-openai") {
3211
+ baseURL = customBaseUrl;
3212
+ }
3213
+ const openai = new OpenAI({
3214
+ apiKey: apiKey || "dummy-key-for-ollama",
3215
+ // Ollama doesn't need a real key
3216
+ baseURL,
3217
+ dangerouslyAllowBrowser: true
3218
+ });
3219
+ const response = await openai.models.list();
3220
+ const fetchedModels = [];
3221
+ for (const model of response.data) {
3222
+ const record = model;
3223
+ const modelName = typeof record.modelName === "string" && record.modelName || typeof record.id === "string" && record.id || typeof record.name === "string" && record.name || typeof record.model === "string" && record.model || "unknown";
3224
+ const modelInfo = models_default[selectedProvider]?.find(
3225
+ (m) => m.model === modelName
3226
+ );
3227
+ fetchedModels.push({
3228
+ model: modelName,
3229
+ provider: selectedProvider,
3230
+ max_tokens: modelInfo?.max_output_tokens,
3231
+ supports_vision: modelInfo?.supports_vision || false,
3232
+ supports_function_calling: modelInfo?.supports_function_calling || false,
3233
+ supports_reasoning_effort: modelInfo?.supports_reasoning_effort || false
3234
+ });
3235
+ }
3236
+ setAvailableModels(fetchedModels);
3237
+ navigateTo("model");
3238
+ return fetchedModels;
3239
+ } catch (error) {
3240
+ console.error("Error fetching models:", error);
3241
+ throw error;
3242
+ } finally {
3243
+ setIsLoadingModels(false);
3244
+ }
3245
+ }
3246
+
3247
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers.ts
3248
+ var modelFetchers_exports = {};
3249
+ __export(modelFetchers_exports, {
3250
+ fetchAnthropicCompatibleProviderModels: () => fetchAnthropicCompatibleProviderModels,
3251
+ fetchBaiduQianfanModels: () => fetchBaiduQianfanModels,
3252
+ fetchCustomOpenAIModels: () => fetchCustomOpenAIModels,
3253
+ fetchDeepSeekModels: () => fetchDeepSeekModels,
3254
+ fetchGLMModels: () => fetchGLMModels,
3255
+ fetchGeminiModels: () => fetchGeminiModels,
3256
+ fetchKimiModels: () => fetchKimiModels,
3257
+ fetchMinimaxModels: () => fetchMinimaxModels,
3258
+ fetchQwenModels: () => fetchQwenModels,
3259
+ fetchSiliconFlowModels: () => fetchSiliconFlowModels
3260
+ });
3261
+
3262
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/anthropic.ts
3263
+ async function fetchAnthropicModels(baseURL, apiKey) {
3264
+ try {
3265
+ const response = await fetch(`${baseURL}/v1/models`, {
3266
+ method: "GET",
3267
+ headers: {
3268
+ "x-api-key": apiKey,
3269
+ "anthropic-version": "2023-06-01",
3270
+ "Content-Type": "application/json",
3271
+ Authorization: `Bearer ${apiKey}`
3272
+ }
3273
+ });
3274
+ if (!response.ok) {
3275
+ if (response.status === 401) {
3276
+ throw new Error(
3277
+ "Invalid API key. Please check your API key and try again."
3278
+ );
3279
+ } else if (response.status === 403) {
3280
+ throw new Error("API key does not have permission to access models.");
3281
+ } else if (response.status === 404) {
3282
+ throw new Error(
3283
+ "API endpoint not found. This provider may not support model listing."
3284
+ );
3285
+ } else if (response.status === 429) {
3286
+ throw new Error(
3287
+ "Too many requests. Please wait a moment and try again."
3288
+ );
3289
+ } else if (response.status >= 500) {
3290
+ throw new Error(
3291
+ "API service is temporarily unavailable. Please try again later."
3292
+ );
3293
+ } else {
3294
+ throw new Error(`Unable to connect to API (${response.status}).`);
3295
+ }
3296
+ }
3297
+ const data = await response.json();
3298
+ let models = [];
3299
+ if (data && data.data && Array.isArray(data.data)) {
3300
+ models = data.data;
3301
+ } else if (Array.isArray(data)) {
3302
+ models = data;
3303
+ } else if (data && data.models && Array.isArray(data.models)) {
3304
+ models = data.models;
3305
+ } else {
3306
+ throw new Error("API returned unexpected response format.");
3307
+ }
3308
+ return models;
3309
+ } catch (error) {
3310
+ if (error instanceof Error && (error.message.includes("API key") || error.message.includes("API endpoint") || error.message.includes("API service") || error.message.includes("response format"))) {
3311
+ throw error;
3312
+ }
3313
+ if (error instanceof Error && error.message.includes("fetch")) {
3314
+ throw new Error(
3315
+ "Unable to connect to the API. Please check the base URL and your internet connection."
3316
+ );
3317
+ }
3318
+ throw new Error(
3319
+ "Failed to fetch models from API. Please check your configuration and try again."
3320
+ );
3321
+ }
3322
+ }
3323
+ async function fetchAnthropicCompatibleModelsWithFallback({
3324
+ baseURL,
3325
+ provider,
3326
+ apiKey,
3327
+ apiKeyUrl,
3328
+ setModelLoadError
3329
+ }) {
3330
+ let lastError = null;
3331
+ try {
3332
+ const models = await fetchAnthropicModels(baseURL, apiKey);
3333
+ return models.map((model) => ({
3334
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3335
+ provider,
3336
+ max_tokens: model.max_tokens || 8192,
3337
+ supports_vision: model.supports_vision || true,
3338
+ supports_function_calling: model.supports_function_calling || true,
3339
+ supports_reasoning_effort: false
3340
+ }));
3341
+ } catch (error) {
3342
+ lastError = error;
3343
+ debug.warn("MODEL_FETCH_NATIVE_API_FAILED", {
3344
+ provider,
3345
+ error: error instanceof Error ? error.message : String(error)
3346
+ });
3347
+ }
3348
+ try {
3349
+ const models = await fetchCustomModels(baseURL, apiKey);
3350
+ return models.map((model) => ({
3351
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3352
+ provider,
3353
+ max_tokens: model.max_tokens || 8192,
3354
+ supports_vision: model.supports_vision || false,
3355
+ supports_function_calling: model.supports_function_calling || true,
3356
+ supports_reasoning_effort: false
3357
+ }));
3358
+ } catch (error) {
3359
+ lastError = error;
3360
+ debug.warn("MODEL_FETCH_OPENAI_API_FAILED", {
3361
+ provider,
3362
+ error: error instanceof Error ? error.message : String(error)
3363
+ });
3364
+ }
3365
+ let errorMessage = `Failed to fetch ${provider} models using both native and OpenAI-compatible API formats`;
3366
+ if (lastError instanceof Error) {
3367
+ errorMessage = lastError.message;
3368
+ }
3369
+ if (errorMessage.includes("API key")) {
3370
+ errorMessage += apiKeyUrl ? `
3371
+
3372
+ \u{1F4A1} Tip: Get your API key from ${apiKeyUrl}` : "\n\n\u{1F4A1} Tip: Check that your API key is set and valid for this provider";
3373
+ } else if (errorMessage.includes("permission")) {
3374
+ errorMessage += `
3375
+
3376
+ \u{1F4A1} Tip: Make sure your API key has access to the ${provider} API`;
3377
+ } else if (errorMessage.includes("connection")) {
3378
+ errorMessage += "\n\n\u{1F4A1} Tip: Check your internet connection and try again";
3379
+ }
3380
+ setModelLoadError(errorMessage);
3381
+ throw new Error(errorMessage);
3382
+ }
3383
+ async function fetchAnthropicCompatibleProviderModels({
3384
+ apiKey,
3385
+ providerBaseUrl,
3386
+ setModelLoadError
3387
+ }) {
3388
+ const defaultBaseURL = "https://api.anthropic.com";
3389
+ const apiKeyUrl = "";
3390
+ const actualProvider = "anthropic";
3391
+ const baseURL = providerBaseUrl || defaultBaseURL;
3392
+ return fetchAnthropicCompatibleModelsWithFallback({
3393
+ baseURL,
3394
+ provider: actualProvider,
3395
+ apiKey,
3396
+ apiKeyUrl,
3397
+ setModelLoadError
3398
+ });
3399
+ }
3400
+
3401
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/baiduQianfan.ts
3402
+ async function fetchBaiduQianfanModels({
3403
+ apiKey,
3404
+ providerBaseUrl,
3405
+ setModelLoadError
3406
+ }) {
3407
+ try {
3408
+ const baseURL = providerBaseUrl || "https://qianfan.baidubce.com/v2";
3409
+ const models = await fetchCustomModels(baseURL, apiKey);
3410
+ return models.map((model) => ({
3411
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3412
+ provider: "baidu-qianfan",
3413
+ max_tokens: model.max_tokens || 8192,
3414
+ supports_vision: false,
3415
+ supports_function_calling: true,
3416
+ supports_reasoning_effort: false
3417
+ }));
3418
+ } catch (error) {
3419
+ let errorMessage = "Failed to fetch Baidu Qianfan models";
3420
+ if (error instanceof Error) {
3421
+ errorMessage = error.message;
3422
+ }
3423
+ if (errorMessage.includes("API key")) {
3424
+ errorMessage += "\n\n\u{1F4A1} Tip: Get your API key from https://console.bce.baidu.com/iam/#/iam/accesslist";
3425
+ } else if (errorMessage.includes("permission")) {
3426
+ errorMessage += "\n\n\u{1F4A1} Tip: Make sure your API key has access to the Baidu Qianfan API";
3427
+ } else if (errorMessage.includes("connection")) {
3428
+ errorMessage += "\n\n\u{1F4A1} Tip: Check your internet connection and try again";
3429
+ }
3430
+ setModelLoadError(errorMessage);
3431
+ throw error;
3432
+ }
3433
+ }
3434
+
3435
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/customOpenAI.ts
3436
+ async function fetchCustomOpenAIModels({
3437
+ apiKey,
3438
+ customBaseUrl,
3439
+ setModelLoadError
3440
+ }) {
3441
+ try {
3442
+ const models = await fetchCustomModels(customBaseUrl, apiKey);
3443
+ return models.map((model) => ({
3444
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3445
+ provider: "custom-openai",
3446
+ max_tokens: model.max_tokens || 4096,
3447
+ supports_vision: false,
3448
+ // Default to false, could be enhanced
3449
+ supports_function_calling: true,
3450
+ supports_reasoning_effort: false
3451
+ }));
3452
+ } catch (error) {
3453
+ let errorMessage = "Failed to fetch custom API models";
3454
+ if (error instanceof Error) {
3455
+ errorMessage = error.message;
3456
+ }
3457
+ if (errorMessage.includes("API key")) {
3458
+ errorMessage += "\n\n\u{1F4A1} Tip: Check that your API key is valid for this endpoint";
3459
+ } else if (errorMessage.includes("endpoint not found")) {
3460
+ errorMessage += "\n\n\u{1F4A1} Tip: Make sure the base URL ends with /v1 and supports OpenAI-compatible API";
3461
+ } else if (errorMessage.includes("connect")) {
3462
+ errorMessage += "\n\n\u{1F4A1} Tip: Verify the base URL is correct and accessible";
3463
+ } else if (errorMessage.includes("response format")) {
3464
+ errorMessage += "\n\n\u{1F4A1} Tip: This API may not be fully OpenAI-compatible";
3465
+ }
3466
+ setModelLoadError(errorMessage);
3467
+ throw error;
3468
+ }
3469
+ }
3470
+
3471
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/deepseek.ts
3472
+ async function fetchDeepSeekModels({
3473
+ apiKey,
3474
+ providerBaseUrl,
3475
+ setModelLoadError
3476
+ }) {
3477
+ try {
3478
+ const baseURL = providerBaseUrl || "https://api.deepseek.com";
3479
+ const models = await fetchCustomModels(baseURL, apiKey);
3480
+ return models.map((model) => ({
3481
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3482
+ provider: "deepseek",
3483
+ max_tokens: model.max_tokens || 8192,
3484
+ supports_vision: false,
3485
+ // Default to false, could be enhanced
3486
+ supports_function_calling: true,
3487
+ supports_reasoning_effort: false
3488
+ }));
3489
+ } catch (error) {
3490
+ let errorMessage = "Failed to fetch DeepSeek models";
3491
+ if (error instanceof Error) {
3492
+ errorMessage = error.message;
3493
+ }
3494
+ if (errorMessage.includes("API key")) {
3495
+ errorMessage += "\n\n\u{1F4A1} Tip: Get your API key from https://platform.deepseek.com/api_keys";
3496
+ } else if (errorMessage.includes("permission")) {
3497
+ errorMessage += "\n\n\u{1F4A1} Tip: Make sure your API key has access to the DeepSeek API";
3498
+ } else if (errorMessage.includes("connection")) {
3499
+ errorMessage += "\n\n\u{1F4A1} Tip: Check your internet connection and try again";
3500
+ }
3501
+ setModelLoadError(errorMessage);
3502
+ throw error;
3503
+ }
3504
+ }
3505
+
3506
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/gemini.ts
3507
+ async function fetchGeminiModels({
3508
+ apiKey,
3509
+ setModelLoadError
3510
+ }) {
3511
+ try {
3512
+ const response = await fetch(
3513
+ `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`
3514
+ );
3515
+ if (!response.ok) {
3516
+ const errorData = await response.json();
3517
+ throw new Error(
3518
+ errorData.error?.message || `API error: ${response.status}`
3519
+ );
3520
+ }
3521
+ const { models } = await response.json();
3522
+ return models.filter(
3523
+ (model) => model.supportedGenerationMethods.includes("generateContent")
3524
+ ).map((model) => ({
3525
+ model: model.name.replace("models/", ""),
3526
+ provider: "gemini",
3527
+ max_tokens: model.outputTokenLimit,
3528
+ supports_vision: model.supportedGenerationMethods.includes("generateContent"),
3529
+ supports_function_calling: model.supportedGenerationMethods.includes("generateContent")
3530
+ }));
3531
+ } catch (error) {
3532
+ setModelLoadError(error instanceof Error ? error.message : "Unknown error");
3533
+ throw error;
3534
+ }
3535
+ }
3536
+
3537
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/glm.ts
3538
+ async function fetchGLMModels({
3539
+ apiKey,
3540
+ providerBaseUrl,
3541
+ setModelLoadError
3542
+ }) {
3543
+ try {
3544
+ const baseURL = providerBaseUrl || "https://open.bigmodel.cn/api/paas/v4";
3545
+ const models = await fetchCustomModels(baseURL, apiKey);
3546
+ return models.map((model) => ({
3547
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3548
+ provider: "glm",
3549
+ max_tokens: model.max_tokens || 8192,
3550
+ supports_vision: false,
3551
+ supports_function_calling: true,
3552
+ supports_reasoning_effort: false
3553
+ }));
3554
+ } catch (error) {
3555
+ let errorMessage = "Failed to fetch GLM models";
3556
+ if (error instanceof Error) {
3557
+ errorMessage = error.message;
3558
+ }
3559
+ if (errorMessage.includes("API key")) {
3560
+ errorMessage += "\n\n\u{1F4A1} Tip: Get your API key from https://open.bigmodel.cn (API Keys section)";
3561
+ } else if (errorMessage.includes("permission")) {
3562
+ errorMessage += "\n\n\u{1F4A1} Tip: Make sure your API key has access to the GLM API";
3563
+ } else if (errorMessage.includes("connection")) {
3564
+ errorMessage += "\n\n\u{1F4A1} Tip: Check your internet connection and try again";
3565
+ }
3566
+ setModelLoadError(errorMessage);
3567
+ throw error;
3568
+ }
3569
+ }
3570
+
3571
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/kimi.ts
3572
+ async function fetchKimiModels({
3573
+ apiKey,
3574
+ providerBaseUrl,
3575
+ setModelLoadError
3576
+ }) {
3577
+ try {
3578
+ const baseURL = providerBaseUrl || "https://api.moonshot.cn/v1";
3579
+ const models = await fetchCustomModels(baseURL, apiKey);
3580
+ return models.map((model) => ({
3581
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3582
+ provider: "kimi",
3583
+ max_tokens: model.max_tokens || 8192,
3584
+ supports_vision: false,
3585
+ // Default to false, could be enhanced
3586
+ supports_function_calling: true,
3587
+ supports_reasoning_effort: false
3588
+ }));
3589
+ } catch (error) {
3590
+ let errorMessage = "Failed to fetch Kimi models";
3591
+ if (error instanceof Error) {
3592
+ errorMessage = error.message;
3593
+ }
3594
+ if (errorMessage.includes("API key")) {
3595
+ errorMessage += "\n\n\u{1F4A1} Tip: Get your API key from https://platform.moonshot.cn/console/api-keys";
3596
+ } else if (errorMessage.includes("permission")) {
3597
+ errorMessage += "\n\n\u{1F4A1} Tip: Make sure your API key has access to the Kimi API";
3598
+ } else if (errorMessage.includes("connection")) {
3599
+ errorMessage += "\n\n\u{1F4A1} Tip: Check your internet connection and try again";
3600
+ }
3601
+ setModelLoadError(errorMessage);
3602
+ throw error;
3603
+ }
3604
+ }
3605
+
3606
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/minimax.ts
3607
+ async function fetchMinimaxModels({
3608
+ apiKey,
3609
+ providerBaseUrl,
3610
+ setModelLoadError
3611
+ }) {
3612
+ try {
3613
+ const baseURL = providerBaseUrl || "https://api.minimaxi.com/v1";
3614
+ const models = await fetchCustomModels(baseURL, apiKey);
3615
+ return models.map((model) => ({
3616
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3617
+ provider: "minimax",
3618
+ max_tokens: model.max_tokens || 8192,
3619
+ supports_vision: false,
3620
+ supports_function_calling: true,
3621
+ supports_reasoning_effort: false
3622
+ }));
3623
+ } catch (error) {
3624
+ let errorMessage = "Failed to fetch MiniMax models";
3625
+ if (error instanceof Error) {
3626
+ errorMessage = error.message;
3627
+ }
3628
+ if (errorMessage.includes("API key")) {
3629
+ errorMessage += "\n\n\u{1F4A1} Tip: Get your API key from https://www.minimax.io/platform/user-center/basic-information";
3630
+ } else if (errorMessage.includes("permission")) {
3631
+ errorMessage += "\n\n\u{1F4A1} Tip: Make sure your API key has access to the MiniMax API";
3632
+ } else if (errorMessage.includes("connection")) {
3633
+ errorMessage += "\n\n\u{1F4A1} Tip: Check your internet connection and try again";
3634
+ }
3635
+ setModelLoadError(errorMessage);
3636
+ throw error;
3637
+ }
3638
+ }
3639
+
3640
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/qwen.ts
3641
+ async function fetchQwenModels({
3642
+ apiKey,
3643
+ providerBaseUrl,
3644
+ setModelLoadError
3645
+ }) {
3646
+ try {
3647
+ const baseURL = providerBaseUrl || "https://dashscope.aliyuncs.com/compatible-mode/v1";
3648
+ const models = await fetchCustomModels(baseURL, apiKey);
3649
+ return models.map((model) => ({
3650
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3651
+ provider: "qwen",
3652
+ max_tokens: model.max_tokens || 8192,
3653
+ supports_vision: false,
3654
+ supports_function_calling: true,
3655
+ supports_reasoning_effort: false
3656
+ }));
3657
+ } catch (error) {
3658
+ let errorMessage = "Failed to fetch Qwen models";
3659
+ if (error instanceof Error) {
3660
+ errorMessage = error.message;
3661
+ }
3662
+ if (errorMessage.includes("API key")) {
3663
+ errorMessage += "\n\n\u{1F4A1} Tip: Get your API key from https://bailian.console.aliyun.com/?tab=model#/api-key";
3664
+ } else if (errorMessage.includes("permission")) {
3665
+ errorMessage += "\n\n\u{1F4A1} Tip: Make sure your API key has access to the Qwen API";
3666
+ } else if (errorMessage.includes("connection")) {
3667
+ errorMessage += "\n\n\u{1F4A1} Tip: Check your internet connection and try again";
3668
+ }
3669
+ setModelLoadError(errorMessage);
3670
+ throw error;
3671
+ }
3672
+ }
3673
+
3674
+ // apps/cli/src/ui/ui/components/model-selector/modelFetchers/siliconflow.ts
3675
+ async function fetchSiliconFlowModels({
3676
+ apiKey,
3677
+ providerBaseUrl,
3678
+ setModelLoadError
3679
+ }) {
3680
+ try {
3681
+ const baseURL = providerBaseUrl || "https://api.siliconflow.cn/v1";
3682
+ const models = await fetchCustomModels(baseURL, apiKey);
3683
+ return models.map((model) => ({
3684
+ model: model.modelName || model.id || model.name || model.model || "unknown",
3685
+ provider: "siliconflow",
3686
+ max_tokens: model.max_tokens || 8192,
3687
+ supports_vision: false,
3688
+ // Default to false, could be enhanced
3689
+ supports_function_calling: true,
3690
+ supports_reasoning_effort: false
3691
+ }));
3692
+ } catch (error) {
3693
+ let errorMessage = "Failed to fetch SiliconFlow models";
3694
+ if (error instanceof Error) {
3695
+ errorMessage = error.message;
3696
+ }
3697
+ if (errorMessage.includes("API key")) {
3698
+ errorMessage += "\n\n\u{1F4A1} Tip: Get your API key from https://cloud.siliconflow.cn/i/oJWsm6io";
3699
+ } else if (errorMessage.includes("permission")) {
3700
+ errorMessage += "\n\n\u{1F4A1} Tip: Make sure your API key has access to the SiliconFlow API";
3701
+ } else if (errorMessage.includes("connection")) {
3702
+ errorMessage += "\n\n\u{1F4A1} Tip: Check your internet connection and try again";
3703
+ }
3704
+ setModelLoadError(errorMessage);
3705
+ throw error;
3706
+ }
3707
+ }
3708
+
3709
+ // apps/cli/src/ui/components/ModelSelector/fetchOllamaModels.ts
3710
+ function isRecord(value) {
3711
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3712
+ }
3713
+ function getNestedValue(value, path) {
3714
+ let current = value;
3715
+ for (const key of path) {
3716
+ if (!isRecord(current)) return void 0;
3717
+ current = current[key];
3718
+ }
3719
+ return current;
3720
+ }
3721
+ function toPositiveFiniteNumber(value) {
3722
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
3723
+ }
3724
+ async function fetchOllamaModels(args) {
3725
+ try {
3726
+ const response = await fetch(`${args.ollamaBaseUrl}/models`);
3727
+ if (!response.ok) {
3728
+ throw new Error(`HTTP error ${response.status}: ${response.statusText}`);
3729
+ }
3730
+ const responseData = await response.json();
3731
+ let models = [];
3732
+ if (isRecord(responseData) && Array.isArray(responseData.data)) {
3733
+ models = responseData.data;
3734
+ } else if (isRecord(responseData) && Array.isArray(responseData.models)) {
3735
+ models = responseData.models;
3736
+ } else if (Array.isArray(responseData)) {
3737
+ models = responseData;
3738
+ } else {
3739
+ throw new Error("Invalid response from Ollama API: missing models array");
3740
+ }
3741
+ const getModelName = (model) => {
3742
+ if (typeof model === "string") return model;
3743
+ if (!isRecord(model)) return "";
3744
+ const candidates = [
3745
+ model.id,
3746
+ model.name,
3747
+ model.modelName,
3748
+ model.model,
3749
+ model.model_name
3750
+ ];
3751
+ for (const c of candidates) {
3752
+ if (typeof c === "string") return c;
3753
+ }
3754
+ return "";
3755
+ };
3756
+ const ollamaModels = models.map((model) => ({
3757
+ model: getModelName(model),
3758
+ provider: "ollama",
3759
+ max_tokens: DEFAULT_MAX_TOKENS,
3760
+ supports_vision: false,
3761
+ supports_function_calling: true,
3762
+ supports_reasoning_effort: false
3763
+ }));
3764
+ const validModels = ollamaModels.filter((model) => model.model);
3765
+ const normalizeOllamaRoot = (url) => {
3766
+ try {
3767
+ const u = new URL(url);
3768
+ let pathname = u.pathname.replace(/\/+$/g, "").replace(/^$/, "");
3769
+ if (pathname.endsWith("/v1")) {
3770
+ pathname = pathname.slice(0, -3);
3771
+ }
3772
+ u.pathname = pathname;
3773
+ return u.toString().replace(/\/+$/g, "");
3774
+ } catch {
3775
+ return url.replace(/\/v1\/?$/g, "");
3776
+ }
3777
+ };
3778
+ const extractContextTokens = (data) => {
3779
+ if (!isRecord(data)) return null;
3780
+ const modelInfoValue = data.model_info;
3781
+ if (isRecord(modelInfoValue)) {
3782
+ for (const key of Object.keys(modelInfoValue)) {
3783
+ if (key.endsWith(".context_length") || key.endsWith("_context_length")) {
3784
+ const val = toPositiveFiniteNumber(modelInfoValue[key]);
3785
+ if (val) return val;
3786
+ }
3787
+ }
3788
+ }
3789
+ const paths = [
3790
+ ["parameters", "num_ctx"],
3791
+ ["model_info", "num_ctx"],
3792
+ ["config", "num_ctx"],
3793
+ ["details", "context_length"],
3794
+ ["context_length"],
3795
+ ["num_ctx"],
3796
+ ["max_tokens"],
3797
+ ["max_new_tokens"]
3798
+ ];
3799
+ const candidates = paths.map((path) => toPositiveFiniteNumber(getNestedValue(data, path))).filter((v) => typeof v === "number");
3800
+ if (candidates.length > 0) return Math.max(...candidates);
3801
+ const parametersValue = getNestedValue(data, ["parameters"]);
3802
+ if (typeof parametersValue === "string") {
3803
+ const m = parametersValue.match(/num_ctx\s*[:=]\s*(\d+)/i);
3804
+ if (!m) return null;
3805
+ const n = parseInt(m[1], 10);
3806
+ return Number.isFinite(n) && n > 0 ? n : null;
3807
+ }
3808
+ return null;
3809
+ };
3810
+ const ollamaRoot = normalizeOllamaRoot(args.ollamaBaseUrl);
3811
+ const enrichedModels = await Promise.all(
3812
+ validModels.map(async (m) => {
3813
+ try {
3814
+ const showResp = await fetch(`${ollamaRoot}/api/show`, {
3815
+ method: "POST",
3816
+ headers: { "Content-Type": "application/json" },
3817
+ body: JSON.stringify({ name: m.model })
3818
+ });
3819
+ if (showResp.ok) {
3820
+ const showData = await showResp.json();
3821
+ const ctx = extractContextTokens(showData);
3822
+ if (typeof ctx === "number" && isFinite(ctx) && ctx > 0) {
3823
+ return { ...m, context_length: ctx };
3824
+ }
3825
+ }
3826
+ return m;
3827
+ } catch {
3828
+ return m;
3829
+ }
3830
+ })
3831
+ );
3832
+ args.setAvailableModels(enrichedModels);
3833
+ if (enrichedModels.length > 0) {
3834
+ args.navigateTo("model");
3835
+ } else {
3836
+ args.setModelLoadError("No models found in your Ollama installation");
3837
+ }
3838
+ return enrichedModels;
3839
+ } catch (error) {
3840
+ const errorMessage = error instanceof Error ? error.message : String(error);
3841
+ if (errorMessage.includes("fetch")) {
3842
+ args.setModelLoadError(
3843
+ `Could not connect to Ollama server at ${args.ollamaBaseUrl}. Make sure Ollama is running and the URL is correct.`
3844
+ );
3845
+ } else {
3846
+ args.setModelLoadError(`Error loading Ollama models: ${errorMessage}`);
3847
+ }
3848
+ console.error("Error fetching Ollama models:", error);
3849
+ return [];
3850
+ }
3851
+ }
3852
+
3853
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorModelFlow.tsx
3854
+ function useModelSelectorModelFlow(state) {
3855
+ async function fetchModels() {
3856
+ return await fetchModelsForProvider({
3857
+ selectedProvider: state.selectedProvider,
3858
+ apiKey: state.apiKey,
3859
+ providerBaseUrl: state.providerBaseUrl,
3860
+ customBaseUrl: state.customBaseUrl,
3861
+ modelFetchers: modelFetchers_exports,
3862
+ setIsLoadingModels: state.setIsLoadingModels,
3863
+ setModelLoadError: state.setModelLoadError,
3864
+ setAvailableModels: state.setAvailableModels,
3865
+ navigateTo: state.navigateTo
3866
+ });
3867
+ }
3868
+ async function fetchModelsWithRetry() {
3869
+ const MAX_RETRIES = 2;
3870
+ let lastError = null;
3871
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
3872
+ state.setFetchRetryCount(attempt);
3873
+ state.setIsRetrying(attempt > 1);
3874
+ if (attempt > 1) {
3875
+ state.setModelLoadError(
3876
+ `Attempt ${attempt}/${MAX_RETRIES}: Retrying model discovery...`
3877
+ );
3878
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3879
+ }
3880
+ try {
3881
+ const models = await fetchModels();
3882
+ state.setFetchRetryCount(0);
3883
+ state.setIsRetrying(false);
3884
+ state.setModelLoadError(null);
3885
+ return models;
3886
+ } catch (error) {
3887
+ lastError = error instanceof Error ? error : new Error(String(error));
3888
+ debug.warn("MODEL_FETCH_RETRY_FAILED", {
3889
+ attempt,
3890
+ maxRetries: MAX_RETRIES,
3891
+ error: lastError.message,
3892
+ provider: state.selectedProvider
3893
+ });
3894
+ if (attempt === MAX_RETRIES) break;
3895
+ }
3896
+ }
3897
+ state.setIsRetrying(false);
3898
+ const errorMessage = lastError?.message || "Unknown error";
3899
+ state.setModelLoadError(
3900
+ `Failed to validate API key after ${MAX_RETRIES} attempts: ${errorMessage}
3901
+
3902
+ Please check your API key and try again, or press Tab to manually enter model name.`
3903
+ );
3904
+ throw new Error(`API key validation failed: ${errorMessage}`);
3905
+ }
3906
+ async function handleApiKeySubmit(key) {
3907
+ const cleanedKey = key.replace(/[\r\n]/g, "").trim();
3908
+ state.setApiKey(cleanedKey);
3909
+ state.setModelLoadError(null);
3910
+ if (state.selectedProvider === "azure") {
3911
+ state.navigateTo("resourceName");
3912
+ return;
3913
+ }
3914
+ try {
3915
+ state.setIsLoadingModels(true);
3916
+ const models = await fetchModelsWithRetry();
3917
+ if (models.length === 0) {
3918
+ state.navigateTo("modelInput");
3919
+ }
3920
+ } catch (error) {
3921
+ console.error("API key validation failed:", error);
3922
+ } finally {
3923
+ state.setIsLoadingModels(false);
3924
+ }
3925
+ }
3926
+ function handleResourceNameSubmit(name) {
3927
+ state.setResourceName(name);
3928
+ state.navigateTo("modelInput");
3929
+ }
3930
+ function handleCustomBaseUrlSubmit(url) {
3931
+ const cleanUrl = url.replace(/\/+$/, "");
3932
+ state.setCustomBaseUrl(cleanUrl);
3933
+ state.navigateTo("apiKey");
3934
+ }
3935
+ function handleProviderBaseUrlSubmit(url) {
3936
+ const cleanUrl = url.replace(/\/+$/, "");
3937
+ state.setProviderBaseUrl(cleanUrl);
3938
+ if (state.selectedProvider === "ollama") {
3939
+ state.setOllamaBaseUrl(cleanUrl);
3940
+ state.setIsLoadingModels(true);
3941
+ state.setModelLoadError(null);
3942
+ fetchOllamaModels({
3943
+ ollamaBaseUrl: cleanUrl,
3944
+ setAvailableModels: state.setAvailableModels,
3945
+ setModelLoadError: state.setModelLoadError,
3946
+ navigateTo: () => state.navigateTo("model")
3947
+ }).finally(() => {
3948
+ state.setIsLoadingModels(false);
3949
+ });
3950
+ } else {
3951
+ state.navigateTo("apiKey");
3952
+ }
3953
+ }
3954
+ function handleCustomModelSubmit(model) {
3955
+ state.setCustomModelName(model);
3956
+ state.setSelectedModel(model);
3957
+ state.setSupportsReasoningEffort(false);
3958
+ state.setReasoningEffort(null);
3959
+ state.setMaxTokensMode("preset");
3960
+ state.setSelectedMaxTokensPreset(DEFAULT_MAX_TOKENS);
3961
+ state.setMaxTokens(DEFAULT_MAX_TOKENS.toString());
3962
+ state.setMaxTokensCursorOffset(DEFAULT_MAX_TOKENS.toString().length);
3963
+ state.navigateTo("modelParams");
3964
+ state.setActiveFieldIndex(0);
3965
+ }
3966
+ function handleModelSelection(model) {
3967
+ state.setSelectedModel(model);
3968
+ const modelInfo = state.availableModels.find((m) => m.model === model);
3969
+ state.setSupportsReasoningEffort(
3970
+ Boolean(modelInfo?.supports_reasoning_effort)
3971
+ );
3972
+ if (!modelInfo?.supports_reasoning_effort) {
3973
+ state.setReasoningEffort(null);
3974
+ }
3975
+ state.setContextLength(modelInfo?.context_length ?? DEFAULT_CONTEXT_LENGTH);
3976
+ const modelMaxTokens = modelInfo?.max_tokens;
3977
+ if (typeof modelMaxTokens === "number" && Number.isFinite(modelMaxTokens)) {
3978
+ const matchingPreset = MAX_TOKENS_OPTIONS.find(
3979
+ (option) => option.value === modelMaxTokens
3980
+ );
3981
+ if (matchingPreset) {
3982
+ state.setMaxTokensMode("preset");
3983
+ state.setSelectedMaxTokensPreset(modelMaxTokens);
3984
+ state.setMaxTokens(modelMaxTokens.toString());
3985
+ } else {
3986
+ state.setMaxTokensMode("custom");
3987
+ state.setMaxTokens(modelMaxTokens.toString());
3988
+ }
3989
+ state.setMaxTokensCursorOffset(modelMaxTokens.toString().length);
3990
+ } else {
3991
+ state.setMaxTokensMode("preset");
3992
+ state.setSelectedMaxTokensPreset(DEFAULT_MAX_TOKENS);
3993
+ state.setMaxTokens(DEFAULT_MAX_TOKENS.toString());
3994
+ state.setMaxTokensCursorOffset(DEFAULT_MAX_TOKENS.toString().length);
3995
+ }
3996
+ state.navigateTo("modelParams");
3997
+ state.setActiveFieldIndex(0);
3998
+ }
3999
+ const handleModelParamsSubmit = () => {
4000
+ if (!CONTEXT_LENGTH_OPTIONS.find((opt) => opt.value === state.contextLength)) {
4001
+ state.setContextLength(DEFAULT_CONTEXT_LENGTH);
4002
+ }
4003
+ state.navigateTo("contextLength");
4004
+ };
4005
+ const getFormFieldsForModelParams = () => {
4006
+ const fields = [
4007
+ {
4008
+ name: "maxTokens",
4009
+ label: "Maximum Tokens",
4010
+ description: "Select the maximum number of tokens to generate.",
4011
+ component: "select",
4012
+ options: MAX_TOKENS_OPTIONS.map((option) => ({
4013
+ label: option.label,
4014
+ value: option.value.toString()
4015
+ })),
4016
+ defaultValue: state.maxTokens
4017
+ }
4018
+ ];
4019
+ if (state.supportsReasoningEffort) {
4020
+ fields.push({
4021
+ name: "reasoningEffort",
4022
+ label: "Reasoning Effort",
4023
+ description: "Controls reasoning depth for complex problems.",
4024
+ component: "select"
4025
+ });
4026
+ }
4027
+ fields.push({ name: "submit", label: "Continue \u2192", component: "button" });
4028
+ return fields;
4029
+ };
4030
+ const reasoningEffortOptions = REASONING_EFFORT_OPTIONS;
4031
+ const handleContextLengthSubmit = () => state.navigateTo("connectionTest");
4032
+ return {
4033
+ fetchModelsWithRetry,
4034
+ handleApiKeySubmit,
4035
+ handleResourceNameSubmit,
4036
+ handleCustomBaseUrlSubmit,
4037
+ handleProviderBaseUrlSubmit,
4038
+ handleCustomModelSubmit,
4039
+ handleModelSelection,
4040
+ handleModelParamsSubmit,
4041
+ getFormFieldsForModelParams,
4042
+ reasoningEffortOptions,
4043
+ handleContextLengthSubmit
4044
+ };
4045
+ }
4046
+
4047
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorTextHandlers.ts
4048
+ function useModelSelectorTextHandlers(state) {
4049
+ function handleCursorOffsetChange(offset) {
4050
+ state.setCursorOffset(offset);
4051
+ }
4052
+ function formatApiKeyDisplay(key) {
4053
+ if (!key) return "";
4054
+ if (key.length <= 10) return "*".repeat(key.length);
4055
+ const prefix = key.slice(0, 4);
4056
+ const suffix = key.slice(-4);
4057
+ const middleLength = Math.max(0, key.length - 8);
4058
+ const middle = "*".repeat(Math.min(middleLength, 30));
4059
+ return `${prefix}${middle}${suffix}`;
4060
+ }
4061
+ function handleApiKeyChange(value) {
4062
+ state.setApiKeyEdited(true);
4063
+ const cleanedValue = value.replace(/[\\r\\n]/g, "").trim();
4064
+ if (value !== cleanedValue && value.length > 0) {
4065
+ state.setApiKeyCleanedNotification(true);
4066
+ setTimeout(() => state.setApiKeyCleanedNotification(false), 3e3);
4067
+ }
4068
+ state.setApiKey(cleanedValue);
4069
+ state.setCursorOffset(cleanedValue.length);
4070
+ }
4071
+ function handleModelSearchChange(value) {
4072
+ state.setModelSearchQuery(value);
4073
+ state.setModelSearchCursorOffset(value.length);
4074
+ }
4075
+ function handleModelSearchCursorOffsetChange(offset) {
4076
+ state.setModelSearchCursorOffset(offset);
4077
+ }
4078
+ return {
4079
+ handleCursorOffsetChange,
4080
+ formatApiKeyDisplay,
4081
+ handleApiKeyChange,
4082
+ handleModelSearchChange,
4083
+ handleModelSearchCursorOffsetChange
4084
+ };
4085
+ }
4086
+
4087
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorActions.tsx
4088
+ function useModelSelectorActions({ props, state, onDone }) {
4089
+ const modelFlow = useModelSelectorModelFlow(state);
4090
+ const textHandlers = useModelSelectorTextHandlers(state);
4091
+ async function saveConfiguration(provider, model) {
4092
+ try {
4093
+ return await saveModelConfiguration({
4094
+ provider,
4095
+ model,
4096
+ providerBaseUrl: state.providerBaseUrl,
4097
+ resourceName: state.resourceName,
4098
+ customBaseUrl: state.customBaseUrl,
4099
+ apiKey: state.apiKey,
4100
+ maxTokens: state.maxTokens,
4101
+ contextLength: state.contextLength,
4102
+ reasoningEffort: state.reasoningEffort ?? void 0
4103
+ });
4104
+ } catch (error) {
4105
+ state.setValidationError(
4106
+ error instanceof Error ? error.message : "Failed to add model"
4107
+ );
4108
+ return null;
4109
+ }
4110
+ }
4111
+ async function handleConfirmation() {
4112
+ state.setValidationError(null);
4113
+ const modelId = await saveConfiguration(
4114
+ state.selectedProvider,
4115
+ state.selectedModel
4116
+ );
4117
+ if (!modelId) return;
4118
+ applyPointersForNewModel({
4119
+ modelId,
4120
+ isOnboarding: Boolean(props.isOnboarding),
4121
+ targetPointer: props.targetPointer
4122
+ });
4123
+ onDone();
4124
+ }
4125
+ const handleBack = () => {
4126
+ const { stack: nextStack, effect } = handleBackNavigation(state.screenStack);
4127
+ if (effect?.type === "resetProviderFocus") {
4128
+ state.setProviderFocusIndex(0);
4129
+ }
4130
+ if (effect?.type === "exit") {
4131
+ if (props.onCancel) props.onCancel();
4132
+ else onDone();
4133
+ return;
4134
+ }
4135
+ if (nextStack !== state.screenStack) {
4136
+ state.setScreenStack(nextStack);
4137
+ }
4138
+ };
4139
+ function handleProviderSelection2(provider) {
4140
+ handleProviderSelection(provider, {
4141
+ navigateTo: state.navigateTo,
4142
+ setPartnerProviderFocusIndex: state.setPartnerProviderFocusIndex,
4143
+ setCodingPlanFocusIndex: state.setCodingPlanFocusIndex,
4144
+ setSelectedProvider: state.setSelectedProvider,
4145
+ setProviderBaseUrl: state.setProviderBaseUrl,
4146
+ saveConfiguration,
4147
+ onDone,
4148
+ selectedModel: state.selectedModel
4149
+ });
4150
+ }
4151
+ async function handleConnectionTest() {
4152
+ state.setIsTestingConnection(true);
4153
+ state.setConnectionTestResult(null);
4154
+ try {
4155
+ const result = await runConnectionTestFlow({
4156
+ params: {
4157
+ selectedProvider: state.selectedProvider,
4158
+ selectedModel: state.selectedModel,
4159
+ apiKey: state.apiKey,
4160
+ maxTokens: state.maxTokens,
4161
+ providerBaseUrl: state.providerBaseUrl,
4162
+ customBaseUrl: state.customBaseUrl,
4163
+ resourceName: state.resourceName
4164
+ },
4165
+ navigateTo: () => state.navigateTo("confirmation")
4166
+ });
4167
+ state.setConnectionTestResult(result);
4168
+ } finally {
4169
+ state.setIsTestingConnection(false);
4170
+ }
4171
+ }
4172
+ return {
4173
+ handleBack,
4174
+ handleProviderSelection: handleProviderSelection2,
4175
+ ...modelFlow,
4176
+ handleConnectionTest,
4177
+ handleConfirmation,
4178
+ ...textHandlers
4179
+ };
4180
+ }
4181
+
4182
+ // apps/cli/src/ui/ui/components/model-selector/useEscapeNavigation.ts
4183
+ import { useRef as useRef2 } from "react";
4184
+ import { useInput as useInput4 } from "ink";
4185
+ function useEscapeNavigation(onEscape, _abortController) {
4186
+ const handledRef = useRef2(false);
4187
+ useInput4(
4188
+ (_input, key) => {
4189
+ if (key.escape && !handledRef.current) {
4190
+ handledRef.current = true;
4191
+ setTimeout(() => {
4192
+ handledRef.current = false;
4193
+ }, 100);
4194
+ onEscape();
4195
+ }
4196
+ },
4197
+ { isActive: true }
4198
+ );
4199
+ }
4200
+
4201
+ // apps/cli/src/ui/components/ModelSelector/useModelSelectorController.tsx
4202
+ function useModelSelectorController(props) {
4203
+ const theme = getTheme();
4204
+ const { stdout } = useStdout();
4205
+ const terminalRows = stdout?.rows ?? 24;
4206
+ const compactLayout = terminalRows <= 22;
4207
+ const tightLayout = terminalRows <= 18;
4208
+ const containerPaddingY = tightLayout ? 0 : compactLayout ? 0 : 1;
4209
+ const containerGap = tightLayout ? 0 : 1;
4210
+ const exitState = useExitOnCtrlCD(() => process.exit(0));
4211
+ const exitStateForScreens = useMemo4(
4212
+ () => ({ pending: exitState.pending, keyName: exitState.keyName ?? "" }),
4213
+ [exitState.pending, exitState.keyName]
4214
+ );
4215
+ const onDone = () => {
4216
+ printModelConfig();
4217
+ props.onDone();
4218
+ };
4219
+ const state = useModelSelectorState({
4220
+ skipModelType: props.skipModelType ?? false
4221
+ });
4222
+ const menus = useModelSelectorMenus({
4223
+ containerPaddingY,
4224
+ containerGap,
4225
+ setProviderFocusIndex: state.setProviderFocusIndex,
4226
+ setPartnerProviderFocusIndex: state.setPartnerProviderFocusIndex,
4227
+ setCodingPlanFocusIndex: state.setCodingPlanFocusIndex
4228
+ });
4229
+ const { modelOptions } = useModelSelectorModelOptions({
4230
+ selectedProvider: state.selectedProvider,
4231
+ availableModels: state.availableModels,
4232
+ modelSearchQuery: state.modelSearchQuery
4233
+ });
4234
+ useEffect2(() => {
4235
+ if (!state.apiKeyEdited && state.selectedProvider) {
4236
+ const envKey = `${state.selectedProvider.toUpperCase()}_API_KEY`;
4237
+ if (process.env[envKey]) {
4238
+ state.setApiKey(process.env[envKey]);
4239
+ } else {
4240
+ state.setApiKey("");
4241
+ }
4242
+ }
4243
+ }, [state.apiKeyEdited, state.selectedProvider, state.setApiKey]);
4244
+ useEffect2(() => {
4245
+ if (state.currentScreen === "contextLength" && !CONTEXT_LENGTH_OPTIONS.find((opt) => opt.value === state.contextLength)) {
4246
+ state.setContextLength(DEFAULT_CONTEXT_LENGTH);
4247
+ }
4248
+ }, [state.contextLength, state.currentScreen, state.setContextLength]);
4249
+ const actions = useModelSelectorActions({ props, state, onDone });
4250
+ useEscapeNavigation(actions.handleBack, props.abortController);
4251
+ useModelSelectorInput({
4252
+ currentScreen: state.currentScreen,
4253
+ mainMenuOptions: menus.mainMenuOptions,
4254
+ providerFocusIndex: state.providerFocusIndex,
4255
+ setProviderFocusIndex: state.setProviderFocusIndex,
4256
+ partnerProviderOptions: menus.partnerProviderOptions,
4257
+ partnerProviderFocusIndex: state.partnerProviderFocusIndex,
4258
+ setPartnerProviderFocusIndex: state.setPartnerProviderFocusIndex,
4259
+ codingPlanOptions: menus.codingPlanOptions,
4260
+ codingPlanFocusIndex: state.codingPlanFocusIndex,
4261
+ setCodingPlanFocusIndex: state.setCodingPlanFocusIndex,
4262
+ selectedProvider: state.selectedProvider,
4263
+ apiKey: state.apiKey,
4264
+ resourceName: state.resourceName,
4265
+ providerBaseUrl: state.providerBaseUrl,
4266
+ customBaseUrl: state.customBaseUrl,
4267
+ customModelName: state.customModelName,
4268
+ contextLength: state.contextLength,
4269
+ setContextLength: state.setContextLength,
4270
+ isTestingConnection: state.isTestingConnection,
4271
+ connectionTestResult: state.connectionTestResult,
4272
+ activeFieldIndex: state.activeFieldIndex,
4273
+ setActiveFieldIndex: state.setActiveFieldIndex,
4274
+ handleProviderSelection: actions.handleProviderSelection,
4275
+ handleApiKeySubmit: actions.handleApiKeySubmit,
4276
+ fetchModelsWithRetry: actions.fetchModelsWithRetry,
4277
+ navigateTo: state.navigateTo,
4278
+ handleResourceNameSubmit: actions.handleResourceNameSubmit,
4279
+ handleCustomBaseUrlSubmit: actions.handleCustomBaseUrlSubmit,
4280
+ handleProviderBaseUrlSubmit: actions.handleProviderBaseUrlSubmit,
4281
+ handleCustomModelSubmit: actions.handleCustomModelSubmit,
4282
+ handleConfirmation: actions.handleConfirmation,
4283
+ setValidationError: state.setValidationError,
4284
+ handleConnectionTest: actions.handleConnectionTest,
4285
+ handleContextLengthSubmit: actions.handleContextLengthSubmit,
4286
+ setModelLoadError: state.setModelLoadError,
4287
+ getFormFieldsForModelParams: actions.getFormFieldsForModelParams,
4288
+ handleModelParamsSubmit: actions.handleModelParamsSubmit
4289
+ });
4290
+ return {
4291
+ theme,
4292
+ exitState: exitStateForScreens,
4293
+ terminalRows,
4294
+ compactLayout,
4295
+ tightLayout,
4296
+ containerPaddingY,
4297
+ containerGap,
4298
+ currentScreen: state.currentScreen,
4299
+ selectedProvider: state.selectedProvider,
4300
+ selectedModel: state.selectedModel,
4301
+ apiKey: state.apiKey,
4302
+ cursorOffset: state.cursorOffset,
4303
+ handleApiKeyChange: actions.handleApiKeyChange,
4304
+ handleApiKeySubmit: actions.handleApiKeySubmit,
4305
+ handleCursorOffsetChange: actions.handleCursorOffsetChange,
4306
+ apiKeyCleanedNotification: state.apiKeyCleanedNotification,
4307
+ isLoadingModels: state.isLoadingModels,
4308
+ modelLoadError: state.modelLoadError,
4309
+ providerBaseUrl: state.providerBaseUrl,
4310
+ setProviderBaseUrl: state.setProviderBaseUrl,
4311
+ providerBaseUrlCursorOffset: state.providerBaseUrlCursorOffset,
4312
+ setProviderBaseUrlCursorOffset: state.setProviderBaseUrlCursorOffset,
4313
+ customBaseUrl: state.customBaseUrl,
4314
+ setCustomBaseUrl: state.setCustomBaseUrl,
4315
+ customBaseUrlCursorOffset: state.customBaseUrlCursorOffset,
4316
+ setCustomBaseUrlCursorOffset: state.setCustomBaseUrlCursorOffset,
4317
+ customModelName: state.customModelName,
4318
+ setCustomModelName: state.setCustomModelName,
4319
+ customModelNameCursorOffset: state.customModelNameCursorOffset,
4320
+ setCustomModelNameCursorOffset: state.setCustomModelNameCursorOffset,
4321
+ resourceName: state.resourceName,
4322
+ setResourceName: state.setResourceName,
4323
+ resourceNameCursorOffset: state.resourceNameCursorOffset,
4324
+ setResourceNameCursorOffset: state.setResourceNameCursorOffset,
4325
+ availableModels: state.availableModels,
4326
+ modelSearchQuery: state.modelSearchQuery,
4327
+ modelSearchCursorOffset: state.modelSearchCursorOffset,
4328
+ handleModelSearchChange: actions.handleModelSearchChange,
4329
+ handleModelSearchCursorOffsetChange: actions.handleModelSearchCursorOffsetChange,
4330
+ modelOptions,
4331
+ handleResourceNameSubmit: actions.handleResourceNameSubmit,
4332
+ handleCustomBaseUrlSubmit: actions.handleCustomBaseUrlSubmit,
4333
+ handleProviderBaseUrlSubmit: actions.handleProviderBaseUrlSubmit,
4334
+ handleCustomModelSubmit: actions.handleCustomModelSubmit,
4335
+ handleModelSelection: actions.handleModelSelection,
4336
+ handleModelParamsSubmit: actions.handleModelParamsSubmit,
4337
+ maxTokens: state.maxTokens,
4338
+ setMaxTokens: state.setMaxTokens,
4339
+ setSelectedMaxTokensPreset: state.setSelectedMaxTokensPreset,
4340
+ setMaxTokensCursorOffset: state.setMaxTokensCursorOffset,
4341
+ supportsReasoningEffort: state.supportsReasoningEffort,
4342
+ reasoningEffortOptions: actions.reasoningEffortOptions,
4343
+ reasoningEffort: state.reasoningEffort,
4344
+ setReasoningEffort: state.setReasoningEffort,
4345
+ contextLength: state.contextLength,
4346
+ isTestingConnection: state.isTestingConnection,
4347
+ connectionTestResult: state.connectionTestResult,
4348
+ validationError: state.validationError,
4349
+ ollamaBaseUrl: state.ollamaBaseUrl,
4350
+ activeFieldIndex: state.activeFieldIndex,
4351
+ setActiveFieldIndex: state.setActiveFieldIndex,
4352
+ getFormFieldsForModelParams: actions.getFormFieldsForModelParams,
4353
+ mainMenuOptions: menus.mainMenuOptions,
4354
+ providerFocusIndex: state.providerFocusIndex,
4355
+ providerReservedLines: menus.providerReservedLines,
4356
+ partnerProviderOptions: menus.partnerProviderOptions,
4357
+ partnerProviderFocusIndex: state.partnerProviderFocusIndex,
4358
+ partnerReservedLines: menus.partnerReservedLines,
4359
+ codingPlanOptions: menus.codingPlanOptions,
4360
+ codingPlanFocusIndex: state.codingPlanFocusIndex,
4361
+ codingReservedLines: menus.codingReservedLines,
4362
+ formatApiKeyDisplay: actions.formatApiKeyDisplay,
4363
+ getProviderLabel: menus.getProviderLabel
4364
+ };
4365
+ }
4366
+
4367
+ // apps/cli/src/ui/components/ModelSelector/ModelSelector.tsx
4368
+ function ModelSelector(props) {
4369
+ const viewProps = useModelSelectorController(props);
4370
+ return /* @__PURE__ */ React19.createElement(ModelSelectorView, { ...viewProps });
4371
+ }
4372
+
4373
+ // apps/cli/src/ui/components/Onboarding.tsx
4374
+ function Onboarding({ onDone }) {
4375
+ const [currentStepIndex, setCurrentStepIndex] = useState4(0);
4376
+ const [showModelSelector, setShowModelSelector] = useState4(false);
4377
+ const config = getGlobalConfig();
4378
+ const [selectedTheme, setSelectedTheme] = useState4(
4379
+ DEFAULT_GLOBAL_CONFIG.theme
4380
+ );
4381
+ const theme = getTheme();
4382
+ function goToNextStep() {
4383
+ if (currentStepIndex < steps.length - 1) {
4384
+ const nextIndex = currentStepIndex + 1;
4385
+ setCurrentStepIndex(nextIndex);
4386
+ }
4387
+ }
4388
+ function handleThemeSelection(newTheme) {
4389
+ saveGlobalConfig({
4390
+ ...config,
4391
+ theme: newTheme
4392
+ });
4393
+ goToNextStep();
4394
+ }
4395
+ function handleThemePreview(newTheme) {
4396
+ setSelectedTheme(newTheme);
4397
+ }
4398
+ function handleProviderSelectionDone() {
4399
+ goToNextStep();
4400
+ }
4401
+ function handleModelSelectionDone() {
4402
+ onDone();
4403
+ }
4404
+ const exitState = useExitOnCtrlCD(() => process.exit(0));
4405
+ useInput5(
4406
+ async (_, key) => {
4407
+ const currentStep = steps[currentStepIndex];
4408
+ if (key.return && currentStep && ["usage", "providers", "model"].includes(currentStep.id)) {
4409
+ if (currentStep.id === "model") {
4410
+ setShowModelSelector(true);
4411
+ } else if (currentStepIndex === steps.length - 1) {
4412
+ onDone();
4413
+ } else {
4414
+ await clearTerminal();
4415
+ goToNextStep();
4416
+ }
4417
+ }
4418
+ },
4419
+ { isActive: !showModelSelector }
4420
+ );
4421
+ const themeStep = /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", gap: 1, paddingLeft: 1 }, /* @__PURE__ */ React20.createElement(Text18, null, "Let's get started."), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column" }, /* @__PURE__ */ React20.createElement(Text18, { bold: true }, "Choose the option that looks best when you select it:"), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "To change this later, run /config")), /* @__PURE__ */ React20.createElement(
4422
+ Select,
4423
+ {
4424
+ options: [
4425
+ { label: "Light text", value: "dark" },
4426
+ { label: "Dark text", value: "light" },
4427
+ {
4428
+ label: "Light text (colorblind-friendly)",
4429
+ value: "dark-daltonized"
4430
+ },
4431
+ {
4432
+ label: "Dark text (colorblind-friendly)",
4433
+ value: "light-daltonized"
4434
+ }
4435
+ ],
4436
+ onFocus: handleThemePreview,
4437
+ onChange: handleThemeSelection
4438
+ }
4439
+ ), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column" }, /* @__PURE__ */ React20.createElement(
4440
+ Box17,
4441
+ {
4442
+ paddingLeft: 1,
4443
+ marginRight: 1,
4444
+ borderStyle: "round",
4445
+ borderColor: "gray",
4446
+ flexDirection: "column"
4447
+ },
4448
+ /* @__PURE__ */ React20.createElement(
4449
+ StructuredDiff,
4450
+ {
4451
+ patch: {
4452
+ oldStart: 1,
4453
+ newStart: 1,
4454
+ oldLines: 3,
4455
+ newLines: 3,
4456
+ lines: [
4457
+ "function greet() {",
4458
+ '- console.log("Hello, World!");',
4459
+ '+ console.log("Hello, anon!");',
4460
+ "}"
4461
+ ]
4462
+ },
4463
+ dim: false,
4464
+ width: 40,
4465
+ overrideTheme: selectedTheme
4466
+ }
4467
+ )
4468
+ )));
4469
+ const providersStep = /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", gap: 1, paddingLeft: 1 }, /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React20.createElement(Text18, { color: theme.secondaryText }, "Next, let's select your preferred AI provider and model.")), /* @__PURE__ */ React20.createElement(
4470
+ ModelSelector,
4471
+ {
4472
+ onDone: handleProviderSelectionDone,
4473
+ skipModelType: true,
4474
+ isOnboarding: true
4475
+ }
4476
+ ));
4477
+ const usageStep = /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", gap: 1, paddingLeft: 1 }, /* @__PURE__ */ React20.createElement(Text18, { bold: true }, "Using ", PRODUCT_NAME, " effectively:"), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React20.createElement(OrderedList, null, /* @__PURE__ */ React20.createElement(OrderedList.Item, null, /* @__PURE__ */ React20.createElement(Text18, null, "Start in your project directory", /* @__PURE__ */ React20.createElement(Newline8, null), /* @__PURE__ */ React20.createElement(Text18, { color: theme.secondaryText }, "Files are automatically added to context when needed."), /* @__PURE__ */ React20.createElement(Newline8, null))), /* @__PURE__ */ React20.createElement(OrderedList.Item, null, /* @__PURE__ */ React20.createElement(Text18, null, "Use ", PRODUCT_NAME, " as a development partner", /* @__PURE__ */ React20.createElement(Newline8, null), /* @__PURE__ */ React20.createElement(Text18, { color: theme.secondaryText }, "Get help with file analysis, editing, bash commands,", /* @__PURE__ */ React20.createElement(Newline8, null), "and git history.", /* @__PURE__ */ React20.createElement(Newline8, null)))), /* @__PURE__ */ React20.createElement(OrderedList.Item, null, /* @__PURE__ */ React20.createElement(Text18, null, "Provide clear context", /* @__PURE__ */ React20.createElement(Newline8, null), /* @__PURE__ */ React20.createElement(Text18, { color: theme.secondaryText }, "Be as specific as you would with another engineer. ", /* @__PURE__ */ React20.createElement(Newline8, null), "The better the context, the better the results. ", /* @__PURE__ */ React20.createElement(Newline8, null)))))), /* @__PURE__ */ React20.createElement(PressEnterToContinue, null));
4478
+ const modelStep = /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", gap: 1, paddingLeft: 1 }, /* @__PURE__ */ React20.createElement(Text18, { bold: true }, "Configure your models:"), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", width: 70 }, /* @__PURE__ */ React20.createElement(Text18, null, "You can customize which models ", PRODUCT_NAME, " uses for different tasks.", /* @__PURE__ */ React20.createElement(Newline8, null), /* @__PURE__ */ React20.createElement(Text18, { color: theme.secondaryText }, "Let's set up your preferred models for large and small tasks.")), /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text18, null, "Press ", /* @__PURE__ */ React20.createElement(Text18, { color: theme.suggestion }, "Enter"), " to continue to the model selection screen."))), /* @__PURE__ */ React20.createElement(PressEnterToContinue, null));
4479
+ const steps = [];
4480
+ steps.push({ id: "theme", component: themeStep });
4481
+ steps.push({ id: "usage", component: usageStep });
4482
+ steps.push({ id: "model", component: modelStep });
4483
+ if (showModelSelector) {
4484
+ return /* @__PURE__ */ React20.createElement(
4485
+ ModelSelector,
4486
+ {
4487
+ onDone: handleModelSelectionDone,
4488
+ skipModelType: true,
4489
+ isOnboarding: true
4490
+ }
4491
+ );
4492
+ }
4493
+ return /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React20.createElement(Text18, { bold: true }, PRODUCT_NAME, " ", exitState.pending ? `(press ${exitState.keyName} again to exit)` : ""), steps[currentStepIndex]?.component)));
4494
+ }
4495
+ function WelcomeBox() {
4496
+ const theme = getTheme();
4497
+ return /* @__PURE__ */ React20.createElement(
4498
+ Box17,
4499
+ {
4500
+ borderColor: theme.kode,
4501
+ borderStyle: "round",
4502
+ paddingX: 1,
4503
+ width: MIN_LOGO_WIDTH
4504
+ },
4505
+ /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { color: theme.kode }, "\u273B"), " Welcome to", " ", /* @__PURE__ */ React20.createElement(Text18, { bold: true }, PRODUCT_NAME), " research preview!")
4506
+ );
4507
+ }
4508
+
4509
+ export {
4510
+ useDoublePress,
4511
+ useExitOnCtrlCD,
4512
+ StructuredDiff,
4513
+ TextInput,
4514
+ setTerminalTitle,
4515
+ clearTerminal,
4516
+ Logo,
4517
+ ModelSelector,
4518
+ Onboarding,
4519
+ WelcomeBox
4520
+ };