@openhands/agent-canvas 1.0.0-rc.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (600) hide show
  1. package/README.md +46 -31
  2. package/README.windows.md +2 -2
  3. package/bin/agent-canvas.mjs +3 -1
  4. package/build/assets/Trans-CBypf0vt.js +1 -0
  5. package/build/assets/{acp-providers-BqUvf67Q.js → acp-providers-BMyF5WII.js} +1 -1
  6. package/build/assets/active-backend-context-Cy_Ui5d7.js +1 -0
  7. package/build/assets/add-backend-modal-CUw7x0zm.js +1 -0
  8. package/build/assets/agent-server-client-options-FoO1qgAQ.js +1 -0
  9. package/build/assets/agent-server-conversation-service.api-D6Cmbnhd.js +5 -0
  10. package/build/assets/{agent-settings-DoHXHcoA.js → agent-settings-DW4vwOaz.js} +2 -2
  11. package/build/assets/{alert-banner-Bsvys-hT.js → alert-banner-BHmoArV2.js} +1 -1
  12. package/build/assets/{analytics-consent-form-modal-CVhsAREx.js → analytics-consent-form-modal-BNK0yu4L.js} +1 -1
  13. package/build/assets/api-key-entry-screen-BjVajIjf.js +1 -0
  14. package/build/assets/app-settings-C-5X6RzZ.js +1 -0
  15. package/build/assets/automation-detail-uDMoYmAc.js +1 -0
  16. package/build/assets/automations-list-DBCKCbId.js +1 -0
  17. package/build/assets/{back-nav-button-D20Avel_.js → back-nav-button-Bckz5Wh6.js} +1 -1
  18. package/build/assets/backend-form-modal-D2-XqJA4.js +1 -0
  19. package/build/assets/{backend-synced-settings-badge-Dcd4712m.js → backend-synced-settings-badge-Dx16CR_k.js} +1 -1
  20. package/build/assets/{base-modal-DvuGYyu1.js → base-modal-7QRNzf6r.js} +1 -1
  21. package/build/assets/{brand-button-9SOzmqPP.js → brand-button-CKLvirxz.js} +1 -1
  22. package/build/assets/browser-D1ZybR7v.js +5 -0
  23. package/build/assets/browser-store-hzasvz9c.js +1 -0
  24. package/build/assets/{browser-tab-bveyJhG9.js → browser-tab-iOQjXzmI.js} +1 -1
  25. package/build/assets/chat-send-button-_Uc9iFpG.js +1 -0
  26. package/build/assets/check-DfvhVBub.js +1 -0
  27. package/build/assets/{checkmark--JWAesua.js → checkmark-BHPhb50L.js} +1 -1
  28. package/build/assets/{chevron-down-B9j6lcFg.js → chevron-down-CMkqyPEY.js} +1 -1
  29. package/build/assets/{circle-plus-check-toggle-AO9DWyDX.js → circle-plus-check-toggle-ChmnRlpR.js} +1 -1
  30. package/build/assets/{close--W-tHn1h.js → close-DBs90sI6.js} +1 -1
  31. package/build/assets/{code-tag-chmctf4w.js → code-tag-BtHOqHwB.js} +1 -1
  32. package/build/assets/{color-themes-0biOprdo.js → color-themes-CBikRn3w.js} +1 -1
  33. package/build/assets/{combobox-caret-C9BNHfSu.js → combobox-caret-evUAh6YW.js} +1 -1
  34. package/build/assets/{command-store-UzKGSUC2.js → command-store-DZfR8QtE.js} +1 -1
  35. package/build/assets/condenser-settings-0tw3yXX8.js +1 -0
  36. package/build/assets/{confirmation-modal-Cq3eqvqH.js → confirmation-modal-DWuxe1zG.js} +1 -1
  37. package/build/assets/{context-menu-list-item-BQU0jZPq.js → context-menu-list-item-BbHFxHaq.js} +1 -1
  38. package/build/assets/conversation-B1v96pQf.js +19 -0
  39. package/build/assets/conversation-DgfcjCPm.js +1 -0
  40. package/build/assets/{conversation-local-storage-an3MiIiJ.js → conversation-local-storage-Btum2xBJ.js} +1 -1
  41. package/build/assets/conversation-panel-TJ7swm6a.js +1 -0
  42. package/build/assets/conversation-service.api-DXawvmNN.js +1 -0
  43. package/build/assets/conversation-state-store-DmuPn875.js +1 -0
  44. package/build/assets/{conversation-store-FQmakEd-.js → conversation-store-Bu89Yxg1.js} +1 -1
  45. package/build/assets/{conversation-tab-empty-state-C8tztloo.js → conversation-tab-empty-state-7js7FjYM.js} +1 -1
  46. package/build/assets/{copy-CdKN5rtJ.js → copy-B0SkXenR.js} +1 -1
  47. package/build/assets/copy-to-clipboard-button-CfH2EQz9.js +1 -0
  48. package/build/assets/{createLucideIcon-CkGTf9Qj.js → createLucideIcon-Bazr-V-B.js} +1 -1
  49. package/build/assets/declaration-Cn-NXHEy.js +1 -0
  50. package/build/assets/{device-verify-YuGXnpRk.js → device-verify-CWKyQ680.js} +1 -1
  51. package/build/assets/{dist-5dObF4p4.js → dist-Cbu6eGx8.js} +1 -1
  52. package/build/assets/dist-Dg2JWLGc.js +1 -0
  53. package/build/assets/{dropdown-classes-CVp0kVdt.js → dropdown-classes-BI1bI1ho.js} +1 -1
  54. package/build/assets/edit-automation-modal-DdAG6kFZ.js +1 -0
  55. package/build/assets/{ellipsis-button-k7HLwYth.js → ellipsis-button-DdCsOBHV.js} +1 -1
  56. package/build/assets/entry.client-DXyvKj5G.js +2 -0
  57. package/build/assets/{enum-filter-dropdown-COqUq3mB.js → enum-filter-dropdown-WROAIeys.js} +1 -1
  58. package/build/assets/{environment-switch-overlay-wB5LxfYZ.js → environment-switch-overlay-Dv0imPE6.js} +1 -1
  59. package/build/assets/{extensions-hub-CoWO3SzQ.js → extensions-hub-CkyDPXvn.js} +1 -1
  60. package/build/assets/{extensions-navigation-D5w2sFQe.js → extensions-navigation-gMR-la1J.js} +1 -1
  61. package/build/assets/{external-link-BATfjnkz.js → external-link-FsutnU75.js} +1 -1
  62. package/build/assets/files-tab-DNUoveJZ.js +1 -0
  63. package/build/assets/files-tab-store-SyvkH3ic.js +1 -0
  64. package/build/assets/{folder-DPTBlxWb.js → folder-DusyBXDE.js} +1 -1
  65. package/build/assets/{globe-Dpgjreyy.js → globe-BUcin4kT.js} +1 -1
  66. package/build/assets/home-D809jWVS.js +1 -0
  67. package/build/assets/{iconBase-BVhFI-0E.js → iconBase-B6CXDDaE.js} +1 -1
  68. package/build/assets/index-redirect-nMYgOTBX.js +1 -0
  69. package/build/assets/install-server-modal-D92Qy57I.js +1 -0
  70. package/build/assets/{launch-fU77utLO.js → launch-BnCG2g2a.js} +1 -1
  71. package/build/assets/lesson-plan-j-ZaE8vK.js +3 -0
  72. package/build/assets/{lightbulb-S616sGEL.js → lightbulb-BJ5tWmAh.js} +1 -1
  73. package/build/assets/{link-external-ooNztGLp.js → link-external-DUtmHAHi.js} +1 -1
  74. package/build/assets/{list-todo-nCa9zxVE.js → list-todo-C-gekh-4.js} +1 -1
  75. package/build/assets/{llm-client-P9f2gKeW.js → llm-client-BZUkZYlc.js} +1 -1
  76. package/build/assets/llm-not-configured-banner-PlvJ99rE.js +27 -0
  77. package/build/assets/llm-settings-Dgk32xRW.js +1 -0
  78. package/build/assets/llm-settings-Dxo7owKm.js +1 -0
  79. package/build/assets/llm-subscription-Bvu8BLXc.js +1 -0
  80. package/build/assets/llm-subscription-service-Bq6z3Wkv.js +1 -0
  81. package/build/assets/loader-circle-Cw7_q9hg.js +1 -0
  82. package/build/assets/{loading-spinner-D7bOU6I7.js → loading-spinner-RF3IlMF5.js} +1 -1
  83. package/build/assets/manage-backends-modal-BT66a6e0.js +1 -0
  84. package/build/assets/manifest-b8402d59.js +1 -0
  85. package/build/assets/markdown-renderer-Tw6FR2ad.js +2 -0
  86. package/build/assets/mcp-B_pA7vZg.js +9 -0
  87. package/build/assets/mcp-client-ppbYplKX.js +1 -0
  88. package/build/assets/messages-Cg9aNVi6.js +41 -0
  89. package/build/assets/{modal-backdrop-_VbcKk5K.js → modal-backdrop-DRpBVVW5.js} +1 -1
  90. package/build/assets/{modal-body-Cd-PtMHr.js → modal-body-CEV2O67p.js} +1 -1
  91. package/build/assets/{modal-classes-CNm4ovfm.js → modal-classes-B2DuQ9qD.js} +1 -1
  92. package/build/assets/{modal-close-button-Dbo0NAMt.js → modal-close-button-gtb7RHFB.js} +1 -1
  93. package/build/assets/model-selector-CAv3RrJ3.js +1 -0
  94. package/build/assets/{mouse-pointer-click-D0QNGSIo.js → mouse-pointer-click-B3i6HcaF.js} +1 -1
  95. package/build/assets/{navigation-context-BpAOh_t5.js → navigation-context-CM9JmTvm.js} +1 -1
  96. package/build/assets/{navigation-link-Dci7Nuyf.js → navigation-link-FmXzqR1J.js} +1 -1
  97. package/build/assets/onboarding-Q9CzuVWy.js +1 -0
  98. package/build/assets/{openhands-logo-Dzm-d_V_.js → openhands-logo-B4aEO2sf.js} +1 -1
  99. package/build/assets/option-service.api-C-s2BYZp.js +1 -0
  100. package/build/assets/organization-service.api-CXTKpFnV.js +1 -0
  101. package/build/assets/{path-utils-YlkpoImJ.js → path-utils-CE98fIFP.js} +1 -1
  102. package/build/assets/{pencil-DPEtl5QD.js → pencil-bWrxRWlr.js} +1 -1
  103. package/build/assets/{plan-components-CutRBi0G.js → plan-components-BF-X7vnQ.js} +1 -1
  104. package/build/assets/{planner-tab-DjIFIQDk.js → planner-tab-jkyaGiRc.js} +1 -1
  105. package/build/assets/plus-BTCK8WtR.js +1 -0
  106. package/build/assets/{profiles-client-BNvHck-I.js → profiles-client-CQ9L1yHR.js} +1 -1
  107. package/build/assets/providers-B3hkV58J.js +1 -0
  108. package/build/assets/proxy-BOcEWkCD.js +1 -0
  109. package/build/assets/{react-dom-hVBnwgwZ.js → react-dom-BBXlXOZX.js} +1 -1
  110. package/build/assets/{react-CM_dJw1Z.js → react-uQK8du1l.js} +1 -1
  111. package/build/assets/{react-Dy05vyj5.js → react-x84lbX6s.js} +1 -1
  112. package/build/assets/{recommended-automations-launcher-DCi0RXIj.js → recommended-automations-launcher-DMk35aFI.js} +11 -11
  113. package/build/assets/root-CV0kyxfP.js +2 -0
  114. package/build/assets/root-PB2Jnj61.css +1 -0
  115. package/build/assets/root-layout-Bl4Qzg5J.js +2 -0
  116. package/build/assets/{runtime-waiting-state-8i0Z3A_i.js → runtime-waiting-state-C1hJpXxl.js} +1 -1
  117. package/build/assets/sdk-section-page-DHbC2_j3.js +1 -0
  118. package/build/assets/sdk-settings-schema-z51bBmMl.js +1 -0
  119. package/build/assets/{search-C_l_XW4e.js → search-Dxm1MUMi.js} +1 -1
  120. package/build/assets/{secrets-service-D6Gcymcs.js → secrets-service-DlScPUwy.js} +1 -1
  121. package/build/assets/secrets-settings-B97-JKBe.js +1 -0
  122. package/build/assets/settings-BpmooyBV.js +1 -0
  123. package/build/assets/{settings-dropdown-input-BP-arqjU.js → settings-dropdown-input-hIIAlmHb.js} +1 -1
  124. package/build/assets/{settings-gear-f-zWpxfI.js → settings-gear-DQLTCnFx.js} +1 -1
  125. package/build/assets/{settings-index-B035V2pu.js → settings-index-kKASonWk.js} +1 -1
  126. package/build/assets/{settings-input-BM8ANxDb.js → settings-input-BlOzCtP3.js} +1 -1
  127. package/build/assets/{settings-list-classes-B9uo5WES.js → settings-list-classes-BFRbCy4U.js} +1 -1
  128. package/build/assets/settings-modal-C4pZwj-l.js +1 -0
  129. package/build/assets/{settings-section-header-context-Cid25HM2.js → settings-section-header-context-iz4Go0dL.js} +1 -1
  130. package/build/assets/settings-service.api-DVLwi82B.js +1 -0
  131. package/build/assets/{settings-switch-xa6q4M6j.js → settings-switch-6cXIH-UE.js} +1 -1
  132. package/build/assets/{settings-utils--H_EvFjx.js → settings-utils-CfScEaPn.js} +1 -1
  133. package/build/assets/shared-conversation-CLkayApe.js +1 -0
  134. package/build/assets/sidebar-mobile-menu-toggle-C8uT4Mhd.js +1 -0
  135. package/build/assets/{sidebar-nav-link-CPWuQ_c6.js → sidebar-nav-link-BKZiAQxQ.js} +1 -1
  136. package/build/assets/{skill-card-pill-row-DeodrwPG.js → skill-card-pill-row-D13WRKIE.js} +1 -1
  137. package/build/assets/{skills-BE5HJuGM.js → skills-NpRk6v79.js} +1 -1
  138. package/build/assets/{skills-client-Ui_9FvXp.js → skills-client-DsqwQEwj.js} +1 -1
  139. package/build/assets/{skills-plugins-Dc4ec3CW.js → skills-plugins-bkwxItAw.js} +1 -1
  140. package/build/assets/{skills-settings-B6HwvA0S.js → skills-settings-DMbrlB2b.js} +2 -2
  141. package/build/assets/{square-chevron-right-BWAB7NPp.js → square-chevron-right-DxZKcVq4.js} +1 -1
  142. package/build/assets/{styled-tooltip-X61SEwqk.js → styled-tooltip-DG1ofAcS.js} +1 -1
  143. package/build/assets/suspense-sOGeme-f.js +1 -0
  144. package/build/assets/{switch-skeleton-BbIWemkK.js → switch-skeleton-CZ6XCVZk.js} +1 -1
  145. package/build/assets/{task-list-tab-BFnyHGsn.js → task-list-tab-CddMPNCD.js} +1 -1
  146. package/build/assets/telemetry-CX4mTZUw.js +2 -0
  147. package/build/assets/{terminal-CltMeLv4.js → terminal-BOAVxAej.js} +1 -1
  148. package/build/assets/{terminal-B92mACR5.js → terminal-DbtwHqt4.js} +1 -1
  149. package/build/assets/{toggle-switch-CL_wRiGs.js → toggle-switch-C0t6ge2m.js} +1 -1
  150. package/build/assets/{trash-2-C-RvV8WA.js → trash-2-Dmprz0A0.js} +1 -1
  151. package/build/assets/{typography-BLSEGjph.js → typography-CNHbO1eE.js} +1 -1
  152. package/build/assets/{u-check-circle-dG_VJqx5.js → u-check-circle-CX66WzfT.js} +1 -1
  153. package/build/assets/{u-check-circle-half-D-5ZPRnd.js → u-check-circle-half-Bj853Ub0.js} +1 -1
  154. package/build/assets/{u-circuit-BXoisXOA.js → u-circuit-B9bLCP7N.js} +1 -1
  155. package/build/assets/{u-edit-DIgADxLH.js → u-edit-D7JyvoWm.js} +1 -1
  156. package/build/assets/use-acp-credential-form-Cj_oUYd5.js +1 -0
  157. package/build/assets/{use-active-conversation-B0_rXnqa.js → use-active-conversation-B64-yy2X.js} +1 -1
  158. package/build/assets/use-agent-settings-schema-BzbQYEzb.js +1 -0
  159. package/build/assets/{use-agent-state-BuOzLeFN.js → use-agent-state-DxFk391r.js} +1 -1
  160. package/build/assets/{use-breakpoint-DpxHDmuH.js → use-breakpoint-Cent5lpI.js} +1 -1
  161. package/build/assets/{use-click-outside-element-DhxCUyWl.js → use-click-outside-element-DOYR26nw.js} +1 -1
  162. package/build/assets/{use-cloud-current-user-id-DVZXa_WH.js → use-cloud-current-user-id-BVf7pb8n.js} +1 -1
  163. package/build/assets/use-config-CvdJUq4E.js +1 -0
  164. package/build/assets/use-create-conversation-Cnouynop.js +1 -0
  165. package/build/assets/{use-create-secret-ClPpLBgD.js → use-create-secret-Cgu4uxyr.js} +1 -1
  166. package/build/assets/{use-event-store-DNSmyzUh.js → use-event-store-Nv92IYMU.js} +1 -1
  167. package/build/assets/use-handle-plan-click-C7824bbe.js +1 -0
  168. package/build/assets/use-is-authed-mnJtd2bw.js +1 -0
  169. package/build/assets/{use-launch-skill-in-chat-BTgXIu3N.js → use-launch-skill-in-chat-NDUo_UCA.js} +1 -1
  170. package/build/assets/use-llm-profiles-9eH3Y3Ef.js +1 -0
  171. package/build/assets/use-runtime-is-ready-DEnxJH7W.js +1 -0
  172. package/build/assets/use-save-settings-DrClHnX7.js +1 -0
  173. package/build/assets/use-settings-BNAoCX93.js +1 -0
  174. package/build/assets/{use-settings-nav-items-DVrMNk0I.js → use-settings-nav-items-CLIW2p2n.js} +2 -2
  175. package/build/assets/{use-skills-Bv8LXrys.js → use-skills-B9gOHrOj.js} +1 -1
  176. package/build/assets/{use-task-list-E5t-jtgs.js → use-task-list-DLKPnq3I.js} +1 -1
  177. package/build/assets/{use-tracking-BhyV5A9i.js → use-tracking-Dph_dRlL.js} +1 -1
  178. package/build/assets/{use-user-conversation-6yFVEXiG.js → use-user-conversation-leIsQH1B.js} +1 -1
  179. package/build/assets/useMutation-cXADjWAv.js +1 -0
  180. package/build/assets/useQuery-BQr6A6ry.js +1 -0
  181. package/build/assets/useTranslation-Bp1btAjp.js +1 -0
  182. package/build/assets/{utils-eXk6yerZ.js → utils-o-OXNtEy.js} +1 -1
  183. package/build/assets/v4-DOSjXdIn.js +1 -0
  184. package/build/assets/{vendor~browser-tab-BGJDGHyS.js → vendor~browser-tab-642bOcE1.js} +1 -1
  185. package/build/assets/{vendor~conversation-panel~conversation-D98u43rl.js → vendor~conversation-panel~conversation-DqmbnXRn.js} +1 -1
  186. package/build/assets/{vendor~conversation-panel~conversation~alert-banner-CtjHOMO_.js → vendor~conversation-panel~conversation~alert-banner-CSDVP1nw.js} +1 -1
  187. package/build/assets/{vendor~conversation-panel~conversation~index-BXt2-3Cc.js → vendor~conversation-panel~conversation~index-DITarr3F.js} +1 -1
  188. package/build/assets/{vendor~entry.client-DrAdOKqA.js → vendor~entry.client-CQ77DucR.js} +1 -1
  189. package/build/assets/{vendor~entry.client-EcEhmoJW.js → vendor~entry.client-ePAFmSwg.js} +1 -1
  190. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~f0pn7nss-DUk-MwlC.js +177 -0
  191. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~lb3qbbk3-BvmOva8I.js +1 -0
  192. package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~lpdshwee-Cl4h-NSi.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~lpdshwee-BkQQxq_q.js} +1 -1
  193. package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~oli4dvxu-BmOMM-6e.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~oli4dvxu-8xF3WeB9.js} +1 -1
  194. package/build/assets/{vendor~entry.client~root~root-layout~index-redirect~home~conversation-panel~conversation~la~cnj3raoq-BjMt2JeR.js → vendor~entry.client~root~root-layout~index-redirect~home~conversation-panel~conversation~la~nyk4vv4g-UzR6fe3Y.js} +1 -1
  195. package/build/assets/{vendor~files-tab--640YzQO.js → vendor~files-tab-Cko7Kr5k.js} +1 -1
  196. package/build/assets/{vendor~home~conversation-panel~conversation-dhKA2JVJ.js → vendor~home~conversation-panel~conversation-D3otw5Jn.js} +1 -1
  197. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DMOoHVBy.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CMdKUYP2.js} +1 -1
  198. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BoE6JRJj.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DSR6Mugx.js} +1 -1
  199. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dqpi2xyZ.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DdOzMvB1.js} +1 -1
  200. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DX2CmDJ0.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dj2eezvL.js} +1 -1
  201. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BNExlCqT.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DyN8jeZz.js} +1 -1
  202. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DCn1M97v.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-lpEWrbMA.js} +1 -1
  203. package/build/assets/{vendor~launch-C0VvUe2W.js → vendor~launch-DoYO54df.js} +1 -1
  204. package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-COLUW-6M.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-BGwU2SbU.js} +1 -1
  205. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation-eG1kjEbj.js → vendor~root-layout~home~conversation-panel~conversation-Iqs0j4Wo.js} +1 -1
  206. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-7eqKdLSQ.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-BdS6bbw9.js} +1 -1
  207. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-BP3MGAAY.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-Fd3F3jth.js} +1 -1
  208. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ceijy54y-Dkd2wxJA.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ceijy54y-Cg0AqwMN.js} +2 -2
  209. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~bxn4ldi1-_LydBB1e.js → vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~bxn4ldi1-BlsbqRyZ.js} +1 -1
  210. package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~ec6pk3ix-CBICqfKC.js +48 -0
  211. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~skills-settings~mcp~automati~dou5smdo-DavKsAvB.js → vendor~root-layout~home~conversation-panel~conversation~launch~skills-settings~automations-~nv6afr11-BYNoKMU0.js} +97 -69
  212. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-CsO1gYxF.js → vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-BspUuSsP.js} +1 -1
  213. package/build/assets/{vendor~root-layout~home~mcp~automations-list-D40jgugf.js → vendor~root-layout~home~mcp~automations-list-C9U_eOqU.js} +1 -1
  214. package/build/assets/{vendor~root-layout~home~mcp~automations-list-cNHi83v_.js → vendor~root-layout~home~mcp~automations-list-DhQ3LPVw.js} +1 -1
  215. package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-xCM3k6Rd.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BEbhHQYg.js} +1 -1
  216. package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BIm99UaF.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-DdgDNzMc.js} +2 -2
  217. package/build/assets/{verification-settings-Be6NxjuT.js → verification-settings-D7R-Ne6L.js} +1 -1
  218. package/build/assets/x-DCZg87p7.js +1 -0
  219. package/build/assets/{x-mark-C42svtnu.js → x-mark-jrBTJbnP.js} +1 -1
  220. package/build/index.html +4 -4
  221. package/build/locales/ar/openhands.json +22 -0
  222. package/build/locales/ca/openhands.json +22 -0
  223. package/build/locales/de/openhands.json +22 -0
  224. package/build/locales/en/openhands.json +22 -0
  225. package/build/locales/es/openhands.json +22 -0
  226. package/build/locales/fr/openhands.json +22 -0
  227. package/build/locales/it/openhands.json +22 -0
  228. package/build/locales/ja/openhands.json +22 -0
  229. package/build/locales/ko-KR/openhands.json +22 -0
  230. package/build/locales/no/openhands.json +22 -0
  231. package/build/locales/pt/openhands.json +22 -0
  232. package/build/locales/tr/openhands.json +22 -0
  233. package/build/locales/uk/openhands.json +22 -0
  234. package/build/locales/zh-CN/openhands.json +22 -0
  235. package/build/locales/zh-TW/openhands.json +22 -0
  236. package/config/defaults.json +4 -7
  237. package/dist/api/agent-server-adapter.cjs +3 -3
  238. package/dist/api/agent-server-adapter.cjs.map +1 -1
  239. package/dist/api/agent-server-adapter.d.ts +8 -0
  240. package/dist/api/agent-server-adapter.js +82 -80
  241. package/dist/api/agent-server-adapter.js.map +1 -1
  242. package/dist/api/agent-server-compatibility.cjs +1 -1
  243. package/dist/api/agent-server-compatibility.cjs.map +1 -1
  244. package/dist/api/agent-server-compatibility.d.ts +10 -0
  245. package/dist/api/agent-server-compatibility.js +69 -25
  246. package/dist/api/agent-server-compatibility.js.map +1 -1
  247. package/dist/api/backend-registry/default-backend.cjs +1 -1
  248. package/dist/api/backend-registry/default-backend.cjs.map +1 -1
  249. package/dist/api/backend-registry/default-backend.d.ts +6 -5
  250. package/dist/api/backend-registry/default-backend.js +1 -1
  251. package/dist/api/backend-registry/default-backend.js.map +1 -1
  252. package/dist/api/backend-registry/storage.cjs +1 -1
  253. package/dist/api/backend-registry/storage.cjs.map +1 -1
  254. package/dist/api/backend-registry/storage.js +38 -68
  255. package/dist/api/backend-registry/storage.js.map +1 -1
  256. package/dist/api/cloud/proxy.cjs +1 -1
  257. package/dist/api/cloud/proxy.cjs.map +1 -1
  258. package/dist/api/cloud/proxy.d.ts +3 -16
  259. package/dist/api/cloud/proxy.js +1 -1
  260. package/dist/api/cloud/proxy.js.map +1 -1
  261. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  262. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  263. package/dist/api/conversation-service/agent-server-conversation-service.api.js +75 -75
  264. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  265. package/dist/api/llm-subscription-service.cjs +2 -0
  266. package/dist/api/llm-subscription-service.cjs.map +1 -0
  267. package/dist/api/llm-subscription-service.d.ts +23 -0
  268. package/dist/api/llm-subscription-service.js +109 -0
  269. package/dist/api/llm-subscription-service.js.map +1 -0
  270. package/dist/api/mcp-service/mcp-service.api.cjs +1 -1
  271. package/dist/api/mcp-service/mcp-service.api.cjs.map +1 -1
  272. package/dist/api/mcp-service/mcp-service.api.d.ts +2 -3
  273. package/dist/api/mcp-service/mcp-service.api.js +38 -13
  274. package/dist/api/mcp-service/mcp-service.api.js.map +1 -1
  275. package/dist/api/settings-service/settings-service.api.cjs +1 -1
  276. package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
  277. package/dist/api/settings-service/settings-service.api.js +41 -42
  278. package/dist/api/settings-service/settings-service.api.js.map +1 -1
  279. package/dist/components/conversation-events/chat/messages.cjs +1 -1
  280. package/dist/components/conversation-events/chat/messages.cjs.map +1 -1
  281. package/dist/components/conversation-events/chat/messages.js +4 -4
  282. package/dist/components/conversation-events/chat/messages.js.map +1 -1
  283. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  284. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  285. package/dist/components/features/backends/backend-form-modal.js +136 -135
  286. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  287. package/dist/components/features/backends/backend-selector.cjs +1 -1
  288. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  289. package/dist/components/features/backends/backend-selector.js +103 -91
  290. package/dist/components/features/backends/backend-selector.js.map +1 -1
  291. package/dist/components/features/backends/manage-backends-modal.cjs +1 -1
  292. package/dist/components/features/backends/manage-backends-modal.cjs.map +1 -1
  293. package/dist/components/features/backends/manage-backends-modal.js +14 -8
  294. package/dist/components/features/backends/manage-backends-modal.js.map +1 -1
  295. package/dist/components/features/chat/path-component.cjs +1 -1
  296. package/dist/components/features/chat/path-component.cjs.map +1 -1
  297. package/dist/components/features/chat/path-component.js +1 -1
  298. package/dist/components/features/chat/path-component.js.map +1 -1
  299. package/dist/components/features/home/workspace-dropdown/folder-browser-modal.cjs +1 -1
  300. package/dist/components/features/home/workspace-dropdown/folder-browser-modal.cjs.map +1 -1
  301. package/dist/components/features/home/workspace-dropdown/folder-browser-modal.js +77 -66
  302. package/dist/components/features/home/workspace-dropdown/folder-browser-modal.js.map +1 -1
  303. package/dist/components/features/mcp-page/custom-server-editor.cjs +1 -1
  304. package/dist/components/features/mcp-page/custom-server-editor.cjs.map +1 -1
  305. package/dist/components/features/mcp-page/custom-server-editor.js +1 -0
  306. package/dist/components/features/mcp-page/custom-server-editor.js.map +1 -1
  307. package/dist/components/features/mcp-page/install-server-modal.cjs +1 -1
  308. package/dist/components/features/mcp-page/install-server-modal.cjs.map +1 -1
  309. package/dist/components/features/mcp-page/install-server-modal.js +73 -46
  310. package/dist/components/features/mcp-page/install-server-modal.js.map +1 -1
  311. package/dist/components/features/mcp-page/marketplace-card.cjs +1 -1
  312. package/dist/components/features/mcp-page/marketplace-card.cjs.map +1 -1
  313. package/dist/components/features/mcp-page/marketplace-card.js +6 -2
  314. package/dist/components/features/mcp-page/marketplace-card.js.map +1 -1
  315. package/dist/components/features/settings/brand-button.cjs +1 -1
  316. package/dist/components/features/settings/brand-button.cjs.map +1 -1
  317. package/dist/components/features/settings/brand-button.js +1 -1
  318. package/dist/components/features/settings/brand-button.js.map +1 -1
  319. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.cjs +2 -0
  320. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.cjs.map +1 -0
  321. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.d.ts +5 -0
  322. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.js +167 -0
  323. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.js.map +1 -0
  324. package/dist/components/shared/modals/settings/model-selector.cjs +1 -1
  325. package/dist/components/shared/modals/settings/model-selector.cjs.map +1 -1
  326. package/dist/components/shared/modals/settings/model-selector.d.ts +1 -2
  327. package/dist/components/shared/modals/settings/model-selector.js +59 -65
  328. package/dist/components/shared/modals/settings/model-selector.js.map +1 -1
  329. package/dist/components/shared/modals/settings/settings-form.cjs +1 -1
  330. package/dist/components/shared/modals/settings/settings-form.cjs.map +1 -1
  331. package/dist/components/shared/modals/settings/settings-form.js +1 -2
  332. package/dist/components/shared/modals/settings/settings-form.js.map +1 -1
  333. package/dist/config/defaults.cjs +2 -0
  334. package/dist/config/defaults.cjs.map +1 -0
  335. package/dist/config/defaults.js +35 -0
  336. package/dist/config/defaults.js.map +1 -0
  337. package/dist/constants/llm-subscription.cjs +2 -0
  338. package/dist/constants/llm-subscription.cjs.map +1 -0
  339. package/dist/constants/llm-subscription.d.ts +16 -0
  340. package/dist/constants/llm-subscription.js +47 -0
  341. package/dist/constants/llm-subscription.js.map +1 -0
  342. package/dist/contexts/conversation-websocket-context.cjs +1 -1
  343. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  344. package/dist/contexts/conversation-websocket-context.js +1 -1
  345. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  346. package/dist/hooks/mutation/use-llm-subscription-auth.cjs +2 -0
  347. package/dist/hooks/mutation/use-llm-subscription-auth.cjs.map +1 -0
  348. package/dist/hooks/mutation/use-llm-subscription-auth.d.ts +3 -0
  349. package/dist/hooks/mutation/use-llm-subscription-auth.js +30 -0
  350. package/dist/hooks/mutation/use-llm-subscription-auth.js.map +1 -0
  351. package/dist/hooks/mutation/use-save-fields-as-secrets.cjs +1 -1
  352. package/dist/hooks/mutation/use-save-fields-as-secrets.cjs.map +1 -1
  353. package/dist/hooks/mutation/use-save-fields-as-secrets.d.ts +8 -6
  354. package/dist/hooks/mutation/use-save-fields-as-secrets.js +1 -1
  355. package/dist/hooks/mutation/use-save-fields-as-secrets.js.map +1 -1
  356. package/dist/hooks/mutation/use-test-mcp-server.d.ts +1 -1
  357. package/dist/hooks/query/query-keys.cjs +1 -1
  358. package/dist/hooks/query/query-keys.cjs.map +1 -1
  359. package/dist/hooks/query/query-keys.d.ts +5 -0
  360. package/dist/hooks/query/query-keys.js +14 -2
  361. package/dist/hooks/query/query-keys.js.map +1 -1
  362. package/dist/hooks/query/use-agent-settings-schema.cjs +1 -1
  363. package/dist/hooks/query/use-agent-settings-schema.cjs.map +1 -1
  364. package/dist/hooks/query/use-agent-settings-schema.d.ts +2 -2
  365. package/dist/hooks/query/use-agent-settings-schema.js +21 -19
  366. package/dist/hooks/query/use-agent-settings-schema.js.map +1 -1
  367. package/dist/hooks/query/use-backends-health.cjs +1 -1
  368. package/dist/hooks/query/use-backends-health.cjs.map +1 -1
  369. package/dist/hooks/query/use-backends-health.js +38 -36
  370. package/dist/hooks/query/use-backends-health.js.map +1 -1
  371. package/dist/hooks/query/use-llm-subscription-models.cjs +2 -0
  372. package/dist/hooks/query/use-llm-subscription-models.cjs.map +1 -0
  373. package/dist/hooks/query/use-llm-subscription-models.d.ts +3 -0
  374. package/dist/hooks/query/use-llm-subscription-models.js +19 -0
  375. package/dist/hooks/query/use-llm-subscription-models.js.map +1 -0
  376. package/dist/hooks/query/use-llm-subscription-status.cjs +2 -0
  377. package/dist/hooks/query/use-llm-subscription-status.cjs.map +1 -0
  378. package/dist/hooks/query/use-llm-subscription-status.d.ts +3 -0
  379. package/dist/hooks/query/use-llm-subscription-status.js +19 -0
  380. package/dist/hooks/query/use-llm-subscription-status.js.map +1 -0
  381. package/dist/hooks/use-websocket.cjs +1 -1
  382. package/dist/hooks/use-websocket.cjs.map +1 -1
  383. package/dist/hooks/use-websocket.d.ts +0 -1
  384. package/dist/hooks/use-websocket.js +37 -38
  385. package/dist/hooks/use-websocket.js.map +1 -1
  386. package/dist/i18n/declaration.cjs +1 -1
  387. package/dist/i18n/declaration.cjs.map +1 -1
  388. package/dist/i18n/declaration.d.ts +22 -0
  389. package/dist/i18n/declaration.js +1 -1
  390. package/dist/i18n/declaration.js.map +1 -1
  391. package/dist/i18n/translation.cjs +2 -2
  392. package/dist/i18n/translation.cjs.map +1 -1
  393. package/dist/i18n/translation.js +374 -0
  394. package/dist/i18n/translation.js.map +1 -1
  395. package/dist/locales/ar/openhands.json +22 -0
  396. package/dist/locales/ca/openhands.json +22 -0
  397. package/dist/locales/de/openhands.json +22 -0
  398. package/dist/locales/en/openhands.json +22 -0
  399. package/dist/locales/es/openhands.json +22 -0
  400. package/dist/locales/fr/openhands.json +22 -0
  401. package/dist/locales/it/openhands.json +22 -0
  402. package/dist/locales/ja/openhands.json +22 -0
  403. package/dist/locales/ko-KR/openhands.json +22 -0
  404. package/dist/locales/no/openhands.json +22 -0
  405. package/dist/locales/pt/openhands.json +22 -0
  406. package/dist/locales/tr/openhands.json +22 -0
  407. package/dist/locales/uk/openhands.json +22 -0
  408. package/dist/locales/zh-CN/openhands.json +22 -0
  409. package/dist/locales/zh-TW/openhands.json +22 -0
  410. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.cjs +1 -1
  411. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.cjs.map +1 -1
  412. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.js +10 -22
  413. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.js.map +1 -1
  414. package/dist/node_modules/@openhands/extensions/skills/index.cjs +97 -69
  415. package/dist/node_modules/@openhands/extensions/skills/index.cjs.map +1 -1
  416. package/dist/node_modules/@openhands/extensions/skills/index.js +3 -3
  417. package/dist/node_modules/@openhands/extensions/skills/index.js.map +1 -1
  418. package/dist/package.cjs +1 -1
  419. package/dist/package.cjs.map +1 -1
  420. package/dist/package.js +3 -5
  421. package/dist/package.js.map +1 -1
  422. package/dist/routes/files-tab.cjs +1 -1
  423. package/dist/routes/files-tab.cjs.map +1 -1
  424. package/dist/routes/files-tab.js +28 -17
  425. package/dist/routes/files-tab.js.map +1 -1
  426. package/dist/routes/llm-settings.cjs +1 -1
  427. package/dist/routes/llm-settings.cjs.map +1 -1
  428. package/dist/routes/llm-settings.js +142 -85
  429. package/dist/routes/llm-settings.js.map +1 -1
  430. package/dist/services/canvas-ui.cjs +1 -1
  431. package/dist/services/canvas-ui.cjs.map +1 -1
  432. package/dist/services/canvas-ui.d.ts +1 -1
  433. package/dist/services/canvas-ui.js +2 -2
  434. package/dist/services/canvas-ui.js.map +1 -1
  435. package/dist/stores/files-tab-store.cjs +1 -1
  436. package/dist/stores/files-tab-store.cjs.map +1 -1
  437. package/dist/stores/files-tab-store.d.ts +2 -1
  438. package/dist/stores/files-tab-store.js +5 -1
  439. package/dist/stores/files-tab-store.js.map +1 -1
  440. package/dist/stores/use-event-store.cjs +1 -1
  441. package/dist/stores/use-event-store.cjs.map +1 -1
  442. package/dist/stores/use-event-store.js +39 -34
  443. package/dist/stores/use-event-store.js.map +1 -1
  444. package/dist/types/mcp-server.d.ts +21 -0
  445. package/dist/ui/dropdown/dropdown.cjs +1 -1
  446. package/dist/ui/dropdown/dropdown.cjs.map +1 -1
  447. package/dist/ui/dropdown/dropdown.js +6 -2
  448. package/dist/ui/dropdown/dropdown.js.map +1 -1
  449. package/dist/utils/handle-event-for-ui.cjs +1 -1
  450. package/dist/utils/handle-event-for-ui.cjs.map +1 -1
  451. package/dist/utils/handle-event-for-ui.d.ts +2 -0
  452. package/dist/utils/handle-event-for-ui.js +1 -1
  453. package/dist/utils/handle-event-for-ui.js.map +1 -1
  454. package/dist/utils/llm-subscription-schema.cjs +2 -0
  455. package/dist/utils/llm-subscription-schema.cjs.map +1 -0
  456. package/dist/utils/llm-subscription-schema.d.ts +2 -0
  457. package/dist/utils/llm-subscription-schema.js +22 -0
  458. package/dist/utils/llm-subscription-schema.js.map +1 -0
  459. package/dist/utils/mcp-config.cjs +1 -1
  460. package/dist/utils/mcp-config.cjs.map +1 -1
  461. package/dist/utils/mcp-config.js +32 -17
  462. package/dist/utils/mcp-config.js.map +1 -1
  463. package/dist/utils/mcp-credential-validation.cjs +2 -0
  464. package/dist/utils/mcp-credential-validation.cjs.map +1 -0
  465. package/dist/utils/mcp-credential-validation.d.ts +22 -0
  466. package/dist/utils/mcp-credential-validation.js +31 -0
  467. package/dist/utils/mcp-credential-validation.js.map +1 -0
  468. package/dist/utils/mcp-marketplace-utils.cjs +1 -1
  469. package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
  470. package/dist/utils/mcp-marketplace-utils.d.ts +7 -2
  471. package/dist/utils/mcp-marketplace-utils.js +33 -48
  472. package/dist/utils/mcp-marketplace-utils.js.map +1 -1
  473. package/dist/utils/sdk-settings-schema.cjs +1 -1
  474. package/dist/utils/sdk-settings-schema.cjs.map +1 -1
  475. package/dist/utils/sdk-settings-schema.js +64 -61
  476. package/dist/utils/sdk-settings-schema.js.map +1 -1
  477. package/dist/utils/websocket-url.cjs +1 -1
  478. package/dist/utils/websocket-url.cjs.map +1 -1
  479. package/dist/utils/websocket-url.js +2 -2
  480. package/dist/utils/websocket-url.js.map +1 -1
  481. package/package.json +3 -5
  482. package/scripts/check-sdk-version-sync.mjs +9 -27
  483. package/scripts/dev-with-automation.mjs +1 -3
  484. package/scripts/docker-build.mjs +0 -4
  485. package/scripts/run-staged-typecheck.mjs +44 -0
  486. package/build/assets/QueryClientProvider-XcSjsfpb.js +0 -1
  487. package/build/assets/Trans-sy4l4TyQ.js +0 -1
  488. package/build/assets/active-backend-context-CNF2lPkA.js +0 -1
  489. package/build/assets/add-backend-modal-BywVr6uq.js +0 -1
  490. package/build/assets/agent-server-adapter-k8veH-1O.js +0 -5
  491. package/build/assets/agent-server-client-options-B-C8JaTb.js +0 -1
  492. package/build/assets/agent-server-conversation-service.api-CGbkhsLj.js +0 -1
  493. package/build/assets/api-key-entry-screen-Dnr5TcV0.js +0 -1
  494. package/build/assets/app-settings-Bgm2-yOM.js +0 -1
  495. package/build/assets/automation-detail-86M_yMqz.js +0 -1
  496. package/build/assets/automations-list-BWUILrqY.js +0 -1
  497. package/build/assets/backend-form-modal-Dl29wkF5.js +0 -1
  498. package/build/assets/browser-store-JRrcGdlk.js +0 -1
  499. package/build/assets/browser-wz8UzRmF.js +0 -5
  500. package/build/assets/chat-send-button-BhoO7T0s.js +0 -1
  501. package/build/assets/check-BhnsMsWy.js +0 -1
  502. package/build/assets/condenser-settings-DgQ0rsAf.js +0 -1
  503. package/build/assets/context-CI9FqkUp.js +0 -1
  504. package/build/assets/conversation-CayU5Lrr.js +0 -19
  505. package/build/assets/conversation-erinyBqT.js +0 -1
  506. package/build/assets/conversation-panel-DiI4q-8-.js +0 -1
  507. package/build/assets/conversation-service.api-shHWtFwu.js +0 -1
  508. package/build/assets/conversation-state-store-NEktJKfL.js +0 -1
  509. package/build/assets/declaration-D9ucT0fq.js +0 -1
  510. package/build/assets/dist-BCpEDEJq.js +0 -1
  511. package/build/assets/edit-automation-modal-8y9Uctxo.js +0 -1
  512. package/build/assets/entry.client-D_wtryHT.js +0 -2
  513. package/build/assets/files-tab-CxdtgkIe.js +0 -1
  514. package/build/assets/files-tab-store-DSKUhziI.js +0 -1
  515. package/build/assets/home-3joHMYSR.js +0 -1
  516. package/build/assets/index-redirect-BQ_55qN6.js +0 -1
  517. package/build/assets/install-server-modal-_7l1gcjd.js +0 -1
  518. package/build/assets/lesson-plan-CHGc-B8Y.js +0 -3
  519. package/build/assets/llm-not-configured-banner-BMGzpBPV.js +0 -27
  520. package/build/assets/llm-settings-BRotm_g6.js +0 -1
  521. package/build/assets/llm-settings-yDgbXP6Q.js +0 -1
  522. package/build/assets/loader-circle-1XB9nbwF.js +0 -1
  523. package/build/assets/manage-backends-modal-tfdzTXWU.js +0 -1
  524. package/build/assets/manifest-bbc1374e.js +0 -1
  525. package/build/assets/markdown-renderer-z446oW0Z.js +0 -2
  526. package/build/assets/mcp-Dopstf0t.js +0 -9
  527. package/build/assets/mcp-settings-redirect-l3Zbmc0F.js +0 -1
  528. package/build/assets/messages-CsFUrAen.js +0 -41
  529. package/build/assets/model-selector-sioC8xP4.js +0 -1
  530. package/build/assets/onboarding-CQh50i2z.js +0 -1
  531. package/build/assets/option-service.api-DjPvPAtd.js +0 -1
  532. package/build/assets/organization-service.api-BNc9Kn6n.js +0 -1
  533. package/build/assets/plus-DKUID4kd.js +0 -1
  534. package/build/assets/providers-BiWSYwRT.js +0 -1
  535. package/build/assets/proxy--Uy1iGsW.js +0 -1
  536. package/build/assets/query-client-config-Ca3tW1AH.js +0 -1
  537. package/build/assets/root-CRkz_h2T.css +0 -1
  538. package/build/assets/root-DrHpnAqU.js +0 -2
  539. package/build/assets/root-layout-Cf7FC9DC.js +0 -2
  540. package/build/assets/sdk-section-page-COlGKv4E.js +0 -1
  541. package/build/assets/sdk-settings-schema-Big50zDH.js +0 -1
  542. package/build/assets/secrets-settings-DYfTc5c8.js +0 -1
  543. package/build/assets/settings-DRmVFdOc.js +0 -1
  544. package/build/assets/settings-modal-CeRvS5Bc.js +0 -1
  545. package/build/assets/settings-service.api-CFt5HmXD.js +0 -1
  546. package/build/assets/shared-conversation-CBbpyPaD.js +0 -1
  547. package/build/assets/sidebar-mobile-menu-toggle-SKcwsEtD.js +0 -1
  548. package/build/assets/suspense-Bqb0V0q1.js +0 -1
  549. package/build/assets/telemetry-DCFAqpIA.js +0 -2
  550. package/build/assets/use-acp-credential-form-LjCIhMSK.js +0 -1
  551. package/build/assets/use-agent-settings-schema-D8x5LJVS.js +0 -1
  552. package/build/assets/use-config-5zEjywCL.js +0 -1
  553. package/build/assets/use-create-conversation-pV2eA-e9.js +0 -1
  554. package/build/assets/use-handle-plan-click-CwN_nNib.js +0 -1
  555. package/build/assets/use-is-authed-B8eLO-Xq.js +0 -1
  556. package/build/assets/use-llm-profiles-CWLfWjav.js +0 -1
  557. package/build/assets/use-runtime-is-ready-Bbory9-s.js +0 -1
  558. package/build/assets/use-save-settings-Xp8DD-jf.js +0 -1
  559. package/build/assets/use-settings-0SeqaDeW.js +0 -1
  560. package/build/assets/useMutation-Dm84WAKR.js +0 -1
  561. package/build/assets/useQuery-DGRSm7IT.js +0 -1
  562. package/build/assets/useTranslation-CL6FTf1b.js +0 -1
  563. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~dqseey1n-D_BSEDk4.js +0 -1
  564. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~jfbjdnyv-C5ZIbuq8.js +0 -177
  565. package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~mcp~settings~settings-index~~m3acswdn-CGm-Jxp5.js +0 -48
  566. package/build/assets/x-CCfDppg5.js +0 -1
  567. package/dist/api/settings-service/legacy-app-preferences-migration.cjs +0 -2
  568. package/dist/api/settings-service/legacy-app-preferences-migration.cjs.map +0 -1
  569. package/dist/api/settings-service/legacy-app-preferences-migration.d.ts +0 -34
  570. package/dist/api/settings-service/legacy-app-preferences-migration.js +0 -52
  571. package/dist/api/settings-service/legacy-app-preferences-migration.js.map +0 -1
  572. package/dist/hooks/query/use-openhands-verified-models.cjs +0 -2
  573. package/dist/hooks/query/use-openhands-verified-models.cjs.map +0 -1
  574. package/dist/hooks/query/use-openhands-verified-models.d.ts +0 -1
  575. package/dist/hooks/query/use-openhands-verified-models.js +0 -14
  576. package/dist/hooks/query/use-openhands-verified-models.js.map +0 -1
  577. package/dist/utils/normalize-display-model.cjs +0 -2
  578. package/dist/utils/normalize-display-model.cjs.map +0 -1
  579. package/dist/utils/normalize-display-model.d.ts +0 -9
  580. package/dist/utils/normalize-display-model.js +0 -13
  581. package/dist/utils/normalize-display-model.js.map +0 -1
  582. package/dist/utils/openhands-llm.cjs +0 -2
  583. package/dist/utils/openhands-llm.cjs.map +0 -1
  584. package/dist/utils/openhands-llm.d.ts +0 -15
  585. package/dist/utils/openhands-llm.js +0 -15
  586. package/dist/utils/openhands-llm.js.map +0 -1
  587. /package/build/assets/{agent-server-ui-style-scope-BwIZYdC1.js → agent-server-ui-style-scope-1MuaCC1k.js} +0 -0
  588. /package/build/assets/{common-Dh7OqABE.js → common-DqjLSBOt.js} +0 -0
  589. /package/build/assets/{environment-switch-store-CiurvTtK.js → environment-switch-store-CBXkf5bS.js} +0 -0
  590. /package/build/assets/{extension-module-card-classes-DP5J6ngE.js → extension-module-card-classes-CVGfXtoL.js} +0 -0
  591. /package/build/assets/{git-status-mapper-BI8FyUVp.js → git-status-mapper-DVFXF1_X.js} +0 -0
  592. /package/build/assets/{handle-capture-consent-CPMXU29K.js → handle-capture-consent-BTvatyfE.js} +0 -0
  593. /package/build/assets/{map-provider-BM2SJarp.js → map-provider-COgsZR9p.js} +0 -0
  594. /package/build/assets/{objectWithoutPropertiesLoose-DSQKyRhw.js → objectWithoutPropertiesLoose-B80nTzVc.js} +0 -0
  595. /package/build/assets/{preload-helper-CT1Z6Pdu.js → preload-helper-D_sPnjeL.js} +0 -0
  596. /package/build/assets/{rolldown-runtime-BFRubm34.js → rolldown-runtime-wFmyQBCi.js} +0 -0
  597. /package/build/assets/{sdk-settings-field-metadata-p2GVlft7.js → sdk-settings-field-metadata-Cc4D-ut7.js} +0 -0
  598. /package/build/assets/{settings-wh2sLLJM.js → settings-1287uR2J.js} +0 -0
  599. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~nh19ovoo-DJTFnsA1.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~cg1m3bv7-aUIHudsf.js} +0 -0
  600. /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ntycl9e1-BmZcy_kH.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ntycl9e1-DPPGSgT1.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"folder-browser-modal.js","names":[],"sources":["../../../../../src/components/features/home/workspace-dropdown/folder-browser-modal.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { BaseModalTitle } from \"#/components/shared/modals/confirmation-modals/base-modal\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport {\n MODAL_MAX_WIDTH_VIEWPORT,\n modalWidthClassName,\n} from \"#/components/shared/modals/modal-body\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { LocalWorkspace, LocalWorkspaceParent } from \"#/types/workspace\";\nimport {\n type HomeDirectoryResponse,\n useHomeDirectory,\n useSearchSubdirs,\n} from \"#/hooks/query/use-search-subdirs\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { cn } from \"#/utils/utils\";\nimport { modalTitleSmClassName } from \"#/utils/modal-classes\";\nimport FolderIcon from \"#/icons/folder.svg?react\";\nimport ChevronLeft from \"#/icons/chevron-left-small.svg?react\";\n\nconst PROJECTS_PATH = \"/projects\";\n\ninterface FolderBrowserModalProps {\n isOpen: boolean;\n onClose: () => void;\n onAdd: (items: LocalWorkspace[]) => void;\n onAddParent?: (items: LocalWorkspaceParent[]) => void;\n}\n\ninterface SidebarEntry {\n label: string;\n path: string;\n}\n\ninterface SidebarSectionProps {\n label: string;\n entries: SidebarEntry[];\n currentPath: string | null;\n onPick: (path: string) => void;\n}\n\nfunction SidebarSection({\n label,\n entries,\n currentPath,\n onPick,\n}: SidebarSectionProps) {\n if (entries.length === 0) return null;\n return (\n <div className=\"px-2 pb-3\">\n <div className=\"px-2 pb-1 text-[11px] uppercase tracking-wide text-[var(--oh-muted)] font-semibold\">\n {label}\n </div>\n <ul>\n {entries.map((entry) => {\n const isActive = currentPath === entry.path;\n return (\n <li key={entry.path}>\n <button\n type=\"button\"\n onClick={() => onPick(entry.path)}\n data-testid={`folder-browser-sidebar-${entry.label.toLowerCase()}`}\n className={cn(\n \"flex items-center gap-2 w-full px-2 py-1 rounded text-sm cursor-pointer\",\n isActive\n ? \"bg-tertiary text-white\"\n : \"text-[var(--oh-text-tertiary)] hover:bg-[var(--oh-surface-raised)]\",\n )}\n >\n <FolderIcon width={14} height={14} className=\"shrink-0\" />\n <span className=\"truncate\">{entry.label}</span>\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\nfunction getParentPath(path: string): string | null {\n const trimmed = path.replace(/\\/+$/, \"\");\n if (!trimmed || trimmed === \"/\") return null;\n const idx = trimmed.lastIndexOf(\"/\");\n if (idx <= 0) return \"/\";\n return trimmed.slice(0, idx);\n}\n\nfunction shouldDefaultToProjectsPath(\n homeData: HomeDirectoryResponse | undefined,\n): boolean {\n return homeData?.home === \"/home/openhands\";\n}\n\nexport function FolderBrowserModal({\n isOpen,\n onClose,\n onAdd,\n onAddParent,\n}: FolderBrowserModalProps) {\n const { t } = useTranslation(\"openhands\");\n const [currentPath, setCurrentPath] = useState<string | null>(null);\n const active = useActiveBackend();\n\n const { data: homeData } = useHomeDirectory();\n\n // Initialize / reset to home each time the modal is opened\n useEffect(() => {\n if (isOpen && homeData?.home && currentPath === null) {\n setCurrentPath(\n shouldDefaultToProjectsPath(homeData) ? PROJECTS_PATH : homeData.home,\n );\n }\n if (!isOpen) {\n setCurrentPath(null);\n }\n }, [isOpen, homeData?.home, currentPath]);\n\n // A backend switch invalidates the previous path — clear it so the\n // open/close effect can re-seed from the new backend's homeData.home.\n useEffect(() => {\n setCurrentPath(null);\n }, [active.backend.id, active.orgId]);\n\n const {\n data: listing,\n isLoading,\n isError,\n error,\n } = useSearchSubdirs(isOpen ? currentPath : null);\n\n const favorites: SidebarEntry[] = useMemo(() => {\n if (!homeData?.home) return [];\n const trimmed = homeData.home.replace(/[\\\\/]+$/, \"\") || homeData.home;\n const backendFavorites = [\n { label: \"Home\", path: trimmed },\n ...(homeData.favorites ?? []),\n ];\n if (\n shouldDefaultToProjectsPath(homeData) &&\n !backendFavorites.some((entry) => entry.path === PROJECTS_PATH)\n ) {\n backendFavorites.push({\n label: PROJECTS_PATH,\n path: PROJECTS_PATH,\n });\n }\n\n return backendFavorites;\n }, [homeData]);\n\n const locations: SidebarEntry[] = homeData?.locations ?? [];\n\n if (!isOpen) return null;\n\n const subdirs = listing?.items ?? [];\n const parent = currentPath ? getParentPath(currentPath) : null;\n\n // Signal that we're inside a container environment without the host\n // home mounted: the agent server reports `/home/openhands` as home and\n // returns no favorites (the only contents are hidden credential dirs).\n // In that case there's nothing useful for the user to browse, so we\n // surface a hint instead of the generic empty state.\n const showHostHomeHint =\n homeData?.home === \"/home/openhands\" &&\n (homeData?.favorites?.length ?? 0) === 0 &&\n currentPath === homeData?.home &&\n !isLoading &&\n !isError &&\n subdirs.length === 0;\n\n const getBasename = (path: string): string => {\n const trimmed = path.replace(/\\/+$/, \"\");\n if (!trimmed) return \"/\";\n const idx = trimmed.lastIndexOf(\"/\");\n return idx >= 0 ? trimmed.slice(idx + 1) || \"/\" : trimmed;\n };\n\n const handleAddDirectory = () => {\n if (!currentPath) return;\n const item: LocalWorkspace = {\n id: currentPath,\n name: getBasename(currentPath),\n path: currentPath,\n };\n onAdd([item]);\n onClose();\n };\n\n const handleAddAllSubdirectories = () => {\n if (!currentPath || !onAddParent) return;\n onAddParent([\n {\n id: currentPath,\n name: getBasename(currentPath),\n path: currentPath,\n },\n ]);\n onClose();\n };\n\n return (\n <ModalBackdrop\n onClose={onClose}\n aria-label={t(I18nKey.HOME$ADD_WORKSPACES_TITLE)}\n >\n <div\n data-testid=\"folder-browser-modal\"\n className={cn(\n \"flex flex-col bg-[var(--oh-surface)] border border-[var(--oh-border-input)] rounded-xl\",\n modalWidthClassName(\"xl\"),\n MODAL_MAX_WIDTH_VIEWPORT,\n \"h-[480px]\",\n )}\n >\n {/* Title bar */}\n <div className=\"flex items-center justify-between px-5 py-3 border-b border-[var(--oh-border-input)]\">\n <BaseModalTitle\n className={modalTitleSmClassName}\n title={t(I18nKey.HOME$ADD_WORKSPACES_TITLE)}\n />\n </div>\n\n {/* Body: sidebar + main */}\n <div className=\"flex flex-1 min-h-0\">\n {/* Sidebar */}\n <aside\n data-testid=\"folder-browser-sidebar\"\n className=\"w-[180px] shrink-0 border-r border-[var(--oh-border-input)] bg-[var(--oh-surface)] py-3 overflow-y-auto\"\n >\n <SidebarSection\n label={t(I18nKey.HOME$FAVORITES)}\n entries={favorites}\n currentPath={currentPath}\n onPick={setCurrentPath}\n />\n <SidebarSection\n label={t(I18nKey.HOME$LOCATIONS)}\n entries={locations}\n currentPath={currentPath}\n onPick={setCurrentPath}\n />\n </aside>\n\n {/* Main */}\n <div className=\"flex-1 flex flex-col min-w-0\">\n {/* Nav row */}\n <div className=\"flex items-center gap-2 px-4 py-2 border-b border-[var(--oh-border-input)]\">\n <button\n type=\"button\"\n data-testid=\"folder-browser-up\"\n onClick={() => parent && setCurrentPath(parent)}\n disabled={!parent}\n aria-label=\"Up\"\n className=\"p-1 rounded hover:bg-[var(--oh-interactive-hover)] text-white disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer\"\n >\n <ChevronLeft width={16} height={16} />\n </button>\n <span\n className=\"text-xs text-[var(--oh-muted)] truncate\"\n data-testid=\"folder-browser-current-path\"\n >\n {currentPath ?? \"\"}\n </span>\n </div>\n\n {/* Column headers */}\n <div className=\"grid grid-cols-[1fr_120px] px-4 py-1 border-b border-[var(--oh-border-input)] text-xs text-[var(--oh-text-secondary)] font-semibold\">\n <span>{t(I18nKey.HOME$NAME)}</span>\n <span>{t(I18nKey.HOME$KIND)}</span>\n </div>\n\n {/* List */}\n <ul\n className=\"flex-1 overflow-auto custom-scrollbar-always\"\n data-testid=\"folder-browser-list\"\n >\n {isLoading && (\n <li className=\"px-4 py-2 text-sm text-[var(--oh-text-secondary)]\">\n {t(I18nKey.HOME$LOADING)}\n </li>\n )}\n {isError && (\n <li\n className=\"px-4 py-2 text-sm text-red-400\"\n data-testid=\"folder-browser-error\"\n >\n {(error as Error | undefined)?.message ?? \"Failed to load\"}\n </li>\n )}\n {!isLoading && !isError && subdirs.length === 0 && (\n <li\n className=\"px-4 py-2 text-sm text-[var(--oh-text-secondary)]\"\n data-testid={\n showHostHomeHint\n ? \"folder-browser-host-home-hint\"\n : \"folder-browser-empty\"\n }\n >\n {showHostHomeHint\n ? t(I18nKey.HOME$HOST_HOME_NOT_MOUNTED_HINT)\n : t(I18nKey.HOME$NO_WORKSPACES)}\n </li>\n )}\n {subdirs.map((entry) => (\n <li key={entry.path}>\n <button\n type=\"button\"\n onClick={() => setCurrentPath(entry.path)}\n className=\"grid grid-cols-[1fr_120px] items-center w-full text-left px-4 py-1.5 text-sm text-white hover:bg-[var(--oh-interactive-hover)] cursor-pointer\"\n data-testid={`folder-browser-entry-${entry.name}`}\n >\n <span className=\"flex items-center gap-2 min-w-0\">\n <FolderIcon width={16} height={16} className=\"shrink-0\" />\n <span className=\"truncate\">{entry.name}</span>\n </span>\n <span className=\"text-[var(--oh-text-secondary)] text-xs\">\n {t(I18nKey.HOME$FOLDER)}\n </span>\n </button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-2 px-5 py-3 border-t border-[var(--oh-border-input)]\">\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={onClose}\n testId=\"folder-browser-cancel\"\n >\n {t(I18nKey.HOME$CANCEL)}\n </BrandButton>\n {onAddParent && (\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={handleAddAllSubdirectories}\n isDisabled={!currentPath || isLoading}\n testId=\"folder-browser-add-all-subdirs\"\n >\n {t(I18nKey.HOME$ADD_ALL_SUBDIRECTORIES)}\n </BrandButton>\n )}\n <BrandButton\n type=\"button\"\n variant=\"primary\"\n onClick={handleAddDirectory}\n isDisabled={!currentPath || isLoading}\n testId=\"folder-browser-use\"\n >\n {t(I18nKey.HOME$ADD_THIS_DIRECTORY)}\n </BrandButton>\n </div>\n </div>\n </ModalBackdrop>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"folder-browser-modal.js","names":[],"sources":["../../../../../src/components/features/home/workspace-dropdown/folder-browser-modal.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { BaseModalTitle } from \"#/components/shared/modals/confirmation-modals/base-modal\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport {\n MODAL_MAX_WIDTH_VIEWPORT,\n modalWidthClassName,\n} from \"#/components/shared/modals/modal-body\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { LocalWorkspace, LocalWorkspaceParent } from \"#/types/workspace\";\nimport {\n type HomeDirectoryResponse,\n useHomeDirectory,\n useSearchSubdirs,\n} from \"#/hooks/query/use-search-subdirs\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { cn } from \"#/utils/utils\";\nimport { modalTitleSmClassName } from \"#/utils/modal-classes\";\nimport FolderIcon from \"#/icons/folder.svg?react\";\nimport ChevronLeft from \"#/icons/chevron-left-small.svg?react\";\n\nconst PROJECTS_PATH = \"/projects\";\n\ninterface FolderBrowserModalProps {\n isOpen: boolean;\n onClose: () => void;\n onAdd: (items: LocalWorkspace[]) => void;\n onAddParent?: (items: LocalWorkspaceParent[]) => void;\n}\n\ninterface SidebarEntry {\n label: string;\n path: string;\n}\n\ninterface SidebarSectionProps {\n label: string;\n entries: SidebarEntry[];\n currentPath: string | null;\n onPick: (path: string) => void;\n}\n\nfunction SidebarSection({\n label,\n entries,\n currentPath,\n onPick,\n}: SidebarSectionProps) {\n if (entries.length === 0) return null;\n return (\n <div className=\"px-2 pb-3\">\n <div className=\"px-2 pb-1 text-[11px] uppercase tracking-wide text-[var(--oh-muted)] font-semibold\">\n {label}\n </div>\n <ul>\n {entries.map((entry) => {\n const isActive = currentPath === entry.path;\n return (\n <li key={entry.path}>\n <button\n type=\"button\"\n onClick={() => onPick(entry.path)}\n data-testid={`folder-browser-sidebar-${entry.label.toLowerCase()}`}\n className={cn(\n \"flex items-center gap-2 w-full px-2 py-1 rounded text-sm cursor-pointer\",\n isActive\n ? \"bg-tertiary text-white\"\n : \"text-[var(--oh-text-tertiary)] hover:bg-[var(--oh-surface-raised)]\",\n )}\n >\n <FolderIcon width={14} height={14} className=\"shrink-0\" />\n <span className=\"truncate\">{entry.label}</span>\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\nfunction getParentPath(path: string): string | null {\n const trimmed = trimTrailingSeparators(path);\n if (!trimmed || trimmed === \"/\" || isWindowsDriveRoot(trimmed)) return null;\n\n const idx = Math.max(trimmed.lastIndexOf(\"/\"), trimmed.lastIndexOf(\"\\\\\"));\n if (idx < 0) return null;\n if (idx === 0) return \"/\";\n\n const parent = trimmed.slice(0, idx);\n if (/^[A-Za-z]:$/.test(parent)) {\n return `${parent}${trimmed[idx]}`;\n }\n\n return parent;\n}\n\nfunction isWindowsDriveRoot(path: string): boolean {\n return /^[A-Za-z]:[\\\\/]?$/.test(path);\n}\n\nfunction trimTrailingSeparators(path: string): string {\n const trimmed = path.replace(/[\\\\/]+$/, \"\");\n if (/^[A-Za-z]:$/.test(trimmed)) {\n const separator = path.includes(\"/\") && !path.includes(\"\\\\\") ? \"/\" : \"\\\\\";\n return `${trimmed}${separator}`;\n }\n return trimmed;\n}\n\nfunction shouldDefaultToProjectsPath(\n homeData: HomeDirectoryResponse | undefined,\n): boolean {\n return homeData?.home === \"/home/openhands\";\n}\n\nexport function FolderBrowserModal({\n isOpen,\n onClose,\n onAdd,\n onAddParent,\n}: FolderBrowserModalProps) {\n const { t } = useTranslation(\"openhands\");\n const [currentPath, setCurrentPath] = useState<string | null>(null);\n const active = useActiveBackend();\n\n const { data: homeData } = useHomeDirectory();\n\n // Initialize / reset to home each time the modal is opened\n useEffect(() => {\n if (isOpen && homeData?.home && currentPath === null) {\n setCurrentPath(\n shouldDefaultToProjectsPath(homeData) ? PROJECTS_PATH : homeData.home,\n );\n }\n if (!isOpen) {\n setCurrentPath(null);\n }\n }, [isOpen, homeData?.home, currentPath]);\n\n // A backend switch invalidates the previous path — clear it so the\n // open/close effect can re-seed from the new backend's homeData.home.\n useEffect(() => {\n setCurrentPath(null);\n }, [active.backend.id, active.orgId]);\n\n const {\n data: listing,\n isLoading,\n isError,\n error,\n } = useSearchSubdirs(isOpen ? currentPath : null);\n\n const favorites: SidebarEntry[] = useMemo(() => {\n if (!homeData?.home) return [];\n const trimmed = trimTrailingSeparators(homeData.home) || homeData.home;\n const backendFavorites = [\n { label: \"Home\", path: trimmed },\n ...(homeData.favorites ?? []),\n ];\n if (\n shouldDefaultToProjectsPath(homeData) &&\n !backendFavorites.some((entry) => entry.path === PROJECTS_PATH)\n ) {\n backendFavorites.push({\n label: PROJECTS_PATH,\n path: PROJECTS_PATH,\n });\n }\n\n return backendFavorites;\n }, [homeData]);\n\n const locations: SidebarEntry[] = homeData?.locations ?? [];\n\n if (!isOpen) return null;\n\n const subdirs = listing?.items ?? [];\n const parent = currentPath ? getParentPath(currentPath) : null;\n\n // Signal that we're inside a container environment without the host\n // home mounted: the agent server reports `/home/openhands` as home and\n // returns no favorites (the only contents are hidden credential dirs).\n // In that case there's nothing useful for the user to browse, so we\n // surface a hint instead of the generic empty state.\n const showHostHomeHint =\n homeData?.home === \"/home/openhands\" &&\n (homeData?.favorites?.length ?? 0) === 0 &&\n currentPath === homeData?.home &&\n !isLoading &&\n !isError &&\n subdirs.length === 0;\n\n const getBasename = (path: string): string => {\n const trimmed = trimTrailingSeparators(path);\n if (!trimmed) return \"/\";\n if (trimmed === \"/\" || isWindowsDriveRoot(trimmed)) return trimmed;\n const idx = Math.max(trimmed.lastIndexOf(\"/\"), trimmed.lastIndexOf(\"\\\\\"));\n return idx >= 0 ? trimmed.slice(idx + 1) || trimmed : trimmed;\n };\n\n const handleAddDirectory = () => {\n if (!currentPath) return;\n const item: LocalWorkspace = {\n id: currentPath,\n name: getBasename(currentPath),\n path: currentPath,\n };\n onAdd([item]);\n onClose();\n };\n\n const handleAddAllSubdirectories = () => {\n if (!currentPath || !onAddParent) return;\n onAddParent([\n {\n id: currentPath,\n name: getBasename(currentPath),\n path: currentPath,\n },\n ]);\n onClose();\n };\n\n return (\n <ModalBackdrop\n onClose={onClose}\n aria-label={t(I18nKey.HOME$ADD_WORKSPACES_TITLE)}\n >\n <div\n data-testid=\"folder-browser-modal\"\n className={cn(\n \"flex flex-col bg-[var(--oh-surface)] border border-[var(--oh-border-input)] rounded-xl\",\n modalWidthClassName(\"xl\"),\n MODAL_MAX_WIDTH_VIEWPORT,\n \"h-[480px]\",\n )}\n >\n {/* Title bar */}\n <div className=\"flex items-center justify-between px-5 py-3 border-b border-[var(--oh-border-input)]\">\n <BaseModalTitle\n className={modalTitleSmClassName}\n title={t(I18nKey.HOME$ADD_WORKSPACES_TITLE)}\n />\n </div>\n\n {/* Body: sidebar + main */}\n <div className=\"flex flex-1 min-h-0\">\n {/* Sidebar */}\n <aside\n data-testid=\"folder-browser-sidebar\"\n className=\"w-[180px] shrink-0 border-r border-[var(--oh-border-input)] bg-[var(--oh-surface)] py-3 overflow-y-auto\"\n >\n <SidebarSection\n label={t(I18nKey.HOME$FAVORITES)}\n entries={favorites}\n currentPath={currentPath}\n onPick={setCurrentPath}\n />\n <SidebarSection\n label={t(I18nKey.HOME$LOCATIONS)}\n entries={locations}\n currentPath={currentPath}\n onPick={setCurrentPath}\n />\n </aside>\n\n {/* Main */}\n <div className=\"flex-1 flex flex-col min-w-0\">\n {/* Nav row */}\n <div className=\"flex items-center gap-2 px-4 py-2 border-b border-[var(--oh-border-input)]\">\n <button\n type=\"button\"\n data-testid=\"folder-browser-up\"\n onClick={() => parent && setCurrentPath(parent)}\n disabled={!parent}\n aria-label=\"Up\"\n className=\"p-1 rounded hover:bg-[var(--oh-interactive-hover)] text-white disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer\"\n >\n <ChevronLeft width={16} height={16} />\n </button>\n <span\n className=\"text-xs text-[var(--oh-muted)] truncate\"\n data-testid=\"folder-browser-current-path\"\n >\n {currentPath ?? \"\"}\n </span>\n </div>\n\n {/* Column headers */}\n <div className=\"grid grid-cols-[1fr_120px] px-4 py-1 border-b border-[var(--oh-border-input)] text-xs text-[var(--oh-text-secondary)] font-semibold\">\n <span>{t(I18nKey.HOME$NAME)}</span>\n <span>{t(I18nKey.HOME$KIND)}</span>\n </div>\n\n {/* List */}\n <ul\n className=\"flex-1 overflow-auto custom-scrollbar-always\"\n data-testid=\"folder-browser-list\"\n >\n {isLoading && (\n <li className=\"px-4 py-2 text-sm text-[var(--oh-text-secondary)]\">\n {t(I18nKey.HOME$LOADING)}\n </li>\n )}\n {isError && (\n <li\n className=\"px-4 py-2 text-sm text-red-400\"\n data-testid=\"folder-browser-error\"\n >\n {(error as Error | undefined)?.message ?? \"Failed to load\"}\n </li>\n )}\n {!isLoading && !isError && subdirs.length === 0 && (\n <li\n className=\"px-4 py-2 text-sm text-[var(--oh-text-secondary)]\"\n data-testid={\n showHostHomeHint\n ? \"folder-browser-host-home-hint\"\n : \"folder-browser-empty\"\n }\n >\n {showHostHomeHint\n ? t(I18nKey.HOME$HOST_HOME_NOT_MOUNTED_HINT)\n : t(I18nKey.HOME$NO_WORKSPACES)}\n </li>\n )}\n {subdirs.map((entry) => (\n <li key={entry.path}>\n <button\n type=\"button\"\n onClick={() => setCurrentPath(entry.path)}\n className=\"grid grid-cols-[1fr_120px] items-center w-full text-left px-4 py-1.5 text-sm text-white hover:bg-[var(--oh-interactive-hover)] cursor-pointer\"\n data-testid={`folder-browser-entry-${entry.name}`}\n >\n <span className=\"flex items-center gap-2 min-w-0\">\n <FolderIcon width={16} height={16} className=\"shrink-0\" />\n <span className=\"truncate\">{entry.name}</span>\n </span>\n <span className=\"text-[var(--oh-text-secondary)] text-xs\">\n {t(I18nKey.HOME$FOLDER)}\n </span>\n </button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-2 px-5 py-3 border-t border-[var(--oh-border-input)]\">\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={onClose}\n testId=\"folder-browser-cancel\"\n >\n {t(I18nKey.HOME$CANCEL)}\n </BrandButton>\n {onAddParent && (\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={handleAddAllSubdirectories}\n isDisabled={!currentPath || isLoading}\n testId=\"folder-browser-add-all-subdirs\"\n >\n {t(I18nKey.HOME$ADD_ALL_SUBDIRECTORIES)}\n </BrandButton>\n )}\n <BrandButton\n type=\"button\"\n variant=\"primary\"\n onClick={handleAddDirectory}\n isDisabled={!currentPath || isLoading}\n testId=\"folder-browser-use\"\n >\n {t(I18nKey.HOME$ADD_THIS_DIRECTORY)}\n </BrandButton>\n </div>\n </div>\n </ModalBackdrop>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../utils/utils.cjs`),i=require(`../../../utils/retrieve-axios-error-message.cjs`),a=require(`../../../utils/custom-toast-handlers.cjs`),o=require(`../../../contexts/active-backend-context.cjs`),s=require(`../../shared/modals/modal-backdrop.cjs`),c=require(`../../../utils/modal-classes.cjs`),l=require(`../../shared/modals/modal-close-button.cjs`),u=require(`../../shared/modals/confirmation-modal.cjs`),d=require(`../../../hooks/mutation/use-delete-mcp-server.cjs`),f=require(`../../../hooks/mutation/use-add-mcp-server.cjs`),p=require(`../../../hooks/mutation/use-test-mcp-server.cjs`),m=require(`../settings/mcp-settings/mcp-server-form.cjs`),h=require(`../../../hooks/mutation/use-update-mcp-server.cjs`);let g=require(`react`);g=e.__toESM(g,1);let _=require(`react/jsx-runtime`);function v({server:e,existingServers:v,onClose:y}){let{t:b}=t.useTranslation(`openhands`),{mutate:x,isPending:S}=f.useAddMcpServer(),{mutate:C,isPending:w}=h.useUpdateMcpServer(),{mutate:T,isPending:E}=d.useDeleteMcpServer(),{mutate:D,isPending:O,data:k,reset:A}=p.useTestMcpServer(),[j,M]=g.default.useState(!1),{backend:N}=o.useActiveBackend(),P=N.kind===`cloud`,F=!!e.id,I=S||w||E,L=I||O||j,R=e=>{switch(e.error_kind){case`timeout`:return b(n.I18nKey.MCP$TEST_ERROR_TIMEOUT);case`connection`:return b(n.I18nKey.MCP$TEST_ERROR_CONNECTION);default:return b(n.I18nKey.MCP$TEST_ERROR_UNKNOWN,{error:e.error})}},z=g.default.useMemo(()=>k?k.ok?{ok:!0,text:b(n.I18nKey.MCP$TEST_SUCCESS,{count:k.tools.length})}:{ok:!1,text:R(k)}:null,[k,b]),B=e=>{a.displayErrorToast(i.retrieveAxiosErrorMessage(e)||b(n.I18nKey.ERROR$GENERIC))};return(0,_.jsxs)(_.Fragment,{children:[(0,_.jsx)(s.ModalBackdrop,{onClose:L?void 0:y,closeOnEscape:!L,"aria-label":b(F?n.I18nKey.MCP$EDIT_CUSTOM_TITLE:n.I18nKey.MCP$ADD_CUSTOM_TITLE),children:(0,_.jsxs)(`div`,{"data-testid":`mcp-custom-editor`,className:`relative bg-base-secondary p-6 rounded-xl border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[90vh] overflow-y-auto custom-scrollbar`,children:[(0,_.jsx)(l.ModalCloseButton,{onClose:y,testId:`mcp-custom-editor-close`,disabled:L}),(0,_.jsx)(`h2`,{className:r.cn(`mb-4 pr-6`,c.modalTitleLgClassName),children:b(F?n.I18nKey.MCP$EDIT_CUSTOM_TITLE:n.I18nKey.MCP$ADD_CUSTOM_TITLE)}),(0,_.jsx)(m.MCPServerForm,{mode:F?`edit`:`add`,server:F?e:void 0,existingServers:v,onSubmit:t=>{A(),D(t,{onSuccess:n=>{n.ok&&(F?C({serverId:e.id,server:t},{onSuccess:y,onError:B}):x(t,{onSuccess:y,onError:B}))},onError:B})},onCancel:y,onDelete:F?()=>M(!0):void 0,isActionDisabled:I,onTest:P?void 0:e=>{D(e)},isTestPending:O,testMessage:P?null:z})]})}),j?(0,_.jsx)(u.ConfirmationModal,{text:b(n.I18nKey.SETTINGS$MCP_CONFIRM_DELETE),onCancel:()=>M(!1),onConfirm:()=>{T(e,{onSuccess:()=>{a.displaySuccessToast(b(n.I18nKey.MCP$REMOVE_SUCCESS)),M(!1),y()},onError:e=>{B(e),M(!1)}})},isConfirming:E}):null]})}exports.CustomServerEditor=v;
1
+ const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../utils/utils.cjs`),i=require(`../../../utils/retrieve-axios-error-message.cjs`),a=require(`../../../utils/custom-toast-handlers.cjs`),o=require(`../../../contexts/active-backend-context.cjs`),s=require(`../../shared/modals/modal-backdrop.cjs`),c=require(`../../../utils/modal-classes.cjs`),l=require(`../../shared/modals/modal-close-button.cjs`),u=require(`../../shared/modals/confirmation-modal.cjs`),d=require(`../../../hooks/mutation/use-delete-mcp-server.cjs`),f=require(`../../../hooks/mutation/use-add-mcp-server.cjs`),p=require(`../../../hooks/mutation/use-test-mcp-server.cjs`),m=require(`../settings/mcp-settings/mcp-server-form.cjs`),h=require(`../../../hooks/mutation/use-update-mcp-server.cjs`);let g=require(`react`);g=e.__toESM(g,1);let _=require(`react/jsx-runtime`);function v({server:e,existingServers:v,onClose:y}){let{t:b}=t.useTranslation(`openhands`),{mutate:x,isPending:S}=f.useAddMcpServer(),{mutate:C,isPending:w}=h.useUpdateMcpServer(),{mutate:T,isPending:E}=d.useDeleteMcpServer(),{mutate:D,isPending:O,data:k,reset:A}=p.useTestMcpServer(),[j,M]=g.default.useState(!1),{backend:N}=o.useActiveBackend(),P=N.kind===`cloud`,F=!!e.id,I=S||w||E,L=I||O||j,R=e=>{switch(e.error_kind){case`timeout`:return b(n.I18nKey.MCP$TEST_ERROR_TIMEOUT);case`connection`:return b(n.I18nKey.MCP$TEST_ERROR_CONNECTION);case`credentials`:return b(n.I18nKey.MCP$TEST_ERROR_CREDENTIALS,{error:e.error});default:return b(n.I18nKey.MCP$TEST_ERROR_UNKNOWN,{error:e.error})}},z=g.default.useMemo(()=>k?k.ok?{ok:!0,text:b(n.I18nKey.MCP$TEST_SUCCESS,{count:k.tools.length})}:{ok:!1,text:R(k)}:null,[k,b]),B=e=>{a.displayErrorToast(i.retrieveAxiosErrorMessage(e)||b(n.I18nKey.ERROR$GENERIC))};return(0,_.jsxs)(_.Fragment,{children:[(0,_.jsx)(s.ModalBackdrop,{onClose:L?void 0:y,closeOnEscape:!L,"aria-label":b(F?n.I18nKey.MCP$EDIT_CUSTOM_TITLE:n.I18nKey.MCP$ADD_CUSTOM_TITLE),children:(0,_.jsxs)(`div`,{"data-testid":`mcp-custom-editor`,className:`relative bg-base-secondary p-6 rounded-xl border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[90vh] overflow-y-auto custom-scrollbar`,children:[(0,_.jsx)(l.ModalCloseButton,{onClose:y,testId:`mcp-custom-editor-close`,disabled:L}),(0,_.jsx)(`h2`,{className:r.cn(`mb-4 pr-6`,c.modalTitleLgClassName),children:b(F?n.I18nKey.MCP$EDIT_CUSTOM_TITLE:n.I18nKey.MCP$ADD_CUSTOM_TITLE)}),(0,_.jsx)(m.MCPServerForm,{mode:F?`edit`:`add`,server:F?e:void 0,existingServers:v,onSubmit:t=>{A(),D(t,{onSuccess:n=>{n.ok&&(F?C({serverId:e.id,server:t},{onSuccess:y,onError:B}):x(t,{onSuccess:y,onError:B}))},onError:B})},onCancel:y,onDelete:F?()=>M(!0):void 0,isActionDisabled:I,onTest:P?void 0:e=>{D(e)},isTestPending:O,testMessage:P?null:z})]})}),j?(0,_.jsx)(u.ConfirmationModal,{text:b(n.I18nKey.SETTINGS$MCP_CONFIRM_DELETE),onCancel:()=>M(!1),onConfirm:()=>{T(e,{onSuccess:()=>{a.displaySuccessToast(b(n.I18nKey.MCP$REMOVE_SUCCESS)),M(!1),y()},onError:e=>{B(e),M(!1)}})},isConfirming:E}):null]})}exports.CustomServerEditor=v;
2
2
  //# sourceMappingURL=custom-server-editor.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"custom-server-editor.cjs","names":[],"sources":["../../../../src/components/features/mcp-page/custom-server-editor.tsx"],"sourcesContent":["import React from \"react\";\nimport { AxiosError } from \"axios\";\nimport { useTranslation } from \"react-i18next\";\nimport type { MCPTestFailure } from \"@openhands/typescript-client\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalCloseButton } from \"#/components/shared/modals/modal-close-button\";\nimport { ConfirmationModal } from \"#/components/shared/modals/confirmation-modal\";\nimport {\n MCPServerForm,\n type TestMessage,\n} from \"#/components/features/settings/mcp-settings/mcp-server-form\";\nimport { useAddMcpServer } from \"#/hooks/mutation/use-add-mcp-server\";\nimport { useUpdateMcpServer } from \"#/hooks/mutation/use-update-mcp-server\";\nimport { useDeleteMcpServer } from \"#/hooks/mutation/use-delete-mcp-server\";\nimport { useTestMcpServer } from \"#/hooks/mutation/use-test-mcp-server\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { MCPServerConfig } from \"#/types/mcp-server\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { cn } from \"#/utils/utils\";\nimport { modalTitleLgClassName } from \"#/utils/modal-classes\";\n\ninterface CustomServerEditorProps {\n server: MCPServerConfig;\n existingServers: MCPServerConfig[];\n onClose: () => void;\n}\n\n/**\n * Modal wrapper around `MCPServerForm` so users can hand-author\n * arbitrary stdio / SSE / SHTTP entries without reaching for raw JSON.\n * An empty `server.id` means \"Add new\".\n */\nexport function CustomServerEditor({\n server,\n existingServers,\n onClose,\n}: CustomServerEditorProps) {\n const { t } = useTranslation(\"openhands\");\n const { mutate: addMcpServer, isPending: isAdding } = useAddMcpServer();\n const { mutate: updateMcpServer, isPending: isUpdating } =\n useUpdateMcpServer();\n const { mutate: deleteMcpServer, isPending: isDeleting } =\n useDeleteMcpServer();\n const {\n mutate: testServer,\n isPending: isTesting,\n data: testResult,\n reset: resetTest,\n } = useTestMcpServer();\n const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);\n\n // The MCP connectivity-test endpoint only exists on the local agent-server.\n // For cloud backends `McpService.testServer` short-circuits with a synthetic\n // success so the save still completes; we hide the manual \"Test connection\"\n // button here so cloud users aren't shown a misleading \"0 tools\" result.\n const { backend } = useActiveBackend();\n const isCloudBackend = backend.kind === \"cloud\";\n\n const isEditing = !!server.id;\n const isPending = isAdding || isUpdating || isDeleting;\n const isDismissBlocked = isPending || isTesting || showDeleteConfirm;\n\n const makeTestErrorMessage = (failure: MCPTestFailure): string => {\n switch (failure.error_kind) {\n case \"timeout\":\n return t(I18nKey.MCP$TEST_ERROR_TIMEOUT);\n case \"connection\":\n return t(I18nKey.MCP$TEST_ERROR_CONNECTION);\n default:\n return t(I18nKey.MCP$TEST_ERROR_UNKNOWN, { error: failure.error });\n }\n };\n\n const testMessage: TestMessage | null = React.useMemo(() => {\n if (!testResult) return null;\n if (testResult.ok) {\n return {\n ok: true,\n text: t(I18nKey.MCP$TEST_SUCCESS, { count: testResult.tools.length }),\n };\n }\n return { ok: false, text: makeTestErrorMessage(testResult) };\n }, [testResult, t]);\n\n // Shared error handler so both add and update surface backend errors\n // as a toast instead of failing silently — previously these calls\n // had no `onError` and the modal closed even on a 4xx/5xx, leaving\n // the user to discover the failure on the next page load.\n const handleError = (err: unknown) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n displayErrorToast(message || t(I18nKey.ERROR$GENERIC));\n };\n\n const handleSubmit = (payload: MCPServerConfig) => {\n resetTest();\n testServer(payload, {\n onSuccess: (result) => {\n if (!result.ok) {\n // Test failed — modal stays open, error shown via testMessage.\n return;\n }\n if (isEditing) {\n updateMcpServer(\n { serverId: server.id, server: payload },\n { onSuccess: onClose, onError: handleError },\n );\n } else {\n addMcpServer(payload, { onSuccess: onClose, onError: handleError });\n }\n },\n onError: handleError,\n });\n };\n\n const handleTestClick = (payload: MCPServerConfig) => {\n testServer(payload);\n };\n\n const handleConfirmDelete = () => {\n deleteMcpServer(server, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$REMOVE_SUCCESS));\n setShowDeleteConfirm(false);\n onClose();\n },\n onError: (err) => {\n handleError(err);\n setShowDeleteConfirm(false);\n },\n });\n };\n\n return (\n <>\n <ModalBackdrop\n // Block backdrop-click / Escape from dismissing the modal while\n // a mutation is in flight — closing mid-request would orphan\n // the request and leave the user with no error feedback.\n onClose={isDismissBlocked ? undefined : onClose}\n closeOnEscape={!isDismissBlocked}\n aria-label={\n isEditing\n ? t(I18nKey.MCP$EDIT_CUSTOM_TITLE)\n : t(I18nKey.MCP$ADD_CUSTOM_TITLE)\n }\n >\n <div\n data-testid=\"mcp-custom-editor\"\n className=\"relative bg-base-secondary p-6 rounded-xl border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[90vh] overflow-y-auto custom-scrollbar\"\n >\n <ModalCloseButton\n onClose={onClose}\n testId=\"mcp-custom-editor-close\"\n disabled={isDismissBlocked}\n />\n <h2 className={cn(\"mb-4 pr-6\", modalTitleLgClassName)}>\n {isEditing\n ? t(I18nKey.MCP$EDIT_CUSTOM_TITLE)\n : t(I18nKey.MCP$ADD_CUSTOM_TITLE)}\n </h2>\n <MCPServerForm\n mode={isEditing ? \"edit\" : \"add\"}\n server={isEditing ? server : undefined}\n existingServers={existingServers}\n onSubmit={handleSubmit}\n onCancel={onClose}\n onDelete={isEditing ? () => setShowDeleteConfirm(true) : undefined}\n isActionDisabled={isPending}\n onTest={isCloudBackend ? undefined : handleTestClick}\n isTestPending={isTesting}\n testMessage={isCloudBackend ? null : testMessage}\n />\n </div>\n </ModalBackdrop>\n\n {showDeleteConfirm ? (\n <ConfirmationModal\n text={t(I18nKey.SETTINGS$MCP_CONFIRM_DELETE)}\n onCancel={() => setShowDeleteConfirm(false)}\n onConfirm={handleConfirmDelete}\n isConfirming={isDeleting}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"custom-server-editor.cjs","names":[],"sources":["../../../../src/components/features/mcp-page/custom-server-editor.tsx"],"sourcesContent":["import React from \"react\";\nimport { AxiosError } from \"axios\";\nimport { useTranslation } from \"react-i18next\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalCloseButton } from \"#/components/shared/modals/modal-close-button\";\nimport { ConfirmationModal } from \"#/components/shared/modals/confirmation-modal\";\nimport {\n MCPServerForm,\n type TestMessage,\n} from \"#/components/features/settings/mcp-settings/mcp-server-form\";\nimport { useAddMcpServer } from \"#/hooks/mutation/use-add-mcp-server\";\nimport { useUpdateMcpServer } from \"#/hooks/mutation/use-update-mcp-server\";\nimport { useDeleteMcpServer } from \"#/hooks/mutation/use-delete-mcp-server\";\nimport { useTestMcpServer } from \"#/hooks/mutation/use-test-mcp-server\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { ExtendedMCPTestFailure, MCPServerConfig } from \"#/types/mcp-server\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { cn } from \"#/utils/utils\";\nimport { modalTitleLgClassName } from \"#/utils/modal-classes\";\n\ninterface CustomServerEditorProps {\n server: MCPServerConfig;\n existingServers: MCPServerConfig[];\n onClose: () => void;\n}\n\n/**\n * Modal wrapper around `MCPServerForm` so users can hand-author\n * arbitrary stdio / SSE / SHTTP entries without reaching for raw JSON.\n * An empty `server.id` means \"Add new\".\n */\nexport function CustomServerEditor({\n server,\n existingServers,\n onClose,\n}: CustomServerEditorProps) {\n const { t } = useTranslation(\"openhands\");\n const { mutate: addMcpServer, isPending: isAdding } = useAddMcpServer();\n const { mutate: updateMcpServer, isPending: isUpdating } =\n useUpdateMcpServer();\n const { mutate: deleteMcpServer, isPending: isDeleting } =\n useDeleteMcpServer();\n const {\n mutate: testServer,\n isPending: isTesting,\n data: testResult,\n reset: resetTest,\n } = useTestMcpServer();\n const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);\n\n // The MCP connectivity-test endpoint only exists on the local agent-server.\n // For cloud backends `McpService.testServer` short-circuits with a synthetic\n // success so the save still completes; we hide the manual \"Test connection\"\n // button here so cloud users aren't shown a misleading \"0 tools\" result.\n const { backend } = useActiveBackend();\n const isCloudBackend = backend.kind === \"cloud\";\n\n const isEditing = !!server.id;\n const isPending = isAdding || isUpdating || isDeleting;\n const isDismissBlocked = isPending || isTesting || showDeleteConfirm;\n\n const makeTestErrorMessage = (failure: ExtendedMCPTestFailure): string => {\n switch (failure.error_kind) {\n case \"timeout\":\n return t(I18nKey.MCP$TEST_ERROR_TIMEOUT);\n case \"connection\":\n return t(I18nKey.MCP$TEST_ERROR_CONNECTION);\n case \"credentials\":\n return t(I18nKey.MCP$TEST_ERROR_CREDENTIALS, { error: failure.error });\n default:\n return t(I18nKey.MCP$TEST_ERROR_UNKNOWN, { error: failure.error });\n }\n };\n\n const testMessage: TestMessage | null = React.useMemo(() => {\n if (!testResult) return null;\n if (testResult.ok) {\n return {\n ok: true,\n text: t(I18nKey.MCP$TEST_SUCCESS, { count: testResult.tools.length }),\n };\n }\n return { ok: false, text: makeTestErrorMessage(testResult) };\n }, [testResult, t]);\n\n // Shared error handler so both add and update surface backend errors\n // as a toast instead of failing silently — previously these calls\n // had no `onError` and the modal closed even on a 4xx/5xx, leaving\n // the user to discover the failure on the next page load.\n const handleError = (err: unknown) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n displayErrorToast(message || t(I18nKey.ERROR$GENERIC));\n };\n\n const handleSubmit = (payload: MCPServerConfig) => {\n resetTest();\n testServer(payload, {\n onSuccess: (result) => {\n if (!result.ok) {\n // Test failed — modal stays open, error shown via testMessage.\n return;\n }\n if (isEditing) {\n updateMcpServer(\n { serverId: server.id, server: payload },\n { onSuccess: onClose, onError: handleError },\n );\n } else {\n addMcpServer(payload, { onSuccess: onClose, onError: handleError });\n }\n },\n onError: handleError,\n });\n };\n\n const handleTestClick = (payload: MCPServerConfig) => {\n testServer(payload);\n };\n\n const handleConfirmDelete = () => {\n deleteMcpServer(server, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$REMOVE_SUCCESS));\n setShowDeleteConfirm(false);\n onClose();\n },\n onError: (err) => {\n handleError(err);\n setShowDeleteConfirm(false);\n },\n });\n };\n\n return (\n <>\n <ModalBackdrop\n // Block backdrop-click / Escape from dismissing the modal while\n // a mutation is in flight — closing mid-request would orphan\n // the request and leave the user with no error feedback.\n onClose={isDismissBlocked ? undefined : onClose}\n closeOnEscape={!isDismissBlocked}\n aria-label={\n isEditing\n ? t(I18nKey.MCP$EDIT_CUSTOM_TITLE)\n : t(I18nKey.MCP$ADD_CUSTOM_TITLE)\n }\n >\n <div\n data-testid=\"mcp-custom-editor\"\n className=\"relative bg-base-secondary p-6 rounded-xl border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[90vh] overflow-y-auto custom-scrollbar\"\n >\n <ModalCloseButton\n onClose={onClose}\n testId=\"mcp-custom-editor-close\"\n disabled={isDismissBlocked}\n />\n <h2 className={cn(\"mb-4 pr-6\", modalTitleLgClassName)}>\n {isEditing\n ? t(I18nKey.MCP$EDIT_CUSTOM_TITLE)\n : t(I18nKey.MCP$ADD_CUSTOM_TITLE)}\n </h2>\n <MCPServerForm\n mode={isEditing ? \"edit\" : \"add\"}\n server={isEditing ? server : undefined}\n existingServers={existingServers}\n onSubmit={handleSubmit}\n onCancel={onClose}\n onDelete={isEditing ? () => setShowDeleteConfirm(true) : undefined}\n isActionDisabled={isPending}\n onTest={isCloudBackend ? undefined : handleTestClick}\n isTestPending={isTesting}\n testMessage={isCloudBackend ? null : testMessage}\n />\n </div>\n </ModalBackdrop>\n\n {showDeleteConfirm ? (\n <ConfirmationModal\n text={t(I18nKey.SETTINGS$MCP_CONFIRM_DELETE)}\n onCancel={() => setShowDeleteConfirm(false)}\n onConfirm={handleConfirmDelete}\n isConfirming={isDeleting}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":"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"}
@@ -21,6 +21,7 @@ function b({ server: b, existingServers: x, onClose: S }) {
21
21
  switch (e.error_kind) {
22
22
  case "timeout": return C(t.MCP$TEST_ERROR_TIMEOUT);
23
23
  case "connection": return C(t.MCP$TEST_ERROR_CONNECTION);
24
+ case "credentials": return C(t.MCP$TEST_ERROR_CREDENTIALS, { error: e.error });
24
25
  default: return C(t.MCP$TEST_ERROR_UNKNOWN, { error: e.error });
25
26
  }
26
27
  }, H = g.useMemo(() => M ? M.ok ? {
@@ -1 +1 @@
1
- {"version":3,"file":"custom-server-editor.js","names":[],"sources":["../../../../src/components/features/mcp-page/custom-server-editor.tsx"],"sourcesContent":["import React from \"react\";\nimport { AxiosError } from \"axios\";\nimport { useTranslation } from \"react-i18next\";\nimport type { MCPTestFailure } from \"@openhands/typescript-client\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalCloseButton } from \"#/components/shared/modals/modal-close-button\";\nimport { ConfirmationModal } from \"#/components/shared/modals/confirmation-modal\";\nimport {\n MCPServerForm,\n type TestMessage,\n} from \"#/components/features/settings/mcp-settings/mcp-server-form\";\nimport { useAddMcpServer } from \"#/hooks/mutation/use-add-mcp-server\";\nimport { useUpdateMcpServer } from \"#/hooks/mutation/use-update-mcp-server\";\nimport { useDeleteMcpServer } from \"#/hooks/mutation/use-delete-mcp-server\";\nimport { useTestMcpServer } from \"#/hooks/mutation/use-test-mcp-server\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { MCPServerConfig } from \"#/types/mcp-server\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { cn } from \"#/utils/utils\";\nimport { modalTitleLgClassName } from \"#/utils/modal-classes\";\n\ninterface CustomServerEditorProps {\n server: MCPServerConfig;\n existingServers: MCPServerConfig[];\n onClose: () => void;\n}\n\n/**\n * Modal wrapper around `MCPServerForm` so users can hand-author\n * arbitrary stdio / SSE / SHTTP entries without reaching for raw JSON.\n * An empty `server.id` means \"Add new\".\n */\nexport function CustomServerEditor({\n server,\n existingServers,\n onClose,\n}: CustomServerEditorProps) {\n const { t } = useTranslation(\"openhands\");\n const { mutate: addMcpServer, isPending: isAdding } = useAddMcpServer();\n const { mutate: updateMcpServer, isPending: isUpdating } =\n useUpdateMcpServer();\n const { mutate: deleteMcpServer, isPending: isDeleting } =\n useDeleteMcpServer();\n const {\n mutate: testServer,\n isPending: isTesting,\n data: testResult,\n reset: resetTest,\n } = useTestMcpServer();\n const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);\n\n // The MCP connectivity-test endpoint only exists on the local agent-server.\n // For cloud backends `McpService.testServer` short-circuits with a synthetic\n // success so the save still completes; we hide the manual \"Test connection\"\n // button here so cloud users aren't shown a misleading \"0 tools\" result.\n const { backend } = useActiveBackend();\n const isCloudBackend = backend.kind === \"cloud\";\n\n const isEditing = !!server.id;\n const isPending = isAdding || isUpdating || isDeleting;\n const isDismissBlocked = isPending || isTesting || showDeleteConfirm;\n\n const makeTestErrorMessage = (failure: MCPTestFailure): string => {\n switch (failure.error_kind) {\n case \"timeout\":\n return t(I18nKey.MCP$TEST_ERROR_TIMEOUT);\n case \"connection\":\n return t(I18nKey.MCP$TEST_ERROR_CONNECTION);\n default:\n return t(I18nKey.MCP$TEST_ERROR_UNKNOWN, { error: failure.error });\n }\n };\n\n const testMessage: TestMessage | null = React.useMemo(() => {\n if (!testResult) return null;\n if (testResult.ok) {\n return {\n ok: true,\n text: t(I18nKey.MCP$TEST_SUCCESS, { count: testResult.tools.length }),\n };\n }\n return { ok: false, text: makeTestErrorMessage(testResult) };\n }, [testResult, t]);\n\n // Shared error handler so both add and update surface backend errors\n // as a toast instead of failing silently — previously these calls\n // had no `onError` and the modal closed even on a 4xx/5xx, leaving\n // the user to discover the failure on the next page load.\n const handleError = (err: unknown) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n displayErrorToast(message || t(I18nKey.ERROR$GENERIC));\n };\n\n const handleSubmit = (payload: MCPServerConfig) => {\n resetTest();\n testServer(payload, {\n onSuccess: (result) => {\n if (!result.ok) {\n // Test failed — modal stays open, error shown via testMessage.\n return;\n }\n if (isEditing) {\n updateMcpServer(\n { serverId: server.id, server: payload },\n { onSuccess: onClose, onError: handleError },\n );\n } else {\n addMcpServer(payload, { onSuccess: onClose, onError: handleError });\n }\n },\n onError: handleError,\n });\n };\n\n const handleTestClick = (payload: MCPServerConfig) => {\n testServer(payload);\n };\n\n const handleConfirmDelete = () => {\n deleteMcpServer(server, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$REMOVE_SUCCESS));\n setShowDeleteConfirm(false);\n onClose();\n },\n onError: (err) => {\n handleError(err);\n setShowDeleteConfirm(false);\n },\n });\n };\n\n return (\n <>\n <ModalBackdrop\n // Block backdrop-click / Escape from dismissing the modal while\n // a mutation is in flight — closing mid-request would orphan\n // the request and leave the user with no error feedback.\n onClose={isDismissBlocked ? undefined : onClose}\n closeOnEscape={!isDismissBlocked}\n aria-label={\n isEditing\n ? t(I18nKey.MCP$EDIT_CUSTOM_TITLE)\n : t(I18nKey.MCP$ADD_CUSTOM_TITLE)\n }\n >\n <div\n data-testid=\"mcp-custom-editor\"\n className=\"relative bg-base-secondary p-6 rounded-xl border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[90vh] overflow-y-auto custom-scrollbar\"\n >\n <ModalCloseButton\n onClose={onClose}\n testId=\"mcp-custom-editor-close\"\n disabled={isDismissBlocked}\n />\n <h2 className={cn(\"mb-4 pr-6\", modalTitleLgClassName)}>\n {isEditing\n ? t(I18nKey.MCP$EDIT_CUSTOM_TITLE)\n : t(I18nKey.MCP$ADD_CUSTOM_TITLE)}\n </h2>\n <MCPServerForm\n mode={isEditing ? \"edit\" : \"add\"}\n server={isEditing ? server : undefined}\n existingServers={existingServers}\n onSubmit={handleSubmit}\n onCancel={onClose}\n onDelete={isEditing ? () => setShowDeleteConfirm(true) : undefined}\n isActionDisabled={isPending}\n onTest={isCloudBackend ? undefined : handleTestClick}\n isTestPending={isTesting}\n testMessage={isCloudBackend ? null : testMessage}\n />\n </div>\n </ModalBackdrop>\n\n {showDeleteConfirm ? (\n <ConfirmationModal\n text={t(I18nKey.SETTINGS$MCP_CONFIRM_DELETE)}\n onCancel={() => setShowDeleteConfirm(false)}\n onConfirm={handleConfirmDelete}\n isConfirming={isDeleting}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqCA,SAAgB,EAAmB,EACjC,WACA,oBACA,cAC0B;CAC1B,IAAM,EAAE,SAAM,EAAe,YAAY,EACnC,EAAE,QAAQ,GAAc,WAAW,MAAa,GAAiB,EACjE,EAAE,QAAQ,GAAiB,WAAW,MAC1C,GAAoB,EAChB,EAAE,QAAQ,GAAiB,WAAW,MAC1C,GAAoB,EAChB,EACJ,QAAQ,GACR,WAAW,GACX,MAAM,GACN,OAAO,MACL,GAAkB,EAChB,CAAC,GAAmB,KAAwB,EAAM,SAAS,GAAM,EAMjE,EAAE,eAAY,GAAkB,EAChC,IAAiB,EAAQ,SAAS,SAElC,IAAY,CAAC,CAAC,EAAO,IACrB,IAAY,KAAY,KAAc,GACtC,IAAmB,KAAa,KAAa,GAE7C,KAAwB,MAAoC;AAChE,UAAQ,EAAQ,YAAhB;GACE,KAAK,UACH,QAAO,EAAE,EAAQ,uBAAuB;GAC1C,KAAK,aACH,QAAO,EAAE,EAAQ,0BAA0B;GAC7C,QACE,QAAO,EAAE,EAAQ,wBAAwB,EAAE,OAAO,EAAQ,OAAO,CAAC;;IAIlE,IAAkC,EAAM,cACvC,IACD,EAAW,KACN;EACL,IAAI;EACJ,MAAM,EAAE,EAAQ,kBAAkB,EAAE,OAAO,EAAW,MAAM,QAAQ,CAAC;EACtE,GAEI;EAAE,IAAI;EAAO,MAAM,EAAqB,EAAW;EAAE,GAPpC,MAQvB,CAAC,GAAY,EAAE,CAAC,EAMb,KAAe,MAAiB;AAEpC,IADgB,EAA0B,EACxB,IAAW,EAAE,EAAQ,cAAc,CAAC;;AA0CxD,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EAIE,SAAS,IAAmB,KAAA,IAAY;EACxC,eAAe,CAAC;EAChB,cAEM,EADJ,IACM,EAAQ,wBACR,EAAQ,qBAAqB;YAGrC,kBAAC,OAAD;GACE,eAAY;GACZ,WAAU;aAFZ;IAIE,kBAAC,GAAD;KACW;KACT,QAAO;KACP,UAAU;KACV,CAAA;IACF,kBAAC,MAAD;KAAI,WAAW,EAAG,aAAa,EAAsB;eAE/C,EADH,IACK,EAAQ,wBACR,EAAQ,qBAAqB;KAChC,CAAA;IACL,kBAAC,GAAD;KACE,MAAM,IAAY,SAAS;KAC3B,QAAQ,IAAY,IAAS,KAAA;KACZ;KACjB,WAvEY,MAA6B;AAEjD,MADA,GAAW,EACX,EAAW,GAAS;OAClB,YAAY,MAAW;AAChB,UAAO,OAIR,IACF,EACE;SAAE,UAAU,EAAO;SAAI,QAAQ;SAAS,EACxC;SAAE,WAAW;SAAS,SAAS;SAAa,CAC7C,GAED,EAAa,GAAS;SAAE,WAAW;SAAS,SAAS;SAAa,CAAC;;OAGvE,SAAS;OACV,CAAC;;KAsDM,UAAU;KACV,UAAU,UAAkB,EAAqB,GAAK,GAAG,KAAA;KACzD,kBAAkB;KAClB,QAAQ,IAAiB,KAAA,KAtDV,MAA6B;AACpD,QAAW,EAAQ;;KAsDX,eAAe;KACf,aAAa,IAAiB,OAAO;KACrC,CAAA;IACE;;EACQ,CAAA,EAEf,IACC,kBAAC,GAAD;EACE,MAAM,EAAE,EAAQ,4BAA4B;EAC5C,gBAAgB,EAAqB,GAAM;EAC3C,iBA7D0B;AAChC,KAAgB,GAAQ;IACtB,iBAAiB;AAGf,KAFA,EAAoB,EAAE,EAAQ,mBAAmB,CAAC,EAClD,EAAqB,GAAM,EAC3B,GAAS;;IAEX,UAAU,MAAQ;AAEhB,KADA,EAAY,EAAI,EAChB,EAAqB,GAAM;;IAE9B,CAAC;;EAmDI,cAAc;EACd,CAAA,GACA,KACH,EAAA,CAAA"}
1
+ {"version":3,"file":"custom-server-editor.js","names":[],"sources":["../../../../src/components/features/mcp-page/custom-server-editor.tsx"],"sourcesContent":["import React from \"react\";\nimport { AxiosError } from \"axios\";\nimport { useTranslation } from \"react-i18next\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalCloseButton } from \"#/components/shared/modals/modal-close-button\";\nimport { ConfirmationModal } from \"#/components/shared/modals/confirmation-modal\";\nimport {\n MCPServerForm,\n type TestMessage,\n} from \"#/components/features/settings/mcp-settings/mcp-server-form\";\nimport { useAddMcpServer } from \"#/hooks/mutation/use-add-mcp-server\";\nimport { useUpdateMcpServer } from \"#/hooks/mutation/use-update-mcp-server\";\nimport { useDeleteMcpServer } from \"#/hooks/mutation/use-delete-mcp-server\";\nimport { useTestMcpServer } from \"#/hooks/mutation/use-test-mcp-server\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { ExtendedMCPTestFailure, MCPServerConfig } from \"#/types/mcp-server\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { cn } from \"#/utils/utils\";\nimport { modalTitleLgClassName } from \"#/utils/modal-classes\";\n\ninterface CustomServerEditorProps {\n server: MCPServerConfig;\n existingServers: MCPServerConfig[];\n onClose: () => void;\n}\n\n/**\n * Modal wrapper around `MCPServerForm` so users can hand-author\n * arbitrary stdio / SSE / SHTTP entries without reaching for raw JSON.\n * An empty `server.id` means \"Add new\".\n */\nexport function CustomServerEditor({\n server,\n existingServers,\n onClose,\n}: CustomServerEditorProps) {\n const { t } = useTranslation(\"openhands\");\n const { mutate: addMcpServer, isPending: isAdding } = useAddMcpServer();\n const { mutate: updateMcpServer, isPending: isUpdating } =\n useUpdateMcpServer();\n const { mutate: deleteMcpServer, isPending: isDeleting } =\n useDeleteMcpServer();\n const {\n mutate: testServer,\n isPending: isTesting,\n data: testResult,\n reset: resetTest,\n } = useTestMcpServer();\n const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);\n\n // The MCP connectivity-test endpoint only exists on the local agent-server.\n // For cloud backends `McpService.testServer` short-circuits with a synthetic\n // success so the save still completes; we hide the manual \"Test connection\"\n // button here so cloud users aren't shown a misleading \"0 tools\" result.\n const { backend } = useActiveBackend();\n const isCloudBackend = backend.kind === \"cloud\";\n\n const isEditing = !!server.id;\n const isPending = isAdding || isUpdating || isDeleting;\n const isDismissBlocked = isPending || isTesting || showDeleteConfirm;\n\n const makeTestErrorMessage = (failure: ExtendedMCPTestFailure): string => {\n switch (failure.error_kind) {\n case \"timeout\":\n return t(I18nKey.MCP$TEST_ERROR_TIMEOUT);\n case \"connection\":\n return t(I18nKey.MCP$TEST_ERROR_CONNECTION);\n case \"credentials\":\n return t(I18nKey.MCP$TEST_ERROR_CREDENTIALS, { error: failure.error });\n default:\n return t(I18nKey.MCP$TEST_ERROR_UNKNOWN, { error: failure.error });\n }\n };\n\n const testMessage: TestMessage | null = React.useMemo(() => {\n if (!testResult) return null;\n if (testResult.ok) {\n return {\n ok: true,\n text: t(I18nKey.MCP$TEST_SUCCESS, { count: testResult.tools.length }),\n };\n }\n return { ok: false, text: makeTestErrorMessage(testResult) };\n }, [testResult, t]);\n\n // Shared error handler so both add and update surface backend errors\n // as a toast instead of failing silently — previously these calls\n // had no `onError` and the modal closed even on a 4xx/5xx, leaving\n // the user to discover the failure on the next page load.\n const handleError = (err: unknown) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n displayErrorToast(message || t(I18nKey.ERROR$GENERIC));\n };\n\n const handleSubmit = (payload: MCPServerConfig) => {\n resetTest();\n testServer(payload, {\n onSuccess: (result) => {\n if (!result.ok) {\n // Test failed — modal stays open, error shown via testMessage.\n return;\n }\n if (isEditing) {\n updateMcpServer(\n { serverId: server.id, server: payload },\n { onSuccess: onClose, onError: handleError },\n );\n } else {\n addMcpServer(payload, { onSuccess: onClose, onError: handleError });\n }\n },\n onError: handleError,\n });\n };\n\n const handleTestClick = (payload: MCPServerConfig) => {\n testServer(payload);\n };\n\n const handleConfirmDelete = () => {\n deleteMcpServer(server, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$REMOVE_SUCCESS));\n setShowDeleteConfirm(false);\n onClose();\n },\n onError: (err) => {\n handleError(err);\n setShowDeleteConfirm(false);\n },\n });\n };\n\n return (\n <>\n <ModalBackdrop\n // Block backdrop-click / Escape from dismissing the modal while\n // a mutation is in flight — closing mid-request would orphan\n // the request and leave the user with no error feedback.\n onClose={isDismissBlocked ? undefined : onClose}\n closeOnEscape={!isDismissBlocked}\n aria-label={\n isEditing\n ? t(I18nKey.MCP$EDIT_CUSTOM_TITLE)\n : t(I18nKey.MCP$ADD_CUSTOM_TITLE)\n }\n >\n <div\n data-testid=\"mcp-custom-editor\"\n className=\"relative bg-base-secondary p-6 rounded-xl border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[90vh] overflow-y-auto custom-scrollbar\"\n >\n <ModalCloseButton\n onClose={onClose}\n testId=\"mcp-custom-editor-close\"\n disabled={isDismissBlocked}\n />\n <h2 className={cn(\"mb-4 pr-6\", modalTitleLgClassName)}>\n {isEditing\n ? t(I18nKey.MCP$EDIT_CUSTOM_TITLE)\n : t(I18nKey.MCP$ADD_CUSTOM_TITLE)}\n </h2>\n <MCPServerForm\n mode={isEditing ? \"edit\" : \"add\"}\n server={isEditing ? server : undefined}\n existingServers={existingServers}\n onSubmit={handleSubmit}\n onCancel={onClose}\n onDelete={isEditing ? () => setShowDeleteConfirm(true) : undefined}\n isActionDisabled={isPending}\n onTest={isCloudBackend ? undefined : handleTestClick}\n isTestPending={isTesting}\n testMessage={isCloudBackend ? null : testMessage}\n />\n </div>\n </ModalBackdrop>\n\n {showDeleteConfirm ? (\n <ConfirmationModal\n text={t(I18nKey.SETTINGS$MCP_CONFIRM_DELETE)}\n onCancel={() => setShowDeleteConfirm(false)}\n onConfirm={handleConfirmDelete}\n isConfirming={isDeleting}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoCA,SAAgB,EAAmB,EACjC,WACA,oBACA,cAC0B;CAC1B,IAAM,EAAE,SAAM,EAAe,YAAY,EACnC,EAAE,QAAQ,GAAc,WAAW,MAAa,GAAiB,EACjE,EAAE,QAAQ,GAAiB,WAAW,MAC1C,GAAoB,EAChB,EAAE,QAAQ,GAAiB,WAAW,MAC1C,GAAoB,EAChB,EACJ,QAAQ,GACR,WAAW,GACX,MAAM,GACN,OAAO,MACL,GAAkB,EAChB,CAAC,GAAmB,KAAwB,EAAM,SAAS,GAAM,EAMjE,EAAE,eAAY,GAAkB,EAChC,IAAiB,EAAQ,SAAS,SAElC,IAAY,CAAC,CAAC,EAAO,IACrB,IAAY,KAAY,KAAc,GACtC,IAAmB,KAAa,KAAa,GAE7C,KAAwB,MAA4C;AACxE,UAAQ,EAAQ,YAAhB;GACE,KAAK,UACH,QAAO,EAAE,EAAQ,uBAAuB;GAC1C,KAAK,aACH,QAAO,EAAE,EAAQ,0BAA0B;GAC7C,KAAK,cACH,QAAO,EAAE,EAAQ,4BAA4B,EAAE,OAAO,EAAQ,OAAO,CAAC;GACxE,QACE,QAAO,EAAE,EAAQ,wBAAwB,EAAE,OAAO,EAAQ,OAAO,CAAC;;IAIlE,IAAkC,EAAM,cACvC,IACD,EAAW,KACN;EACL,IAAI;EACJ,MAAM,EAAE,EAAQ,kBAAkB,EAAE,OAAO,EAAW,MAAM,QAAQ,CAAC;EACtE,GAEI;EAAE,IAAI;EAAO,MAAM,EAAqB,EAAW;EAAE,GAPpC,MAQvB,CAAC,GAAY,EAAE,CAAC,EAMb,KAAe,MAAiB;AAEpC,IADgB,EAA0B,EACxB,IAAW,EAAE,EAAQ,cAAc,CAAC;;AA0CxD,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EAIE,SAAS,IAAmB,KAAA,IAAY;EACxC,eAAe,CAAC;EAChB,cAEM,EADJ,IACM,EAAQ,wBACR,EAAQ,qBAAqB;YAGrC,kBAAC,OAAD;GACE,eAAY;GACZ,WAAU;aAFZ;IAIE,kBAAC,GAAD;KACW;KACT,QAAO;KACP,UAAU;KACV,CAAA;IACF,kBAAC,MAAD;KAAI,WAAW,EAAG,aAAa,EAAsB;eAE/C,EADH,IACK,EAAQ,wBACR,EAAQ,qBAAqB;KAChC,CAAA;IACL,kBAAC,GAAD;KACE,MAAM,IAAY,SAAS;KAC3B,QAAQ,IAAY,IAAS,KAAA;KACZ;KACjB,WAvEY,MAA6B;AAEjD,MADA,GAAW,EACX,EAAW,GAAS;OAClB,YAAY,MAAW;AAChB,UAAO,OAIR,IACF,EACE;SAAE,UAAU,EAAO;SAAI,QAAQ;SAAS,EACxC;SAAE,WAAW;SAAS,SAAS;SAAa,CAC7C,GAED,EAAa,GAAS;SAAE,WAAW;SAAS,SAAS;SAAa,CAAC;;OAGvE,SAAS;OACV,CAAC;;KAsDM,UAAU;KACV,UAAU,UAAkB,EAAqB,GAAK,GAAG,KAAA;KACzD,kBAAkB;KAClB,QAAQ,IAAiB,KAAA,KAtDV,MAA6B;AACpD,QAAW,EAAQ;;KAsDX,eAAe;KACf,aAAa,IAAiB,OAAO;KACrC,CAAA;IACE;;EACQ,CAAA,EAEf,IACC,kBAAC,GAAD;EACE,MAAM,EAAE,EAAQ,4BAA4B;EAC5C,gBAAgB,EAAqB,GAAM;EAC3C,iBA7D0B;AAChC,KAAgB,GAAQ;IACtB,iBAAiB;AAGf,KAFA,EAAoB,EAAE,EAAQ,mBAAmB,CAAC,EAClD,EAAqB,GAAM,EAC3B,GAAS;;IAEX,UAAU,MAAQ;AAEhB,KADA,EAAY,EAAI,EAChB,EAAqB,GAAM;;IAE9B,CAAC;;EAmDI,cAAc;EACd,CAAA,GACA,KACH,EAAA,CAAA"}
@@ -1,2 +1,2 @@
1
- const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../utils/retrieve-axios-error-message.cjs`),i=require(`../../../utils/custom-toast-handlers.cjs`),a=require(`../../../node_modules/uuid/dist/v4.cjs`),o=require(`../../shared/modals/modal-backdrop.cjs`),s=require(`../../../utils/modal-classes.cjs`),c=require(`../../shared/modals/modal-close-button.cjs`),l=require(`../settings/brand-button.cjs`),u=require(`../settings/settings-input.cjs`),d=require(`../../../utils/mcp-marketplace-utils.cjs`),f=require(`../mcp-logo-badge.cjs`),p=require(`./save-as-secret-toggle.cjs`),m=require(`../../../hooks/mutation/use-add-mcp-server.cjs`),h=require(`../../../hooks/mutation/use-test-mcp-server.cjs`),g=require(`../../../hooks/mutation/use-save-fields-as-secrets.cjs`);let _=require(`react`);_=e.__toESM(_,1);let v=require(`react/jsx-runtime`);function y(e){let t=/\[([^\]]+)\]\(([^)]+)\)/g,n=[],r=0;for(let i of e.matchAll(t))i.index>r&&n.push(e.slice(r,i.index)),n.push((0,v.jsx)(`a`,{href:/^https?:\/\//i.test(i[2])?i[2]:`#`,target:`_blank`,rel:`noreferrer`,className:`underline hover:text-white transition-colors`,children:i[1]},i.index)),r=i.index+i[0].length;return r<e.length&&n.push(e.slice(r)),n}function b(e){return e?.transport.kind!==`shttp`&&e?.transport.kind!==`sse`?!1:[`api_key`,`bearer`,`basic`].includes(e.auth.strategy)}function x(e){return e.transport.kind===`stdio`?e.auth.apiKeyOptional??!1:e.auth.apiKeyOptional??e.transport.apiKeyOptional??!1}function S(e){let t={},n={},r=d.getInstallableMcpConnectionOption(e),i=r?.transport;if(i?.kind===`stdio`){for(let e of i.envFields??[])t[e.key]=``,n[e.key]=e.type===`password`;for(let e of i.argFields??[])t[e.key]=``}else b(r)&&(t.api_key=``);return{values:t,errors:{},savedAsSecret:n}}function C({entry:e,onClose:C,onSuccess:w}){let{t:T}=t.useTranslation(`openhands`),{mutate:E,isPending:D}=m.useAddMcpServer(),{mutate:O,isPending:k}=h.useTestMcpServer(),A=g.useSaveFieldsAsSecrets(),[j,M]=_.default.useState(()=>S(e)),N=_.default.useRef(j);N.current=j;let[P,F]=_.default.useState(null),I=d.getInstallableMcpConnectionOption(e),L=I?.transport,R=k||D,z=(e,t)=>{M(n=>({...n,values:{...n.values,[e]:t},errors:{...n.errors,[e]:null}})),F(null)},B=(e,t)=>{M(n=>({...n,savedAsSecret:{...n.savedAsSecret,[e]:t}}))},V=e=>{switch(e.error_kind){case`timeout`:return T(n.I18nKey.MCP$TEST_ERROR_TIMEOUT);case`connection`:return T(n.I18nKey.MCP$TEST_ERROR_CONNECTION);default:return T(n.I18nKey.MCP$TEST_ERROR_UNKNOWN,{error:e.error})}},H=t=>{O(t,{onSuccess:a=>{if(!a.ok){F(V(a));return}E(t,{onSuccess:()=>{i.displaySuccessToast(T(n.I18nKey.MCP$INSTALL_SUCCESS)),w?.(e),C(),L?.kind===`stdio`&&A(L.envFields??[],N.current.values,N.current.savedAsSecret)},onError:e=>{F(r.retrieveAxiosErrorMessage(e)||T(n.I18nKey.ERROR$GENERIC))}})},onError:e=>{F(r.retrieveAxiosErrorMessage(e)||T(n.I18nKey.ERROR$GENERIC))}})},U=()=>{if(L?.kind!==`shttp`&&L?.kind!==`sse`||!I)return;let e=j.values.api_key?.trim()??``,t=b(I);if(t&&!x(I)&&!e){M(e=>({...e,errors:{api_key:T(n.I18nKey.MCP$ERROR_FIELD_REQUIRED)}}));return}H({id:`${L.kind}-${a.default()}`,type:L.kind,url:L.url,...t&&e&&{api_key:e}})},W=()=>{if(L?.kind!==`stdio`)return;let e=L,t={};for(let r of e.envFields??[])r.required&&!(j.values[r.key]??``).trim()&&(t[r.key]=T(n.I18nKey.MCP$ERROR_FIELD_REQUIRED));for(let r of e.argFields??[])r.required&&!(j.values[r.key]??``).trim()&&(t[r.key]=T(n.I18nKey.MCP$ERROR_FIELD_REQUIRED));if(Object.values(t).some(Boolean)){M(e=>({...e,errors:t}));return}let r={};for(let t of e.envFields??[]){let e=j.values[t.key]?.trim();e&&(r[t.key]=e)}let i=[];for(let t of e.argFields??[]){let e=j.values[t.key]?.trim();if(e)for(let t of e.split(/\s+/))t&&i.push(t)}H({id:`stdio-${a.default()}`,type:`stdio`,name:e.serverName,command:e.command,args:[...e.args,...i],...Object.keys(r).length>0&&{env:r}})};return(0,v.jsx)(o.ModalBackdrop,{onClose:C,"aria-label":e.name,children:(0,v.jsxs)(`form`,{"data-testid":`mcp-install-modal`,"data-marketplace-id":e.id,onSubmit:e=>(e.preventDefault(),F(null),L?.kind===`shttp`||L?.kind===`sse`?U():W()),className:`relative bg-base-secondary p-6 rounded-xl flex flex-col gap-4 border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[85vh] overflow-y-auto custom-scrollbar`,children:[(0,v.jsx)(c.ModalCloseButton,{onClose:C,testId:`mcp-install-modal-close`,disabled:R}),(0,v.jsxs)(`div`,{className:`flex items-start gap-3 pr-6`,children:[(0,v.jsx)(f.McpLogoBadge,{entry:e}),(0,v.jsxs)(`div`,{className:`flex flex-col flex-1`,children:[(0,v.jsx)(`h2`,{className:s.modalTitleLgClassName,children:e.name}),(0,v.jsx)(`p`,{className:`text-xs text-tertiary-light`,children:e.description})]})]}),e.installHint&&(0,v.jsx)(`p`,{className:`text-xs text-tertiary-light`,children:e.installHint}),e.docsUrl&&(0,v.jsx)(`a`,{href:e.docsUrl,target:`_blank`,rel:`noreferrer`,className:`text-xs text-[var(--oh-muted)] hover:text-white hover:underline self-start transition-colors`,children:T(n.I18nKey.MCP$VIEW_DOCS)}),(0,v.jsx)(`div`,{className:`flex flex-col gap-3`,children:(()=>{if(L?.kind===`shttp`||L?.kind===`sse`){let e=b(I),t=I?x(I):!1;return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-url`,name:`url`,type:`url`,label:T(n.I18nKey.SETTINGS$MCP_URL),value:L.url,onChange:()=>{},isDisabled:!0,className:`w-full`}),e?(0,v.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-api_key`,name:`api_key`,type:`password`,label:T(n.I18nKey.SETTINGS$MCP_API_KEY),value:j.values.api_key??``,onChange:e=>z(`api_key`,e),placeholder:T(n.I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER),showOptionalTag:t,required:!t,className:`w-full`}),j.errors.api_key&&(0,v.jsx)(`p`,{className:`text-xs text-red-500`,children:j.errors.api_key})]}):null]})}if(L?.kind!==`stdio`)return null;let e=L;return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-command-readonly`,name:`command-readonly`,type:`text`,label:T(n.I18nKey.MCP$COMMAND_LABEL),value:`${e.command} ${e.args.join(` `)}`.trim(),onChange:()=>{},isDisabled:!0,className:`w-full`}),(e.envFields??[]).map(e=>(0,v.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-${e.key}`,name:e.key,type:e.type===`password`?`password`:`text`,label:e.label,value:j.values[e.key]??``,onChange:t=>z(e.key,t),placeholder:e.placeholder,required:e.required,showOptionalTag:!e.required,className:`w-full`}),e.helperText&&(0,v.jsx)(`p`,{className:`text-xs text-tertiary-alt`,children:y(e.helperText)}),j.errors[e.key]&&(0,v.jsx)(`p`,{className:`text-xs text-red-500`,children:j.errors[e.key]}),e.key in j.savedAsSecret&&(0,v.jsx)(p.SaveAsSecretToggle,{fieldKey:e.key,checked:j.savedAsSecret[e.key],onToggle:t=>B(e.key,t)})]},e.key)),(e.argFields??[]).map(e=>(0,v.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-${e.key}`,name:e.key,type:e.type===`password`?`password`:`text`,label:e.label,value:j.values[e.key]??``,onChange:t=>z(e.key,t),placeholder:e.placeholder,required:e.required,showOptionalTag:!e.required,className:`w-full`}),e.helperText&&(0,v.jsx)(`p`,{className:`text-xs text-tertiary-alt`,children:y(e.helperText)}),j.errors[e.key]&&(0,v.jsx)(`p`,{className:`text-xs text-red-500`,children:j.errors[e.key]})]},e.key))]})})()}),P&&(0,v.jsx)(`p`,{"data-testid":`mcp-install-modal-error`,className:`text-sm text-red-500 whitespace-pre-wrap`,children:P}),(0,v.jsxs)(`div`,{className:`flex justify-end gap-2 mt-2`,children:[(0,v.jsx)(l.BrandButton,{type:`button`,variant:`secondary`,onClick:C,testId:`mcp-install-cancel`,children:T(n.I18nKey.BUTTON$CANCEL)}),(0,v.jsx)(l.BrandButton,{type:`submit`,variant:`primary`,isDisabled:R,testId:`mcp-install-submit`,children:T(k?n.I18nKey.MCP$VERIFYING:D?n.I18nKey.SETTINGS$SAVING:n.I18nKey.MCP$INSTALL_BUTTON)})]})]})})}exports.InstallServerModal=C;
1
+ const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../utils/retrieve-axios-error-message.cjs`),i=require(`../../../utils/custom-toast-handlers.cjs`),a=require(`../../../node_modules/uuid/dist/v4.cjs`),o=require(`../../shared/modals/modal-backdrop.cjs`),s=require(`../../../utils/modal-classes.cjs`),c=require(`../../shared/modals/modal-close-button.cjs`),l=require(`../settings/brand-button.cjs`),u=require(`../settings/settings-input.cjs`),d=require(`../../../utils/mcp-marketplace-utils.cjs`),f=require(`../mcp-logo-badge.cjs`),p=require(`./save-as-secret-toggle.cjs`),m=require(`../../../hooks/mutation/use-add-mcp-server.cjs`),h=require(`../../../hooks/mutation/use-test-mcp-server.cjs`),g=require(`../../../hooks/mutation/use-save-fields-as-secrets.cjs`);let _=require(`react`);_=e.__toESM(_,1);let v=require(`react/jsx-runtime`);function y(e){let t=/\[([^\]]+)\]\(([^)]+)\)/g,n=[],r=0;for(let i of e.matchAll(t))i.index>r&&n.push(e.slice(r,i.index)),n.push((0,v.jsx)(`a`,{href:/^https?:\/\//i.test(i[2])?i[2]:`#`,target:`_blank`,rel:`noreferrer`,className:`underline hover:text-white transition-colors`,children:i[1]},i.index)),r=i.index+i[0].length;return r<e.length&&n.push(e.slice(r)),n}function b(e){return e?.transport.kind!==`shttp`&&e?.transport.kind!==`sse`?!1:[`api_key`,`bearer`,`basic`].includes(e.auth.strategy)}function x(e){return e.transport.kind===`stdio`?e.auth.apiKeyOptional??!1:e.auth.apiKeyOptional??e.transport.apiKeyOptional??!1}function S(e){let t={},n={},r=d.getInstallableMcpConnectionOption(e),i=r?.transport;if(i?.kind===`stdio`){for(let e of i.envFields??[])t[e.key]=``,n[e.key]=e.type===`password`;for(let e of i.argFields??[])t[e.key]=``}else b(r)&&(t.api_key=``,r?.auth.credentialSecretName&&(n.api_key=r.auth.saveCredentialAsSecretByDefault??!1));return{values:t,errors:{},savedAsSecret:n}}function C({entry:e,onClose:C,onSuccess:w}){let{t:T}=t.useTranslation(`openhands`),{mutate:E,isPending:D}=m.useAddMcpServer(),{mutate:O,isPending:k}=h.useTestMcpServer(),A=g.useSaveFieldsAsSecrets(),[j,M]=_.default.useState(()=>S(e)),N=_.default.useRef(j);N.current=j;let[P,F]=_.default.useState(null),[I,L]=_.default.useState(!1),R=d.getInstallableMcpConnectionOption(e),z=R?.transport,B=k||D||I,V=(e,t)=>{M(n=>({...n,values:{...n.values,[e]:t},errors:{...n.errors,[e]:null}})),F(null)},H=(e,t)=>{M(n=>({...n,savedAsSecret:{...n.savedAsSecret,[e]:t}}))},U=()=>{let e=R?.auth.credentialSecretName,t=N.current.values.api_key?.trim();return!e||!t||!N.current.savedAsSecret.api_key?Promise.resolve():A([{key:e,label:R.auth.credentialLabel??e,type:`password`}],{[e]:t},{[e]:!0})},W=()=>z?.kind===`stdio`?A(z.envFields??[],N.current.values,N.current.savedAsSecret):z?.kind===`shttp`||z?.kind===`sse`?U():Promise.resolve(),G=e=>{switch(e.error_kind){case`timeout`:return T(n.I18nKey.MCP$TEST_ERROR_TIMEOUT);case`connection`:return T(n.I18nKey.MCP$TEST_ERROR_CONNECTION);case`credentials`:return T(n.I18nKey.MCP$TEST_ERROR_CREDENTIALS,{error:e.error});default:return T(n.I18nKey.MCP$TEST_ERROR_UNKNOWN,{error:e.error})}},K=t=>{O(t,{onSuccess:a=>{if(!a.ok){F(G(a));return}E(t,{onSuccess:()=>{i.displaySuccessToast(T(n.I18nKey.MCP$INSTALL_SUCCESS)),L(!0),(async()=>{try{await W()}finally{w?.(e),C()}})()},onError:e=>{F(r.retrieveAxiosErrorMessage(e)||T(n.I18nKey.ERROR$GENERIC))}})},onError:e=>{F(r.retrieveAxiosErrorMessage(e)||T(n.I18nKey.ERROR$GENERIC))}})},q=()=>{if(z?.kind!==`shttp`&&z?.kind!==`sse`||!R)return;let e=j.values.api_key?.trim()??``,t=b(R);if(t&&!x(R)&&!e){M(e=>({...e,errors:{api_key:T(n.I18nKey.MCP$ERROR_FIELD_REQUIRED)}}));return}K({id:`${z.kind}-${a.default()}`,type:z.kind,url:z.url,...t&&e&&{api_key:e}})},J=()=>{if(z?.kind!==`stdio`)return;let e=z,t={};for(let r of e.envFields??[])r.required&&!(j.values[r.key]??``).trim()&&(t[r.key]=T(n.I18nKey.MCP$ERROR_FIELD_REQUIRED));for(let r of e.argFields??[])r.required&&!(j.values[r.key]??``).trim()&&(t[r.key]=T(n.I18nKey.MCP$ERROR_FIELD_REQUIRED));if(Object.values(t).some(Boolean)){M(e=>({...e,errors:t}));return}let r={};for(let t of e.envFields??[]){let e=j.values[t.key]?.trim();e&&(r[t.key]=e)}let i=[];for(let t of e.argFields??[]){let e=j.values[t.key]?.trim();if(e)for(let t of e.split(/\s+/))t&&i.push(t)}K({id:`stdio-${a.default()}`,type:`stdio`,name:e.serverName,command:e.command,args:[...e.args,...i],...Object.keys(r).length>0&&{env:r}})};return(0,v.jsx)(o.ModalBackdrop,{onClose:C,"aria-label":e.name,children:(0,v.jsxs)(`form`,{"data-testid":`mcp-install-modal`,"data-marketplace-id":e.id,onSubmit:e=>(e.preventDefault(),F(null),z?.kind===`shttp`||z?.kind===`sse`?q():J()),className:`relative bg-base-secondary p-6 rounded-xl flex flex-col gap-4 border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[85vh] overflow-y-auto custom-scrollbar`,children:[(0,v.jsx)(c.ModalCloseButton,{onClose:C,testId:`mcp-install-modal-close`,disabled:B}),(0,v.jsxs)(`div`,{className:`flex items-start gap-3 pr-6`,children:[(0,v.jsx)(f.McpLogoBadge,{entry:e}),(0,v.jsxs)(`div`,{className:`flex flex-col flex-1`,children:[(0,v.jsx)(`h2`,{className:s.modalTitleLgClassName,children:e.name}),(0,v.jsx)(`p`,{className:`text-xs text-tertiary-light`,children:e.description})]})]}),e.installHint&&(0,v.jsx)(`p`,{className:`text-xs text-tertiary-light`,children:e.installHint}),e.docsUrl&&(0,v.jsx)(`a`,{href:e.docsUrl,target:`_blank`,rel:`noreferrer`,className:`text-xs text-[var(--oh-muted)] hover:text-white hover:underline self-start transition-colors`,children:T(n.I18nKey.MCP$VIEW_DOCS)}),(0,v.jsx)(`div`,{className:`flex flex-col gap-3`,children:(()=>{if(z?.kind===`shttp`||z?.kind===`sse`){let e=b(R),t=R?x(R):!1,r=R?.auth.credentialSecretName;return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-url`,name:`url`,type:`url`,label:T(n.I18nKey.SETTINGS$MCP_URL),value:z.url,onChange:()=>{},isDisabled:!0,className:`w-full`}),e?(0,v.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-api_key`,name:`api_key`,type:`password`,label:R?.auth.credentialLabel??T(n.I18nKey.SETTINGS$MCP_API_KEY),value:j.values.api_key??``,onChange:e=>V(`api_key`,e),placeholder:R?.auth.credentialPlaceholder??T(n.I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER),showOptionalTag:t,required:!t,className:`w-full`}),R?.auth.credentialHelp&&(0,v.jsx)(`p`,{className:`text-xs text-tertiary-alt`,children:y(R.auth.credentialHelp)}),j.errors.api_key&&(0,v.jsx)(`p`,{className:`text-xs text-red-500`,children:j.errors.api_key}),r&&(0,v.jsx)(p.SaveAsSecretToggle,{fieldKey:r,checked:j.savedAsSecret.api_key??!1,onToggle:e=>H(`api_key`,e)})]}):null]})}if(z?.kind!==`stdio`)return null;let e=z;return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-command-readonly`,name:`command-readonly`,type:`text`,label:T(n.I18nKey.MCP$COMMAND_LABEL),value:`${e.command} ${e.args.join(` `)}`.trim(),onChange:()=>{},isDisabled:!0,className:`w-full`}),(e.envFields??[]).map(e=>(0,v.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-${e.key}`,name:e.key,type:e.type===`password`?`password`:`text`,label:e.label,value:j.values[e.key]??``,onChange:t=>V(e.key,t),placeholder:e.placeholder,required:e.required,showOptionalTag:!e.required,className:`w-full`}),e.helperText&&(0,v.jsx)(`p`,{className:`text-xs text-tertiary-alt`,children:y(e.helperText)}),j.errors[e.key]&&(0,v.jsx)(`p`,{className:`text-xs text-red-500`,children:j.errors[e.key]}),e.key in j.savedAsSecret&&(0,v.jsx)(p.SaveAsSecretToggle,{fieldKey:e.key,checked:j.savedAsSecret[e.key],onToggle:t=>H(e.key,t)})]},e.key)),(e.argFields??[]).map(e=>(0,v.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,v.jsx)(u.SettingsInput,{testId:`mcp-install-field-${e.key}`,name:e.key,type:e.type===`password`?`password`:`text`,label:e.label,value:j.values[e.key]??``,onChange:t=>V(e.key,t),placeholder:e.placeholder,required:e.required,showOptionalTag:!e.required,className:`w-full`}),e.helperText&&(0,v.jsx)(`p`,{className:`text-xs text-tertiary-alt`,children:y(e.helperText)}),j.errors[e.key]&&(0,v.jsx)(`p`,{className:`text-xs text-red-500`,children:j.errors[e.key]})]},e.key))]})})()}),P&&(0,v.jsx)(`p`,{"data-testid":`mcp-install-modal-error`,className:`text-sm text-red-500 whitespace-pre-wrap`,children:P}),(0,v.jsxs)(`div`,{className:`flex justify-end gap-2 mt-2`,children:[(0,v.jsx)(l.BrandButton,{type:`button`,variant:`secondary`,onClick:C,testId:`mcp-install-cancel`,isDisabled:B,children:T(n.I18nKey.BUTTON$CANCEL)}),(0,v.jsx)(l.BrandButton,{type:`submit`,variant:`primary`,isDisabled:B,testId:`mcp-install-submit`,children:T(k?n.I18nKey.MCP$VERIFYING:D||I?n.I18nKey.SETTINGS$SAVING:n.I18nKey.MCP$INSTALL_BUTTON)})]})]})})}exports.InstallServerModal=C;
2
2
  //# sourceMappingURL=install-server-modal.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"install-server-modal.cjs","names":[],"sources":["../../../../src/components/features/mcp-page/install-server-modal.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { AxiosError } from \"axios\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { MCPTestFailure } from \"@openhands/typescript-client\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalCloseButton } from \"#/components/shared/modals/modal-close-button\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { SettingsInput } from \"#/components/features/settings/settings-input\";\nimport { SaveAsSecretToggle } from \"#/components/features/mcp-page/save-as-secret-toggle\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport type { IntegrationCatalogEntry as MarketplaceEntry } from \"@openhands/extensions/integrations\";\nimport { McpLogoBadge } from \"#/components/features/mcp-logo-badge\";\nimport { MCPServerConfig } from \"#/types/mcp-server\";\nimport { useAddMcpServer } from \"#/hooks/mutation/use-add-mcp-server\";\nimport { useTestMcpServer } from \"#/hooks/mutation/use-test-mcp-server\";\nimport { displaySuccessToast } from \"#/utils/custom-toast-handlers\";\nimport {\n getInstallableMcpConnectionOption,\n type McpMarketplaceConnectionOption,\n} from \"#/utils/mcp-marketplace-utils\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { useSaveFieldsAsSecrets } from \"#/hooks/mutation/use-save-fields-as-secrets\";\nimport { modalTitleLgClassName } from \"#/utils/modal-classes\";\n\n/**\n * Renders a helperText string as React nodes, converting any `[text](url)`\n * markdown links into real `<a>` elements. Plain text segments are left as-is.\n * Only `http:` and `https:` URLs are rendered as links; anything else falls\n * back to `#` to guard against `javascript:` / `data:` XSS vectors.\n */\nfunction renderHelperText(text: string): React.ReactNode {\n const linkPattern = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n const parts: React.ReactNode[] = [];\n let lastIndex = 0;\n for (const match of text.matchAll(linkPattern)) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n parts.push(\n <a\n key={match.index}\n href={/^https?:\\/\\//i.test(match[2]) ? match[2] : \"#\"}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline hover:text-white transition-colors\"\n >\n {match[1]}\n </a>,\n );\n lastIndex = match.index + match[0].length;\n }\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n return parts;\n}\n\ninterface InstallServerModalProps {\n entry: MarketplaceEntry;\n onClose: () => void;\n onSuccess?: (entry: MarketplaceEntry) => void;\n}\n\ninterface FieldState {\n values: Record<string, string>;\n errors: Record<string, string | null>;\n savedAsSecret: Record<string, boolean>;\n}\n\nfunction optionNeedsCredentialField(\n option: McpMarketplaceConnectionOption | undefined,\n): boolean {\n if (option?.transport.kind !== \"shttp\" && option?.transport.kind !== \"sse\") {\n return false;\n }\n return [\"api_key\", \"bearer\", \"basic\"].includes(option.auth.strategy);\n}\n\nfunction isCredentialOptional(option: McpMarketplaceConnectionOption): boolean {\n if (option.transport.kind === \"stdio\") {\n return option.auth.apiKeyOptional ?? false;\n }\n return option.auth.apiKeyOptional ?? option.transport.apiKeyOptional ?? false;\n}\n\nfunction makeInitialState(entry: MarketplaceEntry): FieldState {\n const values: Record<string, string> = {};\n const savedAsSecret: Record<string, boolean> = {};\n const option = getInstallableMcpConnectionOption(entry);\n const template = option?.transport;\n if (template?.kind === \"stdio\") {\n for (const field of template.envFields ?? []) {\n values[field.key] = \"\";\n // Pre-check password fields; non-password fields default to off.\n savedAsSecret[field.key] = field.type === \"password\";\n }\n for (const field of template.argFields ?? []) {\n values[field.key] = \"\";\n }\n } else if (optionNeedsCredentialField(option)) {\n values.api_key = \"\";\n }\n return { values, errors: {}, savedAsSecret };\n}\n\n// The marketplace install modal is intentionally add-only: clicking\n// a catalog tile always appends a new server (the user might want\n// two Slack workspaces, two Postgres connections, etc.) even when\n// one of the same template kind is already installed. Editing an\n// existing server is reached via the installed-server-card's edit\n// button, which opens `CustomServerEditor` instead.\nexport function InstallServerModal({\n entry,\n onClose,\n onSuccess,\n}: InstallServerModalProps) {\n const { t } = useTranslation(\"openhands\");\n const { mutate: addMcpServer, isPending: isAdding } = useAddMcpServer();\n const { mutate: testMcpServer, isPending: isTesting } = useTestMcpServer();\n const saveFieldsAsSecrets = useSaveFieldsAsSecrets();\n\n const [state, setState] = React.useState<FieldState>(() =>\n makeInitialState(entry),\n );\n // Always holds the latest state so async callbacks (onSuccess) never read\n // stale closure values, even under React concurrent-mode scheduling.\n const stateRef = React.useRef(state);\n stateRef.current = state;\n\n const [globalError, setGlobalError] = React.useState<string | null>(null);\n const option = getInstallableMcpConnectionOption(entry);\n const template = option?.transport;\n\n const isPending = isTesting || isAdding;\n\n const setValue = (key: string, value: string) => {\n setState((prev) => ({\n ...prev,\n values: { ...prev.values, [key]: value },\n errors: { ...prev.errors, [key]: null },\n }));\n setGlobalError(null);\n };\n\n const toggleSecret = (key: string, value: boolean) => {\n setState((prev) => ({\n ...prev,\n savedAsSecret: { ...prev.savedAsSecret, [key]: value },\n }));\n };\n\n const makeTestErrorMessage = (failure: MCPTestFailure): string => {\n switch (failure.error_kind) {\n case \"timeout\":\n return t(I18nKey.MCP$TEST_ERROR_TIMEOUT);\n case \"connection\":\n return t(I18nKey.MCP$TEST_ERROR_CONNECTION);\n default:\n return t(I18nKey.MCP$TEST_ERROR_UNKNOWN, { error: failure.error });\n }\n };\n\n const submitServer = (payload: MCPServerConfig) => {\n testMcpServer(payload, {\n onSuccess: (result) => {\n if (!result.ok) {\n setGlobalError(makeTestErrorMessage(result));\n // Modal stays open — do NOT call onClose.\n return;\n }\n addMcpServer(payload, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$INSTALL_SUCCESS));\n onSuccess?.(entry);\n onClose();\n\n // Save checked envFields as secrets in the background so the\n // Automation Server can access them without a separate manual step.\n // Runs after onClose so failures don't block the modal from closing.\n // Uses stateRef.current to avoid reading a stale closure snapshot.\n if (template?.kind === \"stdio\") {\n saveFieldsAsSecrets(\n template.envFields ?? [],\n stateRef.current.values,\n stateRef.current.savedAsSecret,\n );\n }\n },\n onError: (err: unknown) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n setGlobalError(message || t(I18nKey.ERROR$GENERIC));\n },\n });\n },\n onError: (err: unknown) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n setGlobalError(message || t(I18nKey.ERROR$GENERIC));\n },\n });\n };\n\n // ------------------------------------------------------------------\n // Per-template submit handlers. Each is small and self-contained:\n // validate user input, build the payload, then hand off to\n // submitServer.\n // ------------------------------------------------------------------\n const handleHttpServerSubmit = () => {\n // TS narrows this branch to shttp|sse; the equality guard is a\n // runtime/defensive belt to make the helper safe in isolation.\n if (template?.kind !== \"shttp\" && template?.kind !== \"sse\") {\n return;\n }\n if (!option) return;\n const apiKey = state.values.api_key?.trim() ?? \"\";\n const needsCredential = optionNeedsCredentialField(option);\n if (needsCredential && !isCredentialOptional(option) && !apiKey) {\n setState((prev) => ({\n ...prev,\n errors: { api_key: t(I18nKey.MCP$ERROR_FIELD_REQUIRED) },\n }));\n return;\n }\n const payload: MCPServerConfig = {\n id: `${template.kind}-${uuidv4()}`,\n type: template.kind,\n url: template.url,\n ...(needsCredential && apiKey && { api_key: apiKey }),\n };\n submitServer(payload);\n };\n\n const handleStdioSubmit = () => {\n if (template?.kind !== \"stdio\") return;\n const stdio = template;\n const errors: Record<string, string | null> = {};\n\n for (const field of stdio.envFields ?? []) {\n if (field.required && !(state.values[field.key] ?? \"\").trim()) {\n errors[field.key] = t(I18nKey.MCP$ERROR_FIELD_REQUIRED);\n }\n }\n for (const field of stdio.argFields ?? []) {\n if (field.required && !(state.values[field.key] ?? \"\").trim()) {\n errors[field.key] = t(I18nKey.MCP$ERROR_FIELD_REQUIRED);\n }\n }\n if (Object.values(errors).some(Boolean)) {\n setState((prev) => ({ ...prev, errors }));\n return;\n }\n\n const env: Record<string, string> = {};\n for (const field of stdio.envFields ?? []) {\n const v = state.values[field.key]?.trim();\n if (v) env[field.key] = v;\n }\n const extraArgs: string[] = [];\n for (const field of stdio.argFields ?? []) {\n const v = state.values[field.key]?.trim();\n if (v) {\n // Filesystem-style multi-token input: split on whitespace.\n for (const token of v.split(/\\s+/)) {\n if (token) extraArgs.push(token);\n }\n }\n }\n\n const payload: MCPServerConfig = {\n id: `stdio-${uuidv4()}`,\n type: \"stdio\",\n name: stdio.serverName,\n command: stdio.command,\n args: [...stdio.args, ...extraArgs],\n ...(Object.keys(env).length > 0 && { env }),\n };\n submitServer(payload);\n };\n\n const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setGlobalError(null);\n if (template?.kind === \"shttp\" || template?.kind === \"sse\") {\n return handleHttpServerSubmit();\n }\n return handleStdioSubmit();\n };\n\n const renderFields = () => {\n if (template?.kind === \"shttp\" || template?.kind === \"sse\") {\n const shouldRenderCredential = optionNeedsCredentialField(option);\n const apiKeyOptional = option ? isCredentialOptional(option) : false;\n return (\n <>\n <SettingsInput\n testId=\"mcp-install-field-url\"\n name=\"url\"\n type=\"url\"\n label={t(I18nKey.SETTINGS$MCP_URL)}\n value={template.url}\n onChange={() => {}}\n isDisabled\n className=\"w-full\"\n />\n {shouldRenderCredential ? (\n <div className=\"flex flex-col gap-1\">\n <SettingsInput\n testId=\"mcp-install-field-api_key\"\n name=\"api_key\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_API_KEY)}\n value={state.values.api_key ?? \"\"}\n onChange={(v) => setValue(\"api_key\", v)}\n placeholder={t(I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER)}\n showOptionalTag={apiKeyOptional}\n required={!apiKeyOptional}\n className=\"w-full\"\n />\n {state.errors.api_key && (\n <p className=\"text-xs text-red-500\">{state.errors.api_key}</p>\n )}\n </div>\n ) : null}\n </>\n );\n }\n\n if (template?.kind !== \"stdio\") return null;\n const stdio = template;\n return (\n <>\n <SettingsInput\n testId=\"mcp-install-field-command-readonly\"\n name=\"command-readonly\"\n type=\"text\"\n label={t(I18nKey.MCP$COMMAND_LABEL)}\n value={`${stdio.command} ${stdio.args.join(\" \")}`.trim()}\n onChange={() => {}}\n isDisabled\n className=\"w-full\"\n />\n {(stdio.envFields ?? []).map((field) => (\n <div key={field.key} className=\"flex flex-col gap-1\">\n <SettingsInput\n testId={`mcp-install-field-${field.key}`}\n name={field.key}\n type={field.type === \"password\" ? \"password\" : \"text\"}\n label={field.label}\n value={state.values[field.key] ?? \"\"}\n onChange={(v) => setValue(field.key, v)}\n placeholder={field.placeholder}\n required={field.required}\n showOptionalTag={!field.required}\n className=\"w-full\"\n />\n {field.helperText && (\n <p className=\"text-xs text-tertiary-alt\">\n {renderHelperText(field.helperText)}\n </p>\n )}\n {state.errors[field.key] && (\n <p className=\"text-xs text-red-500\">{state.errors[field.key]}</p>\n )}\n {field.key in state.savedAsSecret && (\n <SaveAsSecretToggle\n fieldKey={field.key}\n checked={state.savedAsSecret[field.key]}\n onToggle={(v) => toggleSecret(field.key, v)}\n />\n )}\n </div>\n ))}\n {/* argFields are CLI arguments, not credentials — they don't need\n a \"save as secret\" toggle and are excluded from savedAsSecret. */}\n {(stdio.argFields ?? []).map((field) => (\n <div key={field.key} className=\"flex flex-col gap-1\">\n <SettingsInput\n testId={`mcp-install-field-${field.key}`}\n name={field.key}\n type={field.type === \"password\" ? \"password\" : \"text\"}\n label={field.label}\n value={state.values[field.key] ?? \"\"}\n onChange={(v) => setValue(field.key, v)}\n placeholder={field.placeholder}\n required={field.required}\n showOptionalTag={!field.required}\n className=\"w-full\"\n />\n {field.helperText && (\n <p className=\"text-xs text-tertiary-alt\">\n {renderHelperText(field.helperText)}\n </p>\n )}\n {state.errors[field.key] && (\n <p className=\"text-xs text-red-500\">{state.errors[field.key]}</p>\n )}\n </div>\n ))}\n </>\n );\n };\n\n return (\n <ModalBackdrop onClose={onClose} aria-label={entry.name}>\n <form\n data-testid=\"mcp-install-modal\"\n data-marketplace-id={entry.id}\n onSubmit={handleSubmit}\n className=\"relative bg-base-secondary p-6 rounded-xl flex flex-col gap-4 border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[85vh] overflow-y-auto custom-scrollbar\"\n >\n <ModalCloseButton\n onClose={onClose}\n testId=\"mcp-install-modal-close\"\n disabled={isPending}\n />\n <div className=\"flex items-start gap-3 pr-6\">\n <McpLogoBadge entry={entry} />\n <div className=\"flex flex-col flex-1\">\n <h2 className={modalTitleLgClassName}>{entry.name}</h2>\n <p className=\"text-xs text-tertiary-light\">{entry.description}</p>\n </div>\n </div>\n\n {entry.installHint && (\n <p className=\"text-xs text-tertiary-light\">{entry.installHint}</p>\n )}\n\n {entry.docsUrl && (\n <a\n href={entry.docsUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-xs text-[var(--oh-muted)] hover:text-white hover:underline self-start transition-colors\"\n >\n {t(I18nKey.MCP$VIEW_DOCS)}\n </a>\n )}\n\n <div className=\"flex flex-col gap-3\">{renderFields()}</div>\n\n {globalError && (\n <p\n data-testid=\"mcp-install-modal-error\"\n className=\"text-sm text-red-500 whitespace-pre-wrap\"\n >\n {globalError}\n </p>\n )}\n\n <div className=\"flex justify-end gap-2 mt-2\">\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={onClose}\n testId=\"mcp-install-cancel\"\n >\n {t(I18nKey.BUTTON$CANCEL)}\n </BrandButton>\n <BrandButton\n type=\"submit\"\n variant=\"primary\"\n isDisabled={isPending}\n testId=\"mcp-install-submit\"\n >\n {isTesting\n ? t(I18nKey.MCP$VERIFYING)\n : isAdding\n ? t(I18nKey.SETTINGS$SAVING)\n : t(I18nKey.MCP$INSTALL_BUTTON)}\n </BrandButton>\n </div>\n </form>\n </ModalBackdrop>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"install-server-modal.cjs","names":[],"sources":["../../../../src/components/features/mcp-page/install-server-modal.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { AxiosError } from \"axios\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalCloseButton } from \"#/components/shared/modals/modal-close-button\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { SettingsInput } from \"#/components/features/settings/settings-input\";\nimport { SaveAsSecretToggle } from \"#/components/features/mcp-page/save-as-secret-toggle\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport type {\n IntegrationCatalogEntry as MarketplaceEntry,\n MarketplaceField,\n} from \"@openhands/extensions/integrations\";\nimport { McpLogoBadge } from \"#/components/features/mcp-logo-badge\";\nimport { ExtendedMCPTestFailure, MCPServerConfig } from \"#/types/mcp-server\";\nimport { useAddMcpServer } from \"#/hooks/mutation/use-add-mcp-server\";\nimport { useTestMcpServer } from \"#/hooks/mutation/use-test-mcp-server\";\nimport { displaySuccessToast } from \"#/utils/custom-toast-handlers\";\nimport {\n getInstallableMcpConnectionOption,\n type McpMarketplaceConnectionOption,\n} from \"#/utils/mcp-marketplace-utils\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { useSaveFieldsAsSecrets } from \"#/hooks/mutation/use-save-fields-as-secrets\";\nimport { modalTitleLgClassName } from \"#/utils/modal-classes\";\n\n/**\n * Renders a helperText string as React nodes, converting any `[text](url)`\n * markdown links into real `<a>` elements. Plain text segments are left as-is.\n * Only `http:` and `https:` URLs are rendered as links; anything else falls\n * back to `#` to guard against `javascript:` / `data:` XSS vectors.\n */\nfunction renderHelperText(text: string): React.ReactNode {\n const linkPattern = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n const parts: React.ReactNode[] = [];\n let lastIndex = 0;\n for (const match of text.matchAll(linkPattern)) {\n if (match.index > lastIndex) {\n parts.push(text.slice(lastIndex, match.index));\n }\n parts.push(\n <a\n key={match.index}\n href={/^https?:\\/\\//i.test(match[2]) ? match[2] : \"#\"}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline hover:text-white transition-colors\"\n >\n {match[1]}\n </a>,\n );\n lastIndex = match.index + match[0].length;\n }\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n return parts;\n}\n\ninterface InstallServerModalProps {\n entry: MarketplaceEntry;\n onClose: () => void;\n onSuccess?: (entry: MarketplaceEntry) => void;\n}\n\ninterface FieldState {\n values: Record<string, string>;\n errors: Record<string, string | null>;\n savedAsSecret: Record<string, boolean>;\n}\n\nfunction optionNeedsCredentialField(\n option: McpMarketplaceConnectionOption | undefined,\n): boolean {\n if (option?.transport.kind !== \"shttp\" && option?.transport.kind !== \"sse\") {\n return false;\n }\n return [\"api_key\", \"bearer\", \"basic\"].includes(option.auth.strategy);\n}\n\nfunction isCredentialOptional(option: McpMarketplaceConnectionOption): boolean {\n if (option.transport.kind === \"stdio\") {\n return option.auth.apiKeyOptional ?? false;\n }\n return option.auth.apiKeyOptional ?? option.transport.apiKeyOptional ?? false;\n}\n\nfunction makeInitialState(entry: MarketplaceEntry): FieldState {\n const values: Record<string, string> = {};\n const savedAsSecret: Record<string, boolean> = {};\n const option = getInstallableMcpConnectionOption(entry);\n const template = option?.transport;\n if (template?.kind === \"stdio\") {\n for (const field of template.envFields ?? []) {\n values[field.key] = \"\";\n // Pre-check password fields; non-password fields default to off.\n savedAsSecret[field.key] = field.type === \"password\";\n }\n for (const field of template.argFields ?? []) {\n values[field.key] = \"\";\n }\n } else if (optionNeedsCredentialField(option)) {\n values.api_key = \"\";\n if (option?.auth.credentialSecretName) {\n savedAsSecret.api_key =\n option.auth.saveCredentialAsSecretByDefault ?? false;\n }\n }\n return { values, errors: {}, savedAsSecret };\n}\n\n// The marketplace install modal is intentionally add-only: clicking\n// a catalog tile always appends a new server (the user might want\n// two Slack workspaces, two Postgres connections, etc.) even when\n// one of the same template kind is already installed. Editing an\n// existing server is reached via the installed-server-card's edit\n// button, which opens `CustomServerEditor` instead.\nexport function InstallServerModal({\n entry,\n onClose,\n onSuccess,\n}: InstallServerModalProps) {\n const { t } = useTranslation(\"openhands\");\n const { mutate: addMcpServer, isPending: isAdding } = useAddMcpServer();\n const { mutate: testMcpServer, isPending: isTesting } = useTestMcpServer();\n const saveFieldsAsSecrets = useSaveFieldsAsSecrets();\n\n const [state, setState] = React.useState<FieldState>(() =>\n makeInitialState(entry),\n );\n // Always holds the latest state so async callbacks (onSuccess) never read\n // stale closure values, even under React concurrent-mode scheduling.\n const stateRef = React.useRef(state);\n stateRef.current = state;\n\n const [globalError, setGlobalError] = React.useState<string | null>(null);\n const [isFinalizingInstall, setIsFinalizingInstall] = React.useState(false);\n const option = getInstallableMcpConnectionOption(entry);\n const template = option?.transport;\n\n const isPending = isTesting || isAdding || isFinalizingInstall;\n\n const setValue = (key: string, value: string) => {\n setState((prev) => ({\n ...prev,\n values: { ...prev.values, [key]: value },\n errors: { ...prev.errors, [key]: null },\n }));\n setGlobalError(null);\n };\n\n const toggleSecret = (key: string, value: boolean) => {\n setState((prev) => ({\n ...prev,\n savedAsSecret: { ...prev.savedAsSecret, [key]: value },\n }));\n };\n\n const saveHostedCredentialAsSecret = (): Promise<void> => {\n const secretName = option?.auth.credentialSecretName;\n const apiKey = stateRef.current.values.api_key?.trim();\n if (!secretName || !apiKey || !stateRef.current.savedAsSecret.api_key) {\n return Promise.resolve();\n }\n\n const field: MarketplaceField = {\n key: secretName,\n label: option.auth.credentialLabel ?? secretName,\n type: \"password\",\n };\n return saveFieldsAsSecrets(\n [field],\n { [secretName]: apiKey },\n { [secretName]: true },\n );\n };\n\n const saveSelectedSecrets = (): Promise<void> => {\n if (template?.kind === \"stdio\") {\n return saveFieldsAsSecrets(\n template.envFields ?? [],\n stateRef.current.values,\n stateRef.current.savedAsSecret,\n );\n }\n if (template?.kind === \"shttp\" || template?.kind === \"sse\") {\n return saveHostedCredentialAsSecret();\n }\n return Promise.resolve();\n };\n\n const makeTestErrorMessage = (failure: ExtendedMCPTestFailure): string => {\n switch (failure.error_kind) {\n case \"timeout\":\n return t(I18nKey.MCP$TEST_ERROR_TIMEOUT);\n case \"connection\":\n return t(I18nKey.MCP$TEST_ERROR_CONNECTION);\n case \"credentials\":\n return t(I18nKey.MCP$TEST_ERROR_CREDENTIALS, { error: failure.error });\n default:\n return t(I18nKey.MCP$TEST_ERROR_UNKNOWN, { error: failure.error });\n }\n };\n\n const submitServer = (payload: MCPServerConfig) => {\n testMcpServer(payload, {\n onSuccess: (result) => {\n if (!result.ok) {\n setGlobalError(makeTestErrorMessage(result));\n // Modal stays open — do NOT call onClose.\n return;\n }\n addMcpServer(payload, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$INSTALL_SUCCESS));\n setIsFinalizingInstall(true);\n void (async () => {\n try {\n await saveSelectedSecrets();\n } finally {\n onSuccess?.(entry);\n onClose();\n }\n })();\n },\n onError: (err: unknown) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n setGlobalError(message || t(I18nKey.ERROR$GENERIC));\n },\n });\n },\n onError: (err: unknown) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n setGlobalError(message || t(I18nKey.ERROR$GENERIC));\n },\n });\n };\n\n // ------------------------------------------------------------------\n // Per-template submit handlers. Each is small and self-contained:\n // validate user input, build the payload, then hand off to\n // submitServer.\n // ------------------------------------------------------------------\n const handleHttpServerSubmit = () => {\n // TS narrows this branch to shttp|sse; the equality guard is a\n // runtime/defensive belt to make the helper safe in isolation.\n if (template?.kind !== \"shttp\" && template?.kind !== \"sse\") {\n return;\n }\n if (!option) return;\n const apiKey = state.values.api_key?.trim() ?? \"\";\n const needsCredential = optionNeedsCredentialField(option);\n if (needsCredential && !isCredentialOptional(option) && !apiKey) {\n setState((prev) => ({\n ...prev,\n errors: { api_key: t(I18nKey.MCP$ERROR_FIELD_REQUIRED) },\n }));\n return;\n }\n const payload: MCPServerConfig = {\n id: `${template.kind}-${uuidv4()}`,\n type: template.kind,\n url: template.url,\n ...(needsCredential && apiKey && { api_key: apiKey }),\n };\n submitServer(payload);\n };\n\n const handleStdioSubmit = () => {\n if (template?.kind !== \"stdio\") return;\n const stdio = template;\n const errors: Record<string, string | null> = {};\n\n for (const field of stdio.envFields ?? []) {\n if (field.required && !(state.values[field.key] ?? \"\").trim()) {\n errors[field.key] = t(I18nKey.MCP$ERROR_FIELD_REQUIRED);\n }\n }\n for (const field of stdio.argFields ?? []) {\n if (field.required && !(state.values[field.key] ?? \"\").trim()) {\n errors[field.key] = t(I18nKey.MCP$ERROR_FIELD_REQUIRED);\n }\n }\n if (Object.values(errors).some(Boolean)) {\n setState((prev) => ({ ...prev, errors }));\n return;\n }\n\n const env: Record<string, string> = {};\n for (const field of stdio.envFields ?? []) {\n const v = state.values[field.key]?.trim();\n if (v) env[field.key] = v;\n }\n const extraArgs: string[] = [];\n for (const field of stdio.argFields ?? []) {\n const v = state.values[field.key]?.trim();\n if (v) {\n // Filesystem-style multi-token input: split on whitespace.\n for (const token of v.split(/\\s+/)) {\n if (token) extraArgs.push(token);\n }\n }\n }\n\n const payload: MCPServerConfig = {\n id: `stdio-${uuidv4()}`,\n type: \"stdio\",\n name: stdio.serverName,\n command: stdio.command,\n args: [...stdio.args, ...extraArgs],\n ...(Object.keys(env).length > 0 && { env }),\n };\n submitServer(payload);\n };\n\n const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setGlobalError(null);\n if (template?.kind === \"shttp\" || template?.kind === \"sse\") {\n return handleHttpServerSubmit();\n }\n return handleStdioSubmit();\n };\n\n const renderFields = () => {\n if (template?.kind === \"shttp\" || template?.kind === \"sse\") {\n const shouldRenderCredential = optionNeedsCredentialField(option);\n const apiKeyOptional = option ? isCredentialOptional(option) : false;\n const credentialSecretName = option?.auth.credentialSecretName;\n return (\n <>\n <SettingsInput\n testId=\"mcp-install-field-url\"\n name=\"url\"\n type=\"url\"\n label={t(I18nKey.SETTINGS$MCP_URL)}\n value={template.url}\n onChange={() => {}}\n isDisabled\n className=\"w-full\"\n />\n {shouldRenderCredential ? (\n <div className=\"flex flex-col gap-1\">\n <SettingsInput\n testId=\"mcp-install-field-api_key\"\n name=\"api_key\"\n type=\"password\"\n label={\n option?.auth.credentialLabel ??\n t(I18nKey.SETTINGS$MCP_API_KEY)\n }\n value={state.values.api_key ?? \"\"}\n onChange={(v) => setValue(\"api_key\", v)}\n placeholder={\n option?.auth.credentialPlaceholder ??\n t(I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER)\n }\n showOptionalTag={apiKeyOptional}\n required={!apiKeyOptional}\n className=\"w-full\"\n />\n {option?.auth.credentialHelp && (\n <p className=\"text-xs text-tertiary-alt\">\n {renderHelperText(option.auth.credentialHelp)}\n </p>\n )}\n {state.errors.api_key && (\n <p className=\"text-xs text-red-500\">{state.errors.api_key}</p>\n )}\n {credentialSecretName && (\n <SaveAsSecretToggle\n fieldKey={credentialSecretName}\n checked={state.savedAsSecret.api_key ?? false}\n onToggle={(v) => toggleSecret(\"api_key\", v)}\n />\n )}\n </div>\n ) : null}\n </>\n );\n }\n\n if (template?.kind !== \"stdio\") return null;\n const stdio = template;\n return (\n <>\n <SettingsInput\n testId=\"mcp-install-field-command-readonly\"\n name=\"command-readonly\"\n type=\"text\"\n label={t(I18nKey.MCP$COMMAND_LABEL)}\n value={`${stdio.command} ${stdio.args.join(\" \")}`.trim()}\n onChange={() => {}}\n isDisabled\n className=\"w-full\"\n />\n {(stdio.envFields ?? []).map((field) => (\n <div key={field.key} className=\"flex flex-col gap-1\">\n <SettingsInput\n testId={`mcp-install-field-${field.key}`}\n name={field.key}\n type={field.type === \"password\" ? \"password\" : \"text\"}\n label={field.label}\n value={state.values[field.key] ?? \"\"}\n onChange={(v) => setValue(field.key, v)}\n placeholder={field.placeholder}\n required={field.required}\n showOptionalTag={!field.required}\n className=\"w-full\"\n />\n {field.helperText && (\n <p className=\"text-xs text-tertiary-alt\">\n {renderHelperText(field.helperText)}\n </p>\n )}\n {state.errors[field.key] && (\n <p className=\"text-xs text-red-500\">{state.errors[field.key]}</p>\n )}\n {field.key in state.savedAsSecret && (\n <SaveAsSecretToggle\n fieldKey={field.key}\n checked={state.savedAsSecret[field.key]}\n onToggle={(v) => toggleSecret(field.key, v)}\n />\n )}\n </div>\n ))}\n {/* argFields are CLI arguments, not credentials — they don't need\n a \"save as secret\" toggle and are excluded from savedAsSecret. */}\n {(stdio.argFields ?? []).map((field) => (\n <div key={field.key} className=\"flex flex-col gap-1\">\n <SettingsInput\n testId={`mcp-install-field-${field.key}`}\n name={field.key}\n type={field.type === \"password\" ? \"password\" : \"text\"}\n label={field.label}\n value={state.values[field.key] ?? \"\"}\n onChange={(v) => setValue(field.key, v)}\n placeholder={field.placeholder}\n required={field.required}\n showOptionalTag={!field.required}\n className=\"w-full\"\n />\n {field.helperText && (\n <p className=\"text-xs text-tertiary-alt\">\n {renderHelperText(field.helperText)}\n </p>\n )}\n {state.errors[field.key] && (\n <p className=\"text-xs text-red-500\">{state.errors[field.key]}</p>\n )}\n </div>\n ))}\n </>\n );\n };\n\n return (\n <ModalBackdrop onClose={onClose} aria-label={entry.name}>\n <form\n data-testid=\"mcp-install-modal\"\n data-marketplace-id={entry.id}\n onSubmit={handleSubmit}\n className=\"relative bg-base-secondary p-6 rounded-xl flex flex-col gap-4 border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[85vh] overflow-y-auto custom-scrollbar\"\n >\n <ModalCloseButton\n onClose={onClose}\n testId=\"mcp-install-modal-close\"\n disabled={isPending}\n />\n <div className=\"flex items-start gap-3 pr-6\">\n <McpLogoBadge entry={entry} />\n <div className=\"flex flex-col flex-1\">\n <h2 className={modalTitleLgClassName}>{entry.name}</h2>\n <p className=\"text-xs text-tertiary-light\">{entry.description}</p>\n </div>\n </div>\n\n {entry.installHint && (\n <p className=\"text-xs text-tertiary-light\">{entry.installHint}</p>\n )}\n\n {entry.docsUrl && (\n <a\n href={entry.docsUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-xs text-[var(--oh-muted)] hover:text-white hover:underline self-start transition-colors\"\n >\n {t(I18nKey.MCP$VIEW_DOCS)}\n </a>\n )}\n\n <div className=\"flex flex-col gap-3\">{renderFields()}</div>\n\n {globalError && (\n <p\n data-testid=\"mcp-install-modal-error\"\n className=\"text-sm text-red-500 whitespace-pre-wrap\"\n >\n {globalError}\n </p>\n )}\n\n <div className=\"flex justify-end gap-2 mt-2\">\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={onClose}\n testId=\"mcp-install-cancel\"\n isDisabled={isPending}\n >\n {t(I18nKey.BUTTON$CANCEL)}\n </BrandButton>\n <BrandButton\n type=\"submit\"\n variant=\"primary\"\n isDisabled={isPending}\n testId=\"mcp-install-submit\"\n >\n {isTesting\n ? t(I18nKey.MCP$VERIFYING)\n : isAdding || isFinalizingInstall\n ? t(I18nKey.SETTINGS$SAVING)\n : t(I18nKey.MCP$INSTALL_BUTTON)}\n </BrandButton>\n </div>\n </form>\n </ModalBackdrop>\n );\n}\n"],"mappings":"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"}