@openhands/agent-canvas 1.0.0-alpha.8 → 1.0.0-beta.1

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 (409) hide show
  1. package/README.md +17 -6
  2. package/bin/agent-canvas.mjs +22 -2
  3. package/build/assets/{QueryClientProvider-B7kl84Kj.js → QueryClientProvider-CkGuhXg-.js} +1 -1
  4. package/build/assets/{Trans-1j65oy9O.js → Trans-Cvm_-SMi.js} +1 -1
  5. package/build/assets/acp-providers-CbiRekh9.js +1 -0
  6. package/build/assets/{acp-route-guard-CQTmeJwM.js → acp-route-guard-B2yoBZ_4.js} +1 -1
  7. package/build/assets/{active-backend-context-TVbjnvmP.js → active-backend-context-cCM1vYYZ.js} +1 -1
  8. package/build/assets/add-backend-modal-DIUQzMPa.js +1 -0
  9. package/build/assets/agent-server-client-options-Bc5ZorQZ.js +1 -0
  10. package/build/assets/agent-server-compatibility-BlkUsrX2.js +1 -0
  11. package/build/assets/agent-server-conversation-service.api-DFvqqEDo.js +5 -0
  12. package/build/assets/{agent-settings-B247S9G3.js → agent-settings-CnGSCmK8.js} +1 -1
  13. package/build/assets/{alert-banner-BWoqueRw.js → alert-banner-DtzAX654.js} +1 -1
  14. package/build/assets/{analytics-consent-form-modal-C7sXfxRh.js → analytics-consent-form-modal-CHZ3I37v.js} +1 -1
  15. package/build/assets/api-key-entry-screen-B2gynaCp.js +1 -0
  16. package/build/assets/{app-settings-BVeSaty9.js → app-settings-Db9ITeJH.js} +1 -1
  17. package/build/assets/{automation-detail-g5-RZ0da.js → automation-detail-Di7EOIZD.js} +1 -1
  18. package/build/assets/{automations-list-DHoq_0MM.js → automations-list-IsIWdDiw.js} +1 -1
  19. package/build/assets/backend-form-modal-Dnk33xA_.js +1 -0
  20. package/build/assets/{backend-synced-settings-badge-nAfiUWvM.js → backend-synced-settings-badge-Dc6c7GT4.js} +1 -1
  21. package/build/assets/{base-modal-CQRvRHu1.js → base-modal-_dYTw1ri.js} +1 -1
  22. package/build/assets/{brand-button-C2nEKopC.js → brand-button-Br7f0kZJ.js} +1 -1
  23. package/build/assets/browser-D810xUYt.js +5 -0
  24. package/build/assets/browser-store-Couc4S5D.js +1 -0
  25. package/build/assets/browser-tab-B-aIqXRl.js +1 -0
  26. package/build/assets/{checkmark-BJJrZUF8.js → checkmark-DL7acQA7.js} +1 -1
  27. package/build/assets/{chevron-left-small-CSh-sE9L.js → chevron-left-small-CVWf8TI6.js} +1 -1
  28. package/build/assets/{circle-plus-check-toggle-qs8Va1cC.js → circle-plus-check-toggle-P7ZZToV4.js} +1 -1
  29. package/build/assets/{clock-ZR4Kn-_Y.js → clock-BRjCgHTc.js} +1 -1
  30. package/build/assets/{close-BdmyeRqS.js → close-B5LROHR3.js} +1 -1
  31. package/build/assets/{combobox-caret-B53O9Hsq.js → combobox-caret-to1O8irE.js} +1 -1
  32. package/build/assets/{condenser-settings-A35V3yng.js → condenser-settings-wnEKhBof.js} +1 -1
  33. package/build/assets/{confirmation-modal-C9-La0h3.js → confirmation-modal-Dau3w_sa.js} +1 -1
  34. package/build/assets/{context-menu-list-item-Buu9nc0q.js → context-menu-list-item-CWNFpuiC.js} +1 -1
  35. package/build/assets/conversation-HlncOV7n.js +19 -0
  36. package/build/assets/conversation-MtnkpqA9.js +1 -0
  37. package/build/assets/conversation-panel-DxnM6tRe.js +1 -0
  38. package/build/assets/{conversation-service.api-C8pYCyV6.js → conversation-service.api-nb5W1PqR.js} +1 -1
  39. package/build/assets/{conversation-tab-empty-state-D8dNvo-V.js → conversation-tab-empty-state-DyssnnWa.js} +1 -1
  40. package/build/assets/conversation-websocket-context-C8_PkGLi.js +3 -0
  41. package/build/assets/{copy-C7Ti2d8C.js → copy-DYgmUdIw.js} +1 -1
  42. package/build/assets/{custom-toast-handlers-BOc3qeQ7.js → custom-toast-handlers-C-SZFmto.js} +1 -1
  43. package/build/assets/declaration-BNMqORFE.js +1 -0
  44. package/build/assets/{device-verify-CMusn8nX.js → device-verify-DqDlphsG.js} +1 -1
  45. package/build/assets/{dist-DZHSA2e6.js → dist-C6t0EXL7.js} +1 -1
  46. package/build/assets/{edit-automation-modal-Dnjxbjn7.js → edit-automation-modal-BGzR3nfZ.js} +1 -1
  47. package/build/assets/{ellipsis-button-ugUATsNo.js → ellipsis-button-ZyLMPURn.js} +1 -1
  48. package/build/assets/{entry.client-D9uR9Blz.js → entry.client-1VMHpktY.js} +2 -2
  49. package/build/assets/{enum-filter-dropdown-1vpOGySB.js → enum-filter-dropdown-CEgCdu4A.js} +1 -1
  50. package/build/assets/{environment-switch-overlay-CTCTQikP.js → environment-switch-overlay-XL8yCGP6.js} +1 -1
  51. package/build/assets/{extensions-hub-BSUseHVF.js → extensions-hub-C651jsVh.js} +1 -1
  52. package/build/assets/{extensions-navigation-CT1kc1u_.js → extensions-navigation-BYR8Giqq.js} +1 -1
  53. package/build/assets/files-tab-BhnLgimi.js +1 -0
  54. package/build/assets/{folder-0WSMImNX.js → folder-ZZJVGgd7.js} +1 -1
  55. package/build/assets/git-control-bar-branch-button-M34A5_vX.js +27 -0
  56. package/build/assets/{git-provider-icon-DYE9n7fs.js → git-provider-icon-D5dCNy-k.js} +1 -1
  57. package/build/assets/home-CYQv7yc_.js +1 -0
  58. package/build/assets/{i18n-DjAGhTis.js → i18n-CTohRuoO.js} +1 -1
  59. package/build/assets/install-server-modal-f31_CLrW.js +1 -0
  60. package/build/assets/{launch-hZ0ifhcV.js → launch-DHEUYn2A.js} +1 -1
  61. package/build/assets/{lesson-plan-DRYG5SLI.js → lesson-plan-dH5Bj0pN.js} +1 -1
  62. package/build/assets/{link-external-Df8J52xI.js → link-external-D2POYx4c.js} +1 -1
  63. package/build/assets/{llm-client-ChQzg4wX.js → llm-client-DaH1TuyR.js} +1 -1
  64. package/build/assets/llm-settings-Bql-vydt.js +1 -0
  65. package/build/assets/llm-settings-C_tal6Ds.js +1 -0
  66. package/build/assets/{loading-spinner-C04FGh14.js → loading-spinner-BPtYORNK.js} +1 -1
  67. package/build/assets/{manage-backends-modal-rYeyGx7j.js → manage-backends-modal-l7RkKfwX.js} +1 -1
  68. package/build/assets/{manage-workspaces-modal-C5EuW8m1.js → manage-workspaces-modal-DhKF_8z3.js} +1 -1
  69. package/build/assets/manifest-9fee01b9.js +1 -0
  70. package/build/assets/{markdown-renderer-CEX4Becj.js → markdown-renderer-DMzf2i4x.js} +1 -1
  71. package/build/assets/mcp-D2onbwVk.js +9 -0
  72. package/build/assets/{messages-T2ewVkbp.js → messages-BMzyOW2V.js} +1 -1
  73. package/build/assets/{modal-backdrop-DTYGVmOR.js → modal-backdrop-BAbgYsqB.js} +1 -1
  74. package/build/assets/{modal-body-YElmM1dV.js → modal-body-BI6Ru2Qr.js} +1 -1
  75. package/build/assets/{modal-close-button-C_GpQt9F.js → modal-close-button-t1Gh3qmL.js} +1 -1
  76. package/build/assets/{model-selector-DeMmw-Xa.js → model-selector-SM9IUz-q.js} +1 -1
  77. package/build/assets/{mutation-Cz7N4XAo.js → mutation-D0OogFCz.js} +1 -1
  78. package/build/assets/{navigation-context-DeIPtGPp.js → navigation-context-D0YWpT8d.js} +1 -1
  79. package/build/assets/{navigation-link-C9JD4PYD.js → navigation-link-Cn7KP3c5.js} +1 -1
  80. package/build/assets/{openhands-logo-CI5Fhn1W.js → openhands-logo-CnrF6LKb.js} +1 -1
  81. package/build/assets/{option-service.api-DsI1UW7N.js → option-service.api-KvY_mZMY.js} +1 -1
  82. package/build/assets/{organization-service.api-COwMPFg5.js → organization-service.api-DzYTHTYC.js} +1 -1
  83. package/build/assets/{path-utils-CqJboYxo.js → path-utils-YohAYyMv.js} +1 -1
  84. package/build/assets/{plan-components-DEjMuDDG.js → plan-components-atxXCF0R.js} +1 -1
  85. package/build/assets/{planner-tab-BrntFmb1.js → planner-tab-CFc-hV07.js} +1 -1
  86. package/build/assets/{profiles-client-BGkKEV9j.js → profiles-client-D6IkTJof.js} +1 -1
  87. package/build/assets/{providers-DXvCAN_u.js → providers-Bx6EfrzZ.js} +1 -1
  88. package/build/assets/{proxy-CurRmrqf.js → proxy-CxydCnis.js} +1 -1
  89. package/build/assets/{query-client-config-Ba7qAAoO.js → query-client-config-B7u9asM0.js} +1 -1
  90. package/build/assets/{recommended-automations-launcher-BI9NhG8Y.js → recommended-automations-launcher-sgvfU62c.js} +3 -3
  91. package/build/assets/root-BXWU99D-.js +2 -0
  92. package/build/assets/{root-layout-BLjAEgle.js → root-layout-DVepR4To.js} +2 -2
  93. package/build/assets/sdk-section-page-DOIKvwSL.js +1 -0
  94. package/build/assets/{sdk-settings-schema-QBYH-ONX.js → sdk-settings-schema-DsUf9wu1.js} +1 -1
  95. package/build/assets/{search-Cq_cFrDt.js → search-27Owlc3A.js} +1 -1
  96. package/build/assets/{secrets-service-Bwd5DeUs.js → secrets-service-BsnKFc2x.js} +1 -1
  97. package/build/assets/secrets-settings-Bz_UohPJ.js +1 -0
  98. package/build/assets/{server-client-C3mC8Hl3.js → server-client-DyAQ3NZ_.js} +1 -1
  99. package/build/assets/{settings-D7E2U5tK.js → settings-BYkVX7vW.js} +1 -1
  100. package/build/assets/{settings-client-CwjfwoiB.js → settings-client-C73C7IgV.js} +1 -1
  101. package/build/assets/{settings-dropdown-input-VwAXNrOb.js → settings-dropdown-input-BJYvGdg-.js} +1 -1
  102. package/build/assets/{settings-gear-BJwWR1ej.js → settings-gear-C77PgE_O.js} +1 -1
  103. package/build/assets/{settings-index-J-3BNR0W.js → settings-index-Dz0BmdJD.js} +1 -1
  104. package/build/assets/{settings-input-DBywAnA7.js → settings-input-Bn7F5C75.js} +1 -1
  105. package/build/assets/{settings-list-classes-BOS092DR.js → settings-list-classes-Bf80tWtc.js} +1 -1
  106. package/build/assets/{settings-modal-B8vgWDTe.js → settings-modal-Brzgh5Yw.js} +1 -1
  107. package/build/assets/{settings-section-header-context-32x6WTyL.js → settings-section-header-context-BgZe5YkE.js} +1 -1
  108. package/build/assets/{settings-service.api-FvJGK45W.js → settings-service.api-CZ3uWx4v.js} +1 -1
  109. package/build/assets/{settings-switch-DTKmHC8F.js → settings-switch-BeIKrWms.js} +1 -1
  110. package/build/assets/{shared-conversation-a0QV8o99.js → shared-conversation-DChOdb0t.js} +1 -1
  111. package/build/assets/{sidebar-mobile-menu-toggle-DTUNI1WQ.js → sidebar-mobile-menu-toggle-BWuf4PRH.js} +1 -1
  112. package/build/assets/{sidebar-nav-link-CnWoZcwc.js → sidebar-nav-link-BGjiJq-4.js} +1 -1
  113. package/build/assets/{skill-card-pill-row-tZ599jli.js → skill-card-pill-row-DF1axQCG.js} +1 -1
  114. package/build/assets/{skills-ZyAO5dyK.js → skills-ChIKZPK4.js} +1 -1
  115. package/build/assets/{skills-plugins-BSRz041I.js → skills-plugins-CcI_19lM.js} +1 -1
  116. package/build/assets/{skills-settings-DOnMn9q1.js → skills-settings-DlA5hlXw.js} +1 -1
  117. package/build/assets/{status-CsatcFbK.js → status-hp6M6E7E.js} +1 -1
  118. package/build/assets/{styled-tooltip-CS3mB_1X.js → styled-tooltip-CBzrri6o.js} +1 -1
  119. package/build/assets/{switch-skeleton-C-CfhYYV.js → switch-skeleton-DnC9wLp7.js} +1 -1
  120. package/build/assets/{task-list-tab-Day9nhRT.js → task-list-tab-DUJn1sgz.js} +1 -1
  121. package/build/assets/{terminal-ro4SNjUU.js → terminal-CRf9S0Z2.js} +1 -1
  122. package/build/assets/{terminal-LNa-iU5c.js → terminal-RmuaSdhJ.js} +1 -1
  123. package/build/assets/{toggle-switch-k-IZCDbt.js → toggle-switch-Pvyp2RAN.js} +1 -1
  124. package/build/assets/{typography-vVUMoNUg.js → typography-gpuWmrQO.js} +1 -1
  125. package/build/assets/{u-check-circle-DplbarS5.js → u-check-circle-IUIfACQQ.js} +1 -1
  126. package/build/assets/{u-check-circle-half-yDuiSZHC.js → u-check-circle-half-C1YxB6py.js} +1 -1
  127. package/build/assets/{u-circuit-C9tYkpeK.js → u-circuit-BmVikJHu.js} +1 -1
  128. package/build/assets/{u-edit-KAUlufD8.js → u-edit-CFvXHqZk.js} +1 -1
  129. package/build/assets/use-active-conversation-Db3IWSPK.js +1 -0
  130. package/build/assets/{use-agent-settings-schema-Bvp5UzV8.js → use-agent-settings-schema-33Un7UF2.js} +1 -1
  131. package/build/assets/{use-agent-state-DE5dlEXJ.js → use-agent-state-Bn8vS5sY.js} +1 -1
  132. package/build/assets/{use-cloud-current-user-id-DWVar4st.js → use-cloud-current-user-id-CvkXFnTT.js} +1 -1
  133. package/build/assets/use-config-Co1O8-Ey.js +1 -0
  134. package/build/assets/{use-create-conversation-DW7AGgLA.js → use-create-conversation-CKS3EAHu.js} +1 -1
  135. package/build/assets/{use-event-store-CQZCcVz-.js → use-event-store-BT_gV3ut.js} +1 -1
  136. package/build/assets/use-get-secrets-DuhdIA59.js +1 -0
  137. package/build/assets/{use-handle-plan-click-DpgEQDAV.js → use-handle-plan-click-C9zJpK8A.js} +1 -1
  138. package/build/assets/use-is-authed-BggE5wPj.js +1 -0
  139. package/build/assets/{use-is-creating-conversation-DhDeeWfA.js → use-is-creating-conversation-BZ5hB_Bg.js} +1 -1
  140. package/build/assets/{use-launch-skill-in-chat-DVGPFrbI.js → use-launch-skill-in-chat-fNN_xGZG.js} +1 -1
  141. package/build/assets/{use-llm-profiles-D3-KXwQ0.js → use-llm-profiles-DDOol3gK.js} +1 -1
  142. package/build/assets/use-runtime-is-ready-CQCE3xZC.js +1 -0
  143. package/build/assets/{use-save-settings-CEEKSTWG.js → use-save-settings-VUrj_QNG.js} +1 -1
  144. package/build/assets/{use-settings-DQ7Oo1Hj.js → use-settings-DQIZmIov.js} +1 -1
  145. package/build/assets/{use-settings-nav-items-YmrXrjn9.js → use-settings-nav-items-1ZvovKSr.js} +1 -1
  146. package/build/assets/use-skills-DAMLFjKU.js +1 -0
  147. package/build/assets/{use-task-list-Bs90uF2N.js → use-task-list-CLJbuJgM.js} +1 -1
  148. package/build/assets/use-unified-vscode-url-sZt29HrC.js +1 -0
  149. package/build/assets/use-user-conversation-DfgEB6RW.js +1 -0
  150. package/build/assets/{useMutation-B4OUESdw.js → useMutation-DqrumCWD.js} +1 -1
  151. package/build/assets/{useTranslation-CpIcQBq6.js → useTranslation-DCOdSSMl.js} +1 -1
  152. package/build/assets/{utils-D-HX7JCe.js → utils-i18rdUj2.js} +1 -1
  153. package/build/assets/v4-CNn21NXa.js +1 -0
  154. package/build/assets/{vendor~browser-Dr71AdrG.js → vendor~browser-BNjNhjFU.js} +1 -1
  155. package/build/assets/{vendor~browser-tab-BiVxfjJo.js → vendor~browser-tab-BgwV1mxF.js} +1 -1
  156. package/build/assets/{vendor~conversation-panel~conversation-BlCIz9XQ.js → vendor~conversation-panel~conversation-a9SyrrhV.js} +1 -1
  157. package/build/assets/{vendor~files-tab-DtLR-QD9.js → vendor~files-tab-BGKayPiK.js} +1 -1
  158. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Ds9quNZ9.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-smY2r837.js} +1 -1
  159. package/build/assets/{vendor~home~mcp~automations-list-C5PoHCy6.js → vendor~home~mcp~automations-list-Ccy2I0KU.js} +1 -1
  160. package/build/assets/{vendor~home~mcp~automations-list-BUBGGGYz.js → vendor~home~mcp~automations-list-DoPfwaXj.js} +1 -1
  161. package/build/assets/{vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-CGlZoBKa.js → vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-DbfELDJu.js} +2 -2
  162. package/build/assets/{vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-DE11mPxp.js → vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-Z3nsiNNq.js} +1 -1
  163. package/build/assets/{vendor~launch-Dg--Ssk6.js → vendor~launch-vdeRTWFu.js} +1 -1
  164. package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-DrXgiSCq.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-DW31UyBp.js} +1 -1
  165. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-8b8V5bfO.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-BkQGKpye.js} +1 -1
  166. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-Dy7L6fMG.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-DzIXV3Ui.js} +1 -1
  167. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~k776hupu-D40EXhZx.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~k776hupu-Bbs7UJ5U.js} +2 -2
  168. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~dp08i1qy-CHrEOFl6.js → vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~dp08i1qy-DTwbEEcX.js} +1 -1
  169. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-BP1SKG0F.js → vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-d2oallMa.js} +1 -1
  170. package/build/assets/{vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~kyz9p27j-CyUbhpbm.js → vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~f2l2lr17-CDXvdvb2.js} +1 -1
  171. package/build/assets/{verification-settings-BtlTiHP8.js → verification-settings-CsbvQcYS.js} +1 -1
  172. package/build/assets/{vscode-tab-C0ShhiSU.js → vscode-tab-Zb-QbTuV.js} +1 -1
  173. package/build/assets/{waiting-for-runtime-message-DWPl_Yby.js → waiting-for-runtime-message-CntjExbU.js} +1 -1
  174. package/build/assets/{x-mark-CWI0f9yI.js → x-mark-CrpjscNc.js} +1 -1
  175. package/build/index.html +4 -4
  176. package/build/locales/ar/openhands.json +7 -0
  177. package/build/locales/ca/openhands.json +7 -0
  178. package/build/locales/de/openhands.json +7 -0
  179. package/build/locales/en/openhands.json +7 -0
  180. package/build/locales/es/openhands.json +7 -0
  181. package/build/locales/fr/openhands.json +7 -0
  182. package/build/locales/it/openhands.json +7 -0
  183. package/build/locales/ja/openhands.json +7 -0
  184. package/build/locales/ko-KR/openhands.json +7 -0
  185. package/build/locales/no/openhands.json +7 -0
  186. package/build/locales/pt/openhands.json +7 -0
  187. package/build/locales/tr/openhands.json +7 -0
  188. package/build/locales/uk/openhands.json +7 -0
  189. package/build/locales/zh-CN/openhands.json +7 -0
  190. package/build/locales/zh-TW/openhands.json +7 -0
  191. package/config/defaults.json +0 -4
  192. package/dist/api/agent-server-adapter.cjs +1 -1
  193. package/dist/api/agent-server-adapter.cjs.map +1 -1
  194. package/dist/api/agent-server-adapter.js +2 -1
  195. package/dist/api/agent-server-adapter.js.map +1 -1
  196. package/dist/api/agent-server-compatibility.cjs +1 -1
  197. package/dist/api/agent-server-compatibility.cjs.map +1 -1
  198. package/dist/api/agent-server-compatibility.d.ts +16 -0
  199. package/dist/api/agent-server-compatibility.js +31 -20
  200. package/dist/api/agent-server-compatibility.js.map +1 -1
  201. package/dist/api/agent-server-config.cjs +1 -1
  202. package/dist/api/agent-server-config.cjs.map +1 -1
  203. package/dist/api/agent-server-config.d.ts +45 -0
  204. package/dist/api/agent-server-config.js +49 -21
  205. package/dist/api/agent-server-config.js.map +1 -1
  206. package/dist/api/backend-registry/storage.cjs +1 -1
  207. package/dist/api/backend-registry/storage.cjs.map +1 -1
  208. package/dist/api/backend-registry/storage.js +34 -32
  209. package/dist/api/backend-registry/storage.js.map +1 -1
  210. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  211. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  212. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +5 -4
  213. package/dist/api/conversation-service/agent-server-conversation-service.api.js +70 -76
  214. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  215. package/dist/api/skills-service.cjs +1 -1
  216. package/dist/api/skills-service.cjs.map +1 -1
  217. package/dist/api/skills-service.d.ts +1 -1
  218. package/dist/api/skills-service.js +2 -2
  219. package/dist/api/skills-service.js.map +1 -1
  220. package/dist/components/features/backends/api-key-entry-screen.d.ts +10 -0
  221. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  222. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  223. package/dist/components/features/backends/backend-form-modal.d.ts +23 -2
  224. package/dist/components/features/backends/backend-form-modal.js +185 -173
  225. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  226. package/dist/components/features/browser/browser.cjs +1 -1
  227. package/dist/components/features/browser/browser.cjs.map +1 -1
  228. package/dist/components/features/browser/browser.js +10 -16
  229. package/dist/components/features/browser/browser.js.map +1 -1
  230. package/dist/components/features/conversation-panel/skills-modal.cjs +1 -1
  231. package/dist/components/features/conversation-panel/skills-modal.cjs.map +1 -1
  232. package/dist/components/features/conversation-panel/skills-modal.js +1 -1
  233. package/dist/components/features/conversation-panel/skills-modal.js.map +1 -1
  234. package/dist/components/features/mcp-page/install-server-modal.cjs +1 -1
  235. package/dist/components/features/mcp-page/install-server-modal.cjs.map +1 -1
  236. package/dist/components/features/mcp-page/install-server-modal.js +123 -116
  237. package/dist/components/features/mcp-page/install-server-modal.js.map +1 -1
  238. package/dist/components/features/mcp-page/installed-server-card.cjs +1 -1
  239. package/dist/components/features/mcp-page/installed-server-card.cjs.map +1 -1
  240. package/dist/components/features/mcp-page/installed-server-card.js +40 -40
  241. package/dist/components/features/mcp-page/installed-server-card.js.map +1 -1
  242. package/dist/components/features/mcp-page/marketplace-card.cjs +1 -1
  243. package/dist/components/features/mcp-page/marketplace-card.cjs.map +1 -1
  244. package/dist/components/features/mcp-page/marketplace-card.js +2 -3
  245. package/dist/components/features/mcp-page/marketplace-card.js.map +1 -1
  246. package/dist/components/features/mcp-page/marketplace-section.cjs +1 -1
  247. package/dist/components/features/mcp-page/marketplace-section.cjs.map +1 -1
  248. package/dist/components/features/mcp-page/marketplace-section.js +21 -21
  249. package/dist/components/features/mcp-page/marketplace-section.js.map +1 -1
  250. package/dist/components/features/onboarding/steps/setup-acp-secrets-step.d.ts +27 -0
  251. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.cjs +1 -1
  252. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.js +2 -0
  253. package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs +1 -1
  254. package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs.map +1 -1
  255. package/dist/components/features/settings/sdk-settings/sdk-section-page.d.ts +10 -1
  256. package/dist/components/features/settings/sdk-settings/sdk-section-page.js +87 -84
  257. package/dist/components/features/settings/sdk-settings/sdk-section-page.js.map +1 -1
  258. package/dist/constants/acp-providers.cjs +1 -1
  259. package/dist/constants/acp-providers.cjs.map +1 -1
  260. package/dist/constants/acp-providers.d.ts +25 -0
  261. package/dist/constants/acp-providers.js +1 -0
  262. package/dist/constants/acp-providers.js.map +1 -1
  263. package/dist/contexts/conversation-websocket-context.cjs +3 -3
  264. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  265. package/dist/contexts/conversation-websocket-context.js +177 -165
  266. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  267. package/dist/hooks/chat/use-model-interceptor.cjs.map +1 -1
  268. package/dist/hooks/chat/use-model-interceptor.js.map +1 -1
  269. package/dist/hooks/chat/use-slash-command.cjs +1 -1
  270. package/dist/hooks/chat/use-slash-command.cjs.map +1 -1
  271. package/dist/hooks/chat/use-slash-command.js +1 -1
  272. package/dist/hooks/chat/use-slash-command.js.map +1 -1
  273. package/dist/hooks/mutation/use-switch-llm-profile.cjs.map +1 -1
  274. package/dist/hooks/mutation/use-switch-llm-profile.d.ts +1 -1
  275. package/dist/hooks/mutation/use-switch-llm-profile.js.map +1 -1
  276. package/dist/hooks/query/use-config.cjs +1 -1
  277. package/dist/hooks/query/use-config.cjs.map +1 -1
  278. package/dist/hooks/query/use-config.js +10 -10
  279. package/dist/hooks/query/use-config.js.map +1 -1
  280. package/dist/hooks/query/use-conversation-skills.cjs +2 -0
  281. package/dist/hooks/query/use-conversation-skills.cjs.map +1 -0
  282. package/dist/hooks/query/use-conversation-skills.d.ts +7 -0
  283. package/dist/hooks/query/use-conversation-skills.js +8 -0
  284. package/dist/hooks/query/use-conversation-skills.js.map +1 -0
  285. package/dist/hooks/query/use-local-git-info.cjs +3 -3
  286. package/dist/hooks/query/use-local-git-info.cjs.map +1 -1
  287. package/dist/hooks/query/use-local-git-info.js +24 -25
  288. package/dist/hooks/query/use-local-git-info.js.map +1 -1
  289. package/dist/hooks/query/use-skills.cjs +1 -1
  290. package/dist/hooks/query/use-skills.cjs.map +1 -1
  291. package/dist/hooks/query/use-skills.d.ts +6 -1
  292. package/dist/hooks/query/use-skills.js +3 -3
  293. package/dist/hooks/query/use-skills.js.map +1 -1
  294. package/dist/i18n/declaration.cjs +1 -1
  295. package/dist/i18n/declaration.cjs.map +1 -1
  296. package/dist/i18n/declaration.d.ts +7 -0
  297. package/dist/i18n/declaration.js +1 -1
  298. package/dist/i18n/declaration.js.map +1 -1
  299. package/dist/i18n/translation.cjs +2 -2
  300. package/dist/i18n/translation.cjs.map +1 -1
  301. package/dist/i18n/translation.js +119 -0
  302. package/dist/i18n/translation.js.map +1 -1
  303. package/dist/locales/ar/openhands.json +7 -0
  304. package/dist/locales/ca/openhands.json +7 -0
  305. package/dist/locales/de/openhands.json +7 -0
  306. package/dist/locales/en/openhands.json +7 -0
  307. package/dist/locales/es/openhands.json +7 -0
  308. package/dist/locales/fr/openhands.json +7 -0
  309. package/dist/locales/it/openhands.json +7 -0
  310. package/dist/locales/ja/openhands.json +7 -0
  311. package/dist/locales/ko-KR/openhands.json +7 -0
  312. package/dist/locales/no/openhands.json +7 -0
  313. package/dist/locales/pt/openhands.json +7 -0
  314. package/dist/locales/tr/openhands.json +7 -0
  315. package/dist/locales/uk/openhands.json +7 -0
  316. package/dist/locales/zh-CN/openhands.json +7 -0
  317. package/dist/locales/zh-TW/openhands.json +7 -0
  318. package/dist/package.cjs +1 -1
  319. package/dist/package.cjs.map +1 -1
  320. package/dist/package.js +3 -3
  321. package/dist/package.js.map +1 -1
  322. package/dist/routes/conversation.cjs +1 -1
  323. package/dist/routes/conversation.cjs.map +1 -1
  324. package/dist/routes/conversation.js +61 -63
  325. package/dist/routes/conversation.js.map +1 -1
  326. package/dist/routes/mcp.cjs +1 -1
  327. package/dist/routes/mcp.cjs.map +1 -1
  328. package/dist/routes/mcp.js +64 -64
  329. package/dist/routes/mcp.js.map +1 -1
  330. package/dist/stores/browser-store.cjs +1 -1
  331. package/dist/stores/browser-store.cjs.map +1 -1
  332. package/dist/stores/browser-store.js +1 -1
  333. package/dist/stores/browser-store.js.map +1 -1
  334. package/dist/stores/use-event-store.cjs +1 -1
  335. package/dist/stores/use-event-store.cjs.map +1 -1
  336. package/dist/stores/use-event-store.d.ts +22 -0
  337. package/dist/stores/use-event-store.js +9 -1
  338. package/dist/stores/use-event-store.js.map +1 -1
  339. package/dist/ui/context-menu.d.ts +1 -1
  340. package/dist/ui/help-link.d.ts +1 -1
  341. package/dist/utils/mcp-marketplace-utils.cjs +1 -1
  342. package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
  343. package/dist/utils/mcp-marketplace-utils.d.ts +13 -22
  344. package/dist/utils/mcp-marketplace-utils.js +46 -28
  345. package/dist/utils/mcp-marketplace-utils.js.map +1 -1
  346. package/dist/utils/sdk-settings-schema.cjs +1 -1
  347. package/dist/utils/sdk-settings-schema.cjs.map +1 -1
  348. package/dist/utils/sdk-settings-schema.d.ts +1 -0
  349. package/dist/utils/sdk-settings-schema.js +1 -1
  350. package/dist/utils/sdk-settings-schema.js.map +1 -1
  351. package/package.json +3 -3
  352. package/scripts/dev-safe.mjs +94 -57
  353. package/scripts/dev-static.mjs +2 -3
  354. package/scripts/dev-with-automation.mjs +98 -67
  355. package/scripts/static-server.mjs +77 -35
  356. package/tools/canvas_ui_tool.py +4 -0
  357. package/build/assets/acp-providers-DauuOsW9.js +0 -1
  358. package/build/assets/add-backend-modal-KMmPQNZU.js +0 -1
  359. package/build/assets/agent-server-client-options-DT2GP6VJ.js +0 -1
  360. package/build/assets/agent-server-compatibility-2aOx5iWd.js +0 -1
  361. package/build/assets/agent-server-conversation-service.api-DSl9G5UR.js +0 -5
  362. package/build/assets/backend-form-modal-K6IMCr3p.js +0 -1
  363. package/build/assets/browser-DKG63inJ.js +0 -5
  364. package/build/assets/browser-store-C3AqxAO7.js +0 -1
  365. package/build/assets/browser-tab-B_BuTvrO.js +0 -1
  366. package/build/assets/conversation-BD5WemJI.js +0 -19
  367. package/build/assets/conversation-C47K62n8.js +0 -1
  368. package/build/assets/conversation-panel-Dn-S56Gk.js +0 -1
  369. package/build/assets/conversation-websocket-context-Ywrxd_9p.js +0 -3
  370. package/build/assets/declaration-D378OjpZ.js +0 -1
  371. package/build/assets/files-tab-B3A1NDlZ.js +0 -1
  372. package/build/assets/git-control-bar-branch-button-CcIpmyfM.js +0 -27
  373. package/build/assets/home-dIzxi5Dd.js +0 -1
  374. package/build/assets/install-server-modal-z5VaHeXd.js +0 -1
  375. package/build/assets/llm-settings-2036m7Wt.js +0 -1
  376. package/build/assets/llm-settings-CcHqGOYL.js +0 -1
  377. package/build/assets/manifest-97e839da.js +0 -1
  378. package/build/assets/mcp-C06YssEI.js +0 -9
  379. package/build/assets/root-BS1Td78t.js +0 -2
  380. package/build/assets/sdk-section-page-CJW0G04-.js +0 -1
  381. package/build/assets/secrets-settings-MLXqOtX2.js +0 -1
  382. package/build/assets/use-active-conversation-D15D9GgR.js +0 -1
  383. package/build/assets/use-config-BSu_53GL.js +0 -1
  384. package/build/assets/use-conversation-id-DajhCn2A.js +0 -1
  385. package/build/assets/use-is-authed-hXC8vxgT.js +0 -1
  386. package/build/assets/use-runtime-is-ready-XFbT16BD.js +0 -1
  387. package/build/assets/use-skills-Xe0vjPMt.js +0 -1
  388. package/build/assets/use-unified-vscode-url-BOsIOd-b.js +0 -1
  389. package/build/assets/use-user-conversation-Mc0mQgkl.js +0 -1
  390. /package/build/assets/{automation-XLxhq3I8.js → automation-IdgZq6ZK.js} +0 -0
  391. /package/build/assets/{common-SMkEaBSr.js → common-DR1t-EeP.js} +0 -0
  392. /package/build/assets/{conversation-state-store-Bc0slAjL.js → conversation-state-store-u5jepov0.js} +0 -0
  393. /package/build/assets/{dist-yMQV8IUk.js → dist-BxBP7tFD.js} +0 -0
  394. /package/build/assets/{git-status-mapper-BI8FyUVp.js → git-status-mapper-DnL9OC8_.js} +0 -0
  395. /package/build/assets/{handle-capture-consent-BfZATzpI.js → handle-capture-consent-3XrjZ8wi.js} +0 -0
  396. /package/build/assets/{iconBase-C7N9pPOs.js → iconBase-DE30Zj_-.js} +0 -0
  397. /package/build/assets/{settings-D5am1n6X.js → settings-D_H-qsRm.js} +0 -0
  398. /package/build/assets/{settings-like-page-layout-classes-Bn-M9oOa.js → settings-like-page-layout-classes-I0BDBEoq.js} +0 -0
  399. /package/build/assets/{settings-utils-BsvSU3OM.js → settings-utils-B6Nl07io.js} +0 -0
  400. /package/build/assets/{sidebar-store-cOeaKmIm.js → sidebar-store-Uy3v0AOV.js} +0 -0
  401. /package/build/assets/{use-breakpoint-B86yKT9n.js → use-breakpoint-DbJ6FkQ-.js} +0 -0
  402. /package/build/assets/{use-click-outside-element-835W9pC6.js → use-click-outside-element-DffgWWoZ.js} +0 -0
  403. /package/build/assets/{vendor~browser-BpdPBhgZ.js → vendor~browser-DDiZgqD3.js} +0 -0
  404. /package/build/assets/{vendor~conversation-panel~conversation~alert-banner-Df7_G0zR.js → vendor~conversation-panel~conversation~alert-banner-DbvX3OcM.js} +0 -0
  405. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~b4cctr4k-B7YVdv1X.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~g56ukk6u-DsSvIDZQ.js} +0 -0
  406. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~i9dbt75i-CI82Did1.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~hkqzh1hb-BZ0HXuHD.js} +0 -0
  407. /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~pfbaerbd-zhv9fooy.js → vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~ninslayh-D9P8e98a.js} +0 -0
  408. /package/build/assets/{vendor~terminal-BUxzHKcC.js → vendor~terminal-DUrOWGFE.js} +0 -0
  409. /package/build/assets/{vscode-url-helper-jesbpos5.js → vscode-url-helper-Cwy1A62q.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"browser-store.cjs","names":[],"sources":["../../src/stores/browser-store.ts"],"sourcesContent":["import { create } from \"zustand\";\n\ninterface BrowserState {\n // URL of browser window (placeholder for now, will be replaced with the actual URL later)\n url: string;\n // Base64-encoded screenshot of browser window (placeholder for now, will be replaced with the actual screenshot later)\n screenshotSrc: string;\n}\n\ninterface BrowserStore extends BrowserState {\n setUrl: (url: string) => void;\n setScreenshotSrc: (screenshotSrc: string) => void;\n reset: () => void;\n}\n\nconst initialState: BrowserState = {\n url: \"https://github.com/OpenHands/OpenHands\",\n screenshotSrc: \"\",\n};\n\nexport const useBrowserStore = create<BrowserStore>((set) => ({\n ...initialState,\n setUrl: (url: string) => set({ url }),\n setScreenshotSrc: (screenshotSrc: string) => set({ screenshotSrc }),\n reset: () => set(initialState),\n}));\n"],"mappings":"sGAeA,IAAM,EAA6B,CACjC,IAAK,yCACL,cAAe,GAChB,CAEY,EAAkB,EAAA,OAAsB,IAAS,CAC5D,GAAG,EACH,OAAS,GAAgB,EAAI,CAAE,MAAK,CAAC,CACrC,iBAAmB,GAA0B,EAAI,CAAE,gBAAe,CAAC,CACnE,UAAa,EAAI,EAAa,CAC/B,EAAE"}
1
+ {"version":3,"file":"browser-store.cjs","names":[],"sources":["../../src/stores/browser-store.ts"],"sourcesContent":["import { create } from \"zustand\";\n\ninterface BrowserState {\n // URL of the last page the agent navigated to in the browser panel.\n url: string;\n // Base64-encoded screenshot of the browser window, when the tool provides one.\n screenshotSrc: string;\n}\n\ninterface BrowserStore extends BrowserState {\n setUrl: (url: string) => void;\n setScreenshotSrc: (screenshotSrc: string) => void;\n reset: () => void;\n}\n\nconst initialState: BrowserState = {\n url: \"\",\n screenshotSrc: \"\",\n};\n\nexport const useBrowserStore = create<BrowserStore>((set) => ({\n ...initialState,\n setUrl: (url: string) => set({ url }),\n setScreenshotSrc: (screenshotSrc: string) => set({ screenshotSrc }),\n reset: () => set(initialState),\n}));\n"],"mappings":"sGAeA,IAAM,EAA6B,CACjC,IAAK,GACL,cAAe,GAChB,CAEY,EAAkB,EAAA,OAAsB,IAAS,CAC5D,GAAG,EACH,OAAS,GAAgB,EAAI,CAAE,MAAK,CAAC,CACrC,iBAAmB,GAA0B,EAAI,CAAE,gBAAe,CAAC,CACnE,UAAa,EAAI,EAAa,CAC/B,EAAE"}
@@ -1,7 +1,7 @@
1
1
  import { create as e } from "../node_modules/zustand/esm/react.js";
2
2
  //#region src/stores/browser-store.ts
3
3
  var t = {
4
- url: "https://github.com/OpenHands/OpenHands",
4
+ url: "",
5
5
  screenshotSrc: ""
6
6
  }, n = e((e) => ({
7
7
  ...t,
@@ -1 +1 @@
1
- {"version":3,"file":"browser-store.js","names":[],"sources":["../../src/stores/browser-store.ts"],"sourcesContent":["import { create } from \"zustand\";\n\ninterface BrowserState {\n // URL of browser window (placeholder for now, will be replaced with the actual URL later)\n url: string;\n // Base64-encoded screenshot of browser window (placeholder for now, will be replaced with the actual screenshot later)\n screenshotSrc: string;\n}\n\ninterface BrowserStore extends BrowserState {\n setUrl: (url: string) => void;\n setScreenshotSrc: (screenshotSrc: string) => void;\n reset: () => void;\n}\n\nconst initialState: BrowserState = {\n url: \"https://github.com/OpenHands/OpenHands\",\n screenshotSrc: \"\",\n};\n\nexport const useBrowserStore = create<BrowserStore>((set) => ({\n ...initialState,\n setUrl: (url: string) => set({ url }),\n setScreenshotSrc: (screenshotSrc: string) => set({ screenshotSrc }),\n reset: () => set(initialState),\n}));\n"],"mappings":";;AAeA,IAAM,IAA6B;CACjC,KAAK;CACL,eAAe;CAChB,EAEY,IAAkB,GAAsB,OAAS;CAC5D,GAAG;CACH,SAAS,MAAgB,EAAI,EAAE,QAAK,CAAC;CACrC,mBAAmB,MAA0B,EAAI,EAAE,kBAAe,CAAC;CACnE,aAAa,EAAI,EAAa;CAC/B,EAAE"}
1
+ {"version":3,"file":"browser-store.js","names":[],"sources":["../../src/stores/browser-store.ts"],"sourcesContent":["import { create } from \"zustand\";\n\ninterface BrowserState {\n // URL of the last page the agent navigated to in the browser panel.\n url: string;\n // Base64-encoded screenshot of the browser window, when the tool provides one.\n screenshotSrc: string;\n}\n\ninterface BrowserStore extends BrowserState {\n setUrl: (url: string) => void;\n setScreenshotSrc: (screenshotSrc: string) => void;\n reset: () => void;\n}\n\nconst initialState: BrowserState = {\n url: \"\",\n screenshotSrc: \"\",\n};\n\nexport const useBrowserStore = create<BrowserStore>((set) => ({\n ...initialState,\n setUrl: (url: string) => set({ url }),\n setScreenshotSrc: (screenshotSrc: string) => set({ screenshotSrc }),\n reset: () => set(initialState),\n}));\n"],"mappings":";;AAeA,IAAM,IAA6B;CACjC,KAAK;CACL,eAAe;CAChB,EAEY,IAAkB,GAAsB,OAAS;CAC5D,GAAG;CACH,SAAS,MAAgB,EAAI,EAAE,QAAK,CAAC;CACrC,mBAAmB,MAA0B,EAAI,EAAE,kBAAe,CAAC;CACnE,aAAa,EAAI,EAAa;CAC/B,EAAE"}
@@ -1,2 +1,2 @@
1
- require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../node_modules/zustand/esm/react.cjs`),t=require(`../utils/handle-event-for-ui.cjs`);var n=e=>`id`in e?e.id:void 0,r=e=>`timestamp`in e?e.timestamp:void 0,i=(e,t)=>{let n=r(e),i=r(t);return!n&&!i?0:n?i?n.localeCompare(i):-1:1},a=(e,t)=>{if(e.length===0)return!1;let n=e[e.length-1],i=r(n),a=r(t);return!i||!a?!1:a<i},o=(e,r)=>{let i=n(r);if(i!==void 0&&e.eventIds.has(i))return e;let a=i===void 0?e.eventIds:new Set(e.eventIds).add(i);return{...e,events:[...e.events,r],eventIds:a,uiEvents:t.handleEventForUI(r,e.uiEvents)}},s=e=>({...e,events:[...e.events].sort(i),uiEvents:[...e.uiEvents].sort(i)}),c=(e,t)=>{let n=o(e,t);return n===e?e:!a(e.events,t)&&!a(e.uiEvents,t)?n:s(n)},l=e.create()(e=>({events:[],eventIds:new Set,uiEvents:[],addEvent:t=>e(e=>c(e,t)),addEvents:r=>e(e=>{if(r.length===0)return e;let i=new Set(e.eventIds),a=[...e.events],o=[...e.uiEvents],c=!1;for(let e of r){let r=n(e);r!==void 0&&i.has(r)||(c=!0,r!==void 0&&i.add(r),a.push(e),o=t.handleEventForUI(e,o))}return c?s({...e,events:a,eventIds:i,uiEvents:o}):e}),clearEvents:()=>e(()=>({events:[],eventIds:new Set,uiEvents:[]}))}));exports.useEventStore=l;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../node_modules/zustand/esm/react.cjs`),t=require(`../utils/handle-event-for-ui.cjs`);var n=e=>`id`in e?e.id:void 0,r=e=>`timestamp`in e?e.timestamp:void 0,i=(e,t)=>{let n=r(e),i=r(t);return!n&&!i?0:n?i?n.localeCompare(i):-1:1},a=(e,t)=>{if(e.length===0)return!1;let n=e[e.length-1],i=r(n),a=r(t);return!i||!a?!1:a<i},o=(e,r)=>{let i=n(r);if(i!==void 0&&e.eventIds.has(i))return e;let a=i===void 0?e.eventIds:new Set(e.eventIds).add(i);return{...e,events:[...e.events,r],eventIds:a,uiEvents:t.handleEventForUI(r,e.uiEvents)}},s=e=>({...e,events:[...e.events].sort(i),uiEvents:[...e.uiEvents].sort(i)}),c=(e,t)=>{let n=o(e,t);return n===e?e:!a(e.events,t)&&!a(e.uiEvents,t)?n:s(n)},l=e.create()(e=>({events:[],eventIds:new Set,uiEvents:[],loadedConversationId:null,addEvent:t=>e(e=>c(e,t)),addEvents:r=>e(e=>{if(r.length===0)return e;let i=new Set(e.eventIds),a=[...e.events],o=[...e.uiEvents],c=!1;for(let e of r){let r=n(e);r!==void 0&&i.has(r)||(c=!0,r!==void 0&&i.add(r),a.push(e),o=t.handleEventForUI(e,o))}return c?s({...e,events:a,eventIds:i,uiEvents:o}):e}),clearEvents:()=>e(()=>({events:[],eventIds:new Set,uiEvents:[],loadedConversationId:null})),clearEventsForConversation:t=>e(()=>({events:[],eventIds:new Set,uiEvents:[],loadedConversationId:t}))}));exports.useEventStore=l;
2
2
  //# sourceMappingURL=use-event-store.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-event-store.cjs","names":[],"sources":["../../src/stores/use-event-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport { handleEventForUI } from \"#/utils/handle-event-for-ui\";\n\nexport type OHEvent = OpenHandsEvent & {\n isFromPlanningAgent?: boolean;\n};\n\nconst getEventId = (event: OHEvent): string | number | undefined =>\n \"id\" in event ? event.id : undefined;\n\nconst getEventTimestamp = (event: OHEvent): string | undefined =>\n \"timestamp\" in event ? event.timestamp : undefined;\n\n/**\n * Compare two events by timestamp for sorting.\n * Events without timestamps are placed at the end.\n */\nconst compareEventsByTimestamp = (a: OHEvent, b: OHEvent): number => {\n const timestampA = getEventTimestamp(a);\n const timestampB = getEventTimestamp(b);\n\n // Events without timestamps go to the end\n if (!timestampA && !timestampB) return 0;\n if (!timestampA) return 1;\n if (!timestampB) return -1;\n\n // Compare ISO timestamp strings (lexicographic comparison works for ISO format)\n return timestampA.localeCompare(timestampB);\n};\n\n/**\n * Check if the new event needs sorting (i.e., it's out of order).\n * Returns true if the new event's timestamp is earlier than the last event's timestamp.\n */\nconst needsSorting = (events: OHEvent[], newEvent: OHEvent): boolean => {\n if (events.length === 0) return false;\n\n const lastEvent = events[events.length - 1];\n const lastTimestamp = getEventTimestamp(lastEvent);\n const newTimestamp = getEventTimestamp(newEvent);\n\n // If either event doesn't have a timestamp, don't sort\n if (!lastTimestamp || !newTimestamp) return false;\n\n // Sort needed if new event's timestamp is earlier than last event's timestamp\n return newTimestamp < lastTimestamp;\n};\n\nexport interface EventState {\n events: OHEvent[];\n eventIds: Set<string | number>;\n uiEvents: OHEvent[];\n addEvent: (event: OHEvent) => void;\n /**\n * Bulk-insert events. Used for the initial REST history load and for\n * \"scroll up to load older\" pagination. Newly-added events are de-duped\n * against the existing store and the combined list is re-sorted by\n * timestamp so older pages drop into the correct position.\n */\n addEvents: (events: OHEvent[]) => void;\n clearEvents: () => void;\n}\n\nconst appendEvent = (state: EventState, event: OHEvent): EventState => {\n // Deduplicate: skip if event with same id already exists (O(1) lookup)\n const eventId = getEventId(event);\n if (eventId !== undefined && state.eventIds.has(eventId)) {\n return state;\n }\n\n const newEventIds =\n eventId !== undefined\n ? new Set(state.eventIds).add(eventId)\n : state.eventIds;\n\n return {\n ...state,\n events: [...state.events, event],\n eventIds: newEventIds,\n uiEvents: handleEventForUI(event, state.uiEvents),\n };\n};\n\nconst sortEventState = (state: EventState): EventState => ({\n ...state,\n events: [...state.events].sort(compareEventsByTimestamp),\n uiEvents: [...state.uiEvents].sort(compareEventsByTimestamp),\n});\n\nconst applyAddEvent = (state: EventState, event: OHEvent): EventState => {\n const next = appendEvent(state, event);\n if (next === state) {\n return state;\n }\n\n if (\n !needsSorting(state.events, event) &&\n !needsSorting(state.uiEvents, event)\n ) {\n return next;\n }\n\n return sortEventState(next);\n};\n\nexport const useEventStore = create<EventState>()((set) => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n addEvent: (event: OHEvent) => set((state) => applyAddEvent(state, event)),\n addEvents: (incoming: OHEvent[]) =>\n set((state) => {\n if (incoming.length === 0) return state;\n\n const eventIds = new Set(state.eventIds);\n const events = [...state.events];\n let uiEvents = [...state.uiEvents];\n let added = false;\n\n for (const event of incoming) {\n const eventId = getEventId(event);\n const isDuplicate = eventId !== undefined && eventIds.has(eventId);\n\n if (!isDuplicate) {\n added = true;\n if (eventId !== undefined) {\n eventIds.add(eventId);\n }\n events.push(event);\n uiEvents = handleEventForUI(event, uiEvents);\n }\n }\n\n if (!added) {\n return state;\n }\n\n return sortEventState({\n ...state,\n events,\n eventIds,\n uiEvents,\n });\n }),\n clearEvents: () =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n })),\n}));\n\n// In dev builds, expose the store on `window` so that fixture/preview\n// scripts (e.g. .pr/issue-132 demo capture) can inject synthetic events\n// without round-tripping through the agent-server. Tree-shaken in\n// production builds via `import.meta.env.DEV`.\nif (\n typeof window !== \"undefined\" &&\n typeof import.meta !== \"undefined\" &&\n (import.meta as { env?: { DEV?: boolean } }).env?.DEV\n) {\n (\n window as unknown as { __OH_EVENT_STORE__?: typeof useEventStore }\n ).__OH_EVENT_STORE__ = useEventStore;\n}\n"],"mappings":"oJAQA,IAAM,EAAc,GAClB,OAAQ,EAAQ,EAAM,GAAK,IAAA,GAEvB,EAAqB,GACzB,cAAe,EAAQ,EAAM,UAAY,IAAA,GAMrC,GAA4B,EAAY,IAAuB,CACnE,IAAM,EAAa,EAAkB,EAAE,CACjC,EAAa,EAAkB,EAAE,CAQvC,MALI,CAAC,GAAc,CAAC,EAAmB,EAClC,EACA,EAGE,EAAW,cAAc,EAAW,CAHnB,GADA,GAWpB,GAAgB,EAAmB,IAA+B,CACtE,GAAI,EAAO,SAAW,EAAG,MAAO,GAEhC,IAAM,EAAY,EAAO,EAAO,OAAS,GACnC,EAAgB,EAAkB,EAAU,CAC5C,EAAe,EAAkB,EAAS,CAMhD,MAHI,CAAC,GAAiB,CAAC,EAAqB,GAGrC,EAAe,GAkBlB,GAAe,EAAmB,IAA+B,CAErE,IAAM,EAAU,EAAW,EAAM,CACjC,GAAI,IAAY,IAAA,IAAa,EAAM,SAAS,IAAI,EAAQ,CACtD,OAAO,EAGT,IAAM,EACJ,IAAY,IAAA,GAER,EAAM,SADN,IAAI,IAAI,EAAM,SAAS,CAAC,IAAI,EAAQ,CAG1C,MAAO,CACL,GAAG,EACH,OAAQ,CAAC,GAAG,EAAM,OAAQ,EAAM,CAChC,SAAU,EACV,SAAU,EAAA,iBAAiB,EAAO,EAAM,SAAS,CAClD,EAGG,EAAkB,IAAmC,CACzD,GAAG,EACH,OAAQ,CAAC,GAAG,EAAM,OAAO,CAAC,KAAK,EAAyB,CACxD,SAAU,CAAC,GAAG,EAAM,SAAS,CAAC,KAAK,EAAyB,CAC7D,EAEK,GAAiB,EAAmB,IAA+B,CACvE,IAAM,EAAO,EAAY,EAAO,EAAM,CAYtC,OAXI,IAAS,EACJ,EAIP,CAAC,EAAa,EAAM,OAAQ,EAAM,EAClC,CAAC,EAAa,EAAM,SAAU,EAAM,CAE7B,EAGF,EAAe,EAAK,EAGhB,EAAgB,EAAA,QAAoB,CAAE,IAAS,CAC1D,OAAQ,EAAE,CACV,SAAU,IAAI,IACd,SAAU,EAAE,CACZ,SAAW,GAAmB,EAAK,GAAU,EAAc,EAAO,EAAM,CAAC,CACzE,UAAY,GACV,EAAK,GAAU,CACb,GAAI,EAAS,SAAW,EAAG,OAAO,EAElC,IAAM,EAAW,IAAI,IAAI,EAAM,SAAS,CAClC,EAAS,CAAC,GAAG,EAAM,OAAO,CAC5B,EAAW,CAAC,GAAG,EAAM,SAAS,CAC9B,EAAQ,GAEZ,IAAK,IAAM,KAAS,EAAU,CAC5B,IAAM,EAAU,EAAW,EAAM,CACb,IAAY,IAAA,IAAa,EAAS,IAAI,EAAQ,GAGhE,EAAQ,GACJ,IAAY,IAAA,IACd,EAAS,IAAI,EAAQ,CAEvB,EAAO,KAAK,EAAM,CAClB,EAAW,EAAA,iBAAiB,EAAO,EAAS,EAQhD,OAJK,EAIE,EAAe,CACpB,GAAG,EACH,SACA,WACA,WACD,CAAC,CARO,GAST,CACJ,gBACE,OAAW,CACT,OAAQ,EAAE,CACV,SAAU,IAAI,IACd,SAAU,EAAE,CACb,EAAE,CACN,EAAE"}
1
+ {"version":3,"file":"use-event-store.cjs","names":[],"sources":["../../src/stores/use-event-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport { handleEventForUI } from \"#/utils/handle-event-for-ui\";\n\nexport type OHEvent = OpenHandsEvent & {\n isFromPlanningAgent?: boolean;\n};\n\nconst getEventId = (event: OHEvent): string | number | undefined =>\n \"id\" in event ? event.id : undefined;\n\nconst getEventTimestamp = (event: OHEvent): string | undefined =>\n \"timestamp\" in event ? event.timestamp : undefined;\n\n/**\n * Compare two events by timestamp for sorting.\n * Events without timestamps are placed at the end.\n */\nconst compareEventsByTimestamp = (a: OHEvent, b: OHEvent): number => {\n const timestampA = getEventTimestamp(a);\n const timestampB = getEventTimestamp(b);\n\n // Events without timestamps go to the end\n if (!timestampA && !timestampB) return 0;\n if (!timestampA) return 1;\n if (!timestampB) return -1;\n\n // Compare ISO timestamp strings (lexicographic comparison works for ISO format)\n return timestampA.localeCompare(timestampB);\n};\n\n/**\n * Check if the new event needs sorting (i.e., it's out of order).\n * Returns true if the new event's timestamp is earlier than the last event's timestamp.\n */\nconst needsSorting = (events: OHEvent[], newEvent: OHEvent): boolean => {\n if (events.length === 0) return false;\n\n const lastEvent = events[events.length - 1];\n const lastTimestamp = getEventTimestamp(lastEvent);\n const newTimestamp = getEventTimestamp(newEvent);\n\n // If either event doesn't have a timestamp, don't sort\n if (!lastTimestamp || !newTimestamp) return false;\n\n // Sort needed if new event's timestamp is earlier than last event's timestamp\n return newTimestamp < lastTimestamp;\n};\n\nexport interface EventState {\n events: OHEvent[];\n eventIds: Set<string | number>;\n uiEvents: OHEvent[];\n /**\n * The conversation whose events currently populate the store. The store is\n * global (not keyed by conversation), so the conversation route uses this to\n * tell a genuine conversation switch apart from a remount of the *same*\n * conversation (e.g. navigating to Settings and back) — only the former\n * should clear the accumulated events.\n */\n loadedConversationId: string | null;\n addEvent: (event: OHEvent) => void;\n /**\n * Bulk-insert events. Used for the initial REST history load and for\n * \"scroll up to load older\" pagination. Newly-added events are de-duped\n * against the existing store and the combined list is re-sorted by\n * timestamp so older pages drop into the correct position.\n */\n addEvents: (events: OHEvent[]) => void;\n /**\n * Clear all events. Also resets `loadedConversationId` to `null` so the\n * store never claims to hold a conversation whose events have been wiped —\n * the invariant (`loadedConversationId` reflects the conversation whose\n * events are in the arrays) holds even for a standalone clear.\n */\n clearEvents: () => void;\n /**\n * Atomically clear all events and record which conversation is now loaded.\n * Collapsing the reset and the bookkeeping into a single `set` keeps the\n * store invariant enforced at the boundary, rather than relying on every\n * call-site to invoke a clear and a `loadedConversationId` setter in the\n * right order.\n */\n clearEventsForConversation: (conversationId: string | null) => void;\n}\n\nconst appendEvent = (state: EventState, event: OHEvent): EventState => {\n // Deduplicate: skip if event with same id already exists (O(1) lookup)\n const eventId = getEventId(event);\n if (eventId !== undefined && state.eventIds.has(eventId)) {\n return state;\n }\n\n const newEventIds =\n eventId !== undefined\n ? new Set(state.eventIds).add(eventId)\n : state.eventIds;\n\n return {\n ...state,\n events: [...state.events, event],\n eventIds: newEventIds,\n uiEvents: handleEventForUI(event, state.uiEvents),\n };\n};\n\nconst sortEventState = (state: EventState): EventState => ({\n ...state,\n events: [...state.events].sort(compareEventsByTimestamp),\n uiEvents: [...state.uiEvents].sort(compareEventsByTimestamp),\n});\n\nconst applyAddEvent = (state: EventState, event: OHEvent): EventState => {\n const next = appendEvent(state, event);\n if (next === state) {\n return state;\n }\n\n if (\n !needsSorting(state.events, event) &&\n !needsSorting(state.uiEvents, event)\n ) {\n return next;\n }\n\n return sortEventState(next);\n};\n\nexport const useEventStore = create<EventState>()((set) => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: null,\n addEvent: (event: OHEvent) => set((state) => applyAddEvent(state, event)),\n addEvents: (incoming: OHEvent[]) =>\n set((state) => {\n if (incoming.length === 0) return state;\n\n const eventIds = new Set(state.eventIds);\n const events = [...state.events];\n let uiEvents = [...state.uiEvents];\n let added = false;\n\n for (const event of incoming) {\n const eventId = getEventId(event);\n const isDuplicate = eventId !== undefined && eventIds.has(eventId);\n\n if (!isDuplicate) {\n added = true;\n if (eventId !== undefined) {\n eventIds.add(eventId);\n }\n events.push(event);\n uiEvents = handleEventForUI(event, uiEvents);\n }\n }\n\n if (!added) {\n return state;\n }\n\n return sortEventState({\n ...state,\n events,\n eventIds,\n uiEvents,\n });\n }),\n clearEvents: () =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: null,\n })),\n clearEventsForConversation: (conversationId: string | null) =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: conversationId,\n })),\n}));\n\n// In dev builds, expose the store on `window` so that fixture/preview\n// scripts (e.g. .pr/issue-132 demo capture) can inject synthetic events\n// without round-tripping through the agent-server. Tree-shaken in\n// production builds via `import.meta.env.DEV`.\nif (\n typeof window !== \"undefined\" &&\n typeof import.meta !== \"undefined\" &&\n (import.meta as { env?: { DEV?: boolean } }).env?.DEV\n) {\n (\n window as unknown as { __OH_EVENT_STORE__?: typeof useEventStore }\n ).__OH_EVENT_STORE__ = useEventStore;\n}\n"],"mappings":"oJAQA,IAAM,EAAc,GAClB,OAAQ,EAAQ,EAAM,GAAK,IAAA,GAEvB,EAAqB,GACzB,cAAe,EAAQ,EAAM,UAAY,IAAA,GAMrC,GAA4B,EAAY,IAAuB,CACnE,IAAM,EAAa,EAAkB,EAAE,CACjC,EAAa,EAAkB,EAAE,CAQvC,MALI,CAAC,GAAc,CAAC,EAAmB,EAClC,EACA,EAGE,EAAW,cAAc,EAAW,CAHnB,GADA,GAWpB,GAAgB,EAAmB,IAA+B,CACtE,GAAI,EAAO,SAAW,EAAG,MAAO,GAEhC,IAAM,EAAY,EAAO,EAAO,OAAS,GACnC,EAAgB,EAAkB,EAAU,CAC5C,EAAe,EAAkB,EAAS,CAMhD,MAHI,CAAC,GAAiB,CAAC,EAAqB,GAGrC,EAAe,GAwClB,GAAe,EAAmB,IAA+B,CAErE,IAAM,EAAU,EAAW,EAAM,CACjC,GAAI,IAAY,IAAA,IAAa,EAAM,SAAS,IAAI,EAAQ,CACtD,OAAO,EAGT,IAAM,EACJ,IAAY,IAAA,GAER,EAAM,SADN,IAAI,IAAI,EAAM,SAAS,CAAC,IAAI,EAAQ,CAG1C,MAAO,CACL,GAAG,EACH,OAAQ,CAAC,GAAG,EAAM,OAAQ,EAAM,CAChC,SAAU,EACV,SAAU,EAAA,iBAAiB,EAAO,EAAM,SAAS,CAClD,EAGG,EAAkB,IAAmC,CACzD,GAAG,EACH,OAAQ,CAAC,GAAG,EAAM,OAAO,CAAC,KAAK,EAAyB,CACxD,SAAU,CAAC,GAAG,EAAM,SAAS,CAAC,KAAK,EAAyB,CAC7D,EAEK,GAAiB,EAAmB,IAA+B,CACvE,IAAM,EAAO,EAAY,EAAO,EAAM,CAYtC,OAXI,IAAS,EACJ,EAIP,CAAC,EAAa,EAAM,OAAQ,EAAM,EAClC,CAAC,EAAa,EAAM,SAAU,EAAM,CAE7B,EAGF,EAAe,EAAK,EAGhB,EAAgB,EAAA,QAAoB,CAAE,IAAS,CAC1D,OAAQ,EAAE,CACV,SAAU,IAAI,IACd,SAAU,EAAE,CACZ,qBAAsB,KACtB,SAAW,GAAmB,EAAK,GAAU,EAAc,EAAO,EAAM,CAAC,CACzE,UAAY,GACV,EAAK,GAAU,CACb,GAAI,EAAS,SAAW,EAAG,OAAO,EAElC,IAAM,EAAW,IAAI,IAAI,EAAM,SAAS,CAClC,EAAS,CAAC,GAAG,EAAM,OAAO,CAC5B,EAAW,CAAC,GAAG,EAAM,SAAS,CAC9B,EAAQ,GAEZ,IAAK,IAAM,KAAS,EAAU,CAC5B,IAAM,EAAU,EAAW,EAAM,CACb,IAAY,IAAA,IAAa,EAAS,IAAI,EAAQ,GAGhE,EAAQ,GACJ,IAAY,IAAA,IACd,EAAS,IAAI,EAAQ,CAEvB,EAAO,KAAK,EAAM,CAClB,EAAW,EAAA,iBAAiB,EAAO,EAAS,EAQhD,OAJK,EAIE,EAAe,CACpB,GAAG,EACH,SACA,WACA,WACD,CAAC,CARO,GAST,CACJ,gBACE,OAAW,CACT,OAAQ,EAAE,CACV,SAAU,IAAI,IACd,SAAU,EAAE,CACZ,qBAAsB,KACvB,EAAE,CACL,2BAA6B,GAC3B,OAAW,CACT,OAAQ,EAAE,CACV,SAAU,IAAI,IACd,SAAU,EAAE,CACZ,qBAAsB,EACvB,EAAE,CACN,EAAE"}
@@ -6,6 +6,14 @@ export interface EventState {
6
6
  events: OHEvent[];
7
7
  eventIds: Set<string | number>;
8
8
  uiEvents: OHEvent[];
9
+ /**
10
+ * The conversation whose events currently populate the store. The store is
11
+ * global (not keyed by conversation), so the conversation route uses this to
12
+ * tell a genuine conversation switch apart from a remount of the *same*
13
+ * conversation (e.g. navigating to Settings and back) — only the former
14
+ * should clear the accumulated events.
15
+ */
16
+ loadedConversationId: string | null;
9
17
  addEvent: (event: OHEvent) => void;
10
18
  /**
11
19
  * Bulk-insert events. Used for the initial REST history load and for
@@ -14,6 +22,20 @@ export interface EventState {
14
22
  * timestamp so older pages drop into the correct position.
15
23
  */
16
24
  addEvents: (events: OHEvent[]) => void;
25
+ /**
26
+ * Clear all events. Also resets `loadedConversationId` to `null` so the
27
+ * store never claims to hold a conversation whose events have been wiped —
28
+ * the invariant (`loadedConversationId` reflects the conversation whose
29
+ * events are in the arrays) holds even for a standalone clear.
30
+ */
17
31
  clearEvents: () => void;
32
+ /**
33
+ * Atomically clear all events and record which conversation is now loaded.
34
+ * Collapsing the reset and the bookkeeping into a single `set` keeps the
35
+ * store invariant enforced at the boundary, rather than relying on every
36
+ * call-site to invoke a clear and a `loadedConversationId` setter in the
37
+ * right order.
38
+ */
39
+ clearEventsForConversation: (conversationId: string | null) => void;
18
40
  }
19
41
  export declare const useEventStore: import("zustand").UseBoundStore<import("zustand").StoreApi<EventState>>;
@@ -29,6 +29,7 @@ var n = (e) => "id" in e ? e.id : void 0, r = (e) => "timestamp" in e ? e.timest
29
29
  events: [],
30
30
  eventIds: /* @__PURE__ */ new Set(),
31
31
  uiEvents: [],
32
+ loadedConversationId: null,
32
33
  addEvent: (t) => e((e) => c(e, t)),
33
34
  addEvents: (r) => e((e) => {
34
35
  if (r.length === 0) return e;
@@ -47,7 +48,14 @@ var n = (e) => "id" in e ? e.id : void 0, r = (e) => "timestamp" in e ? e.timest
47
48
  clearEvents: () => e(() => ({
48
49
  events: [],
49
50
  eventIds: /* @__PURE__ */ new Set(),
50
- uiEvents: []
51
+ uiEvents: [],
52
+ loadedConversationId: null
53
+ })),
54
+ clearEventsForConversation: (t) => e(() => ({
55
+ events: [],
56
+ eventIds: /* @__PURE__ */ new Set(),
57
+ uiEvents: [],
58
+ loadedConversationId: t
51
59
  }))
52
60
  }));
53
61
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"use-event-store.js","names":[],"sources":["../../src/stores/use-event-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport { handleEventForUI } from \"#/utils/handle-event-for-ui\";\n\nexport type OHEvent = OpenHandsEvent & {\n isFromPlanningAgent?: boolean;\n};\n\nconst getEventId = (event: OHEvent): string | number | undefined =>\n \"id\" in event ? event.id : undefined;\n\nconst getEventTimestamp = (event: OHEvent): string | undefined =>\n \"timestamp\" in event ? event.timestamp : undefined;\n\n/**\n * Compare two events by timestamp for sorting.\n * Events without timestamps are placed at the end.\n */\nconst compareEventsByTimestamp = (a: OHEvent, b: OHEvent): number => {\n const timestampA = getEventTimestamp(a);\n const timestampB = getEventTimestamp(b);\n\n // Events without timestamps go to the end\n if (!timestampA && !timestampB) return 0;\n if (!timestampA) return 1;\n if (!timestampB) return -1;\n\n // Compare ISO timestamp strings (lexicographic comparison works for ISO format)\n return timestampA.localeCompare(timestampB);\n};\n\n/**\n * Check if the new event needs sorting (i.e., it's out of order).\n * Returns true if the new event's timestamp is earlier than the last event's timestamp.\n */\nconst needsSorting = (events: OHEvent[], newEvent: OHEvent): boolean => {\n if (events.length === 0) return false;\n\n const lastEvent = events[events.length - 1];\n const lastTimestamp = getEventTimestamp(lastEvent);\n const newTimestamp = getEventTimestamp(newEvent);\n\n // If either event doesn't have a timestamp, don't sort\n if (!lastTimestamp || !newTimestamp) return false;\n\n // Sort needed if new event's timestamp is earlier than last event's timestamp\n return newTimestamp < lastTimestamp;\n};\n\nexport interface EventState {\n events: OHEvent[];\n eventIds: Set<string | number>;\n uiEvents: OHEvent[];\n addEvent: (event: OHEvent) => void;\n /**\n * Bulk-insert events. Used for the initial REST history load and for\n * \"scroll up to load older\" pagination. Newly-added events are de-duped\n * against the existing store and the combined list is re-sorted by\n * timestamp so older pages drop into the correct position.\n */\n addEvents: (events: OHEvent[]) => void;\n clearEvents: () => void;\n}\n\nconst appendEvent = (state: EventState, event: OHEvent): EventState => {\n // Deduplicate: skip if event with same id already exists (O(1) lookup)\n const eventId = getEventId(event);\n if (eventId !== undefined && state.eventIds.has(eventId)) {\n return state;\n }\n\n const newEventIds =\n eventId !== undefined\n ? new Set(state.eventIds).add(eventId)\n : state.eventIds;\n\n return {\n ...state,\n events: [...state.events, event],\n eventIds: newEventIds,\n uiEvents: handleEventForUI(event, state.uiEvents),\n };\n};\n\nconst sortEventState = (state: EventState): EventState => ({\n ...state,\n events: [...state.events].sort(compareEventsByTimestamp),\n uiEvents: [...state.uiEvents].sort(compareEventsByTimestamp),\n});\n\nconst applyAddEvent = (state: EventState, event: OHEvent): EventState => {\n const next = appendEvent(state, event);\n if (next === state) {\n return state;\n }\n\n if (\n !needsSorting(state.events, event) &&\n !needsSorting(state.uiEvents, event)\n ) {\n return next;\n }\n\n return sortEventState(next);\n};\n\nexport const useEventStore = create<EventState>()((set) => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n addEvent: (event: OHEvent) => set((state) => applyAddEvent(state, event)),\n addEvents: (incoming: OHEvent[]) =>\n set((state) => {\n if (incoming.length === 0) return state;\n\n const eventIds = new Set(state.eventIds);\n const events = [...state.events];\n let uiEvents = [...state.uiEvents];\n let added = false;\n\n for (const event of incoming) {\n const eventId = getEventId(event);\n const isDuplicate = eventId !== undefined && eventIds.has(eventId);\n\n if (!isDuplicate) {\n added = true;\n if (eventId !== undefined) {\n eventIds.add(eventId);\n }\n events.push(event);\n uiEvents = handleEventForUI(event, uiEvents);\n }\n }\n\n if (!added) {\n return state;\n }\n\n return sortEventState({\n ...state,\n events,\n eventIds,\n uiEvents,\n });\n }),\n clearEvents: () =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n })),\n}));\n\n// In dev builds, expose the store on `window` so that fixture/preview\n// scripts (e.g. .pr/issue-132 demo capture) can inject synthetic events\n// without round-tripping through the agent-server. Tree-shaken in\n// production builds via `import.meta.env.DEV`.\nif (\n typeof window !== \"undefined\" &&\n typeof import.meta !== \"undefined\" &&\n (import.meta as { env?: { DEV?: boolean } }).env?.DEV\n) {\n (\n window as unknown as { __OH_EVENT_STORE__?: typeof useEventStore }\n ).__OH_EVENT_STORE__ = useEventStore;\n}\n"],"mappings":";;;AAQA,IAAM,KAAc,MAClB,QAAQ,IAAQ,EAAM,KAAK,KAAA,GAEvB,KAAqB,MACzB,eAAe,IAAQ,EAAM,YAAY,KAAA,GAMrC,KAA4B,GAAY,MAAuB;CACnE,IAAM,IAAa,EAAkB,EAAE,EACjC,IAAa,EAAkB,EAAE;AAQvC,QALI,CAAC,KAAc,CAAC,IAAmB,IAClC,IACA,IAGE,EAAW,cAAc,EAAW,GAHnB,KADA;GAWpB,KAAgB,GAAmB,MAA+B;AACtE,KAAI,EAAO,WAAW,EAAG,QAAO;CAEhC,IAAM,IAAY,EAAO,EAAO,SAAS,IACnC,IAAgB,EAAkB,EAAU,EAC5C,IAAe,EAAkB,EAAS;AAMhD,QAHI,CAAC,KAAiB,CAAC,IAAqB,KAGrC,IAAe;GAkBlB,KAAe,GAAmB,MAA+B;CAErE,IAAM,IAAU,EAAW,EAAM;AACjC,KAAI,MAAY,KAAA,KAAa,EAAM,SAAS,IAAI,EAAQ,CACtD,QAAO;CAGT,IAAM,IACJ,MAAY,KAAA,IAER,EAAM,WADN,IAAI,IAAI,EAAM,SAAS,CAAC,IAAI,EAAQ;AAG1C,QAAO;EACL,GAAG;EACH,QAAQ,CAAC,GAAG,EAAM,QAAQ,EAAM;EAChC,UAAU;EACV,UAAU,EAAiB,GAAO,EAAM,SAAS;EAClD;GAGG,KAAkB,OAAmC;CACzD,GAAG;CACH,QAAQ,CAAC,GAAG,EAAM,OAAO,CAAC,KAAK,EAAyB;CACxD,UAAU,CAAC,GAAG,EAAM,SAAS,CAAC,KAAK,EAAyB;CAC7D,GAEK,KAAiB,GAAmB,MAA+B;CACvE,IAAM,IAAO,EAAY,GAAO,EAAM;AAYtC,QAXI,MAAS,IACJ,IAIP,CAAC,EAAa,EAAM,QAAQ,EAAM,IAClC,CAAC,EAAa,EAAM,UAAU,EAAM,GAE7B,IAGF,EAAe,EAAK;GAGhB,IAAgB,GAAoB,EAAE,OAAS;CAC1D,QAAQ,EAAE;CACV,0BAAU,IAAI,KAAK;CACnB,UAAU,EAAE;CACZ,WAAW,MAAmB,GAAK,MAAU,EAAc,GAAO,EAAM,CAAC;CACzE,YAAY,MACV,GAAK,MAAU;AACb,MAAI,EAAS,WAAW,EAAG,QAAO;EAElC,IAAM,IAAW,IAAI,IAAI,EAAM,SAAS,EAClC,IAAS,CAAC,GAAG,EAAM,OAAO,EAC5B,IAAW,CAAC,GAAG,EAAM,SAAS,EAC9B,IAAQ;AAEZ,OAAK,IAAM,KAAS,GAAU;GAC5B,IAAM,IAAU,EAAW,EAAM;AAGjC,GAFoB,MAAY,KAAA,KAAa,EAAS,IAAI,EAAQ,KAGhE,IAAQ,IACJ,MAAY,KAAA,KACd,EAAS,IAAI,EAAQ,EAEvB,EAAO,KAAK,EAAM,EAClB,IAAW,EAAiB,GAAO,EAAS;;AAQhD,SAJK,IAIE,EAAe;GACpB,GAAG;GACH;GACA;GACA;GACD,CAAC,GARO;GAST;CACJ,mBACE,SAAW;EACT,QAAQ,EAAE;EACV,0BAAU,IAAI,KAAK;EACnB,UAAU,EAAE;EACb,EAAE;CACN,EAAE"}
1
+ {"version":3,"file":"use-event-store.js","names":[],"sources":["../../src/stores/use-event-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport { handleEventForUI } from \"#/utils/handle-event-for-ui\";\n\nexport type OHEvent = OpenHandsEvent & {\n isFromPlanningAgent?: boolean;\n};\n\nconst getEventId = (event: OHEvent): string | number | undefined =>\n \"id\" in event ? event.id : undefined;\n\nconst getEventTimestamp = (event: OHEvent): string | undefined =>\n \"timestamp\" in event ? event.timestamp : undefined;\n\n/**\n * Compare two events by timestamp for sorting.\n * Events without timestamps are placed at the end.\n */\nconst compareEventsByTimestamp = (a: OHEvent, b: OHEvent): number => {\n const timestampA = getEventTimestamp(a);\n const timestampB = getEventTimestamp(b);\n\n // Events without timestamps go to the end\n if (!timestampA && !timestampB) return 0;\n if (!timestampA) return 1;\n if (!timestampB) return -1;\n\n // Compare ISO timestamp strings (lexicographic comparison works for ISO format)\n return timestampA.localeCompare(timestampB);\n};\n\n/**\n * Check if the new event needs sorting (i.e., it's out of order).\n * Returns true if the new event's timestamp is earlier than the last event's timestamp.\n */\nconst needsSorting = (events: OHEvent[], newEvent: OHEvent): boolean => {\n if (events.length === 0) return false;\n\n const lastEvent = events[events.length - 1];\n const lastTimestamp = getEventTimestamp(lastEvent);\n const newTimestamp = getEventTimestamp(newEvent);\n\n // If either event doesn't have a timestamp, don't sort\n if (!lastTimestamp || !newTimestamp) return false;\n\n // Sort needed if new event's timestamp is earlier than last event's timestamp\n return newTimestamp < lastTimestamp;\n};\n\nexport interface EventState {\n events: OHEvent[];\n eventIds: Set<string | number>;\n uiEvents: OHEvent[];\n /**\n * The conversation whose events currently populate the store. The store is\n * global (not keyed by conversation), so the conversation route uses this to\n * tell a genuine conversation switch apart from a remount of the *same*\n * conversation (e.g. navigating to Settings and back) — only the former\n * should clear the accumulated events.\n */\n loadedConversationId: string | null;\n addEvent: (event: OHEvent) => void;\n /**\n * Bulk-insert events. Used for the initial REST history load and for\n * \"scroll up to load older\" pagination. Newly-added events are de-duped\n * against the existing store and the combined list is re-sorted by\n * timestamp so older pages drop into the correct position.\n */\n addEvents: (events: OHEvent[]) => void;\n /**\n * Clear all events. Also resets `loadedConversationId` to `null` so the\n * store never claims to hold a conversation whose events have been wiped —\n * the invariant (`loadedConversationId` reflects the conversation whose\n * events are in the arrays) holds even for a standalone clear.\n */\n clearEvents: () => void;\n /**\n * Atomically clear all events and record which conversation is now loaded.\n * Collapsing the reset and the bookkeeping into a single `set` keeps the\n * store invariant enforced at the boundary, rather than relying on every\n * call-site to invoke a clear and a `loadedConversationId` setter in the\n * right order.\n */\n clearEventsForConversation: (conversationId: string | null) => void;\n}\n\nconst appendEvent = (state: EventState, event: OHEvent): EventState => {\n // Deduplicate: skip if event with same id already exists (O(1) lookup)\n const eventId = getEventId(event);\n if (eventId !== undefined && state.eventIds.has(eventId)) {\n return state;\n }\n\n const newEventIds =\n eventId !== undefined\n ? new Set(state.eventIds).add(eventId)\n : state.eventIds;\n\n return {\n ...state,\n events: [...state.events, event],\n eventIds: newEventIds,\n uiEvents: handleEventForUI(event, state.uiEvents),\n };\n};\n\nconst sortEventState = (state: EventState): EventState => ({\n ...state,\n events: [...state.events].sort(compareEventsByTimestamp),\n uiEvents: [...state.uiEvents].sort(compareEventsByTimestamp),\n});\n\nconst applyAddEvent = (state: EventState, event: OHEvent): EventState => {\n const next = appendEvent(state, event);\n if (next === state) {\n return state;\n }\n\n if (\n !needsSorting(state.events, event) &&\n !needsSorting(state.uiEvents, event)\n ) {\n return next;\n }\n\n return sortEventState(next);\n};\n\nexport const useEventStore = create<EventState>()((set) => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: null,\n addEvent: (event: OHEvent) => set((state) => applyAddEvent(state, event)),\n addEvents: (incoming: OHEvent[]) =>\n set((state) => {\n if (incoming.length === 0) return state;\n\n const eventIds = new Set(state.eventIds);\n const events = [...state.events];\n let uiEvents = [...state.uiEvents];\n let added = false;\n\n for (const event of incoming) {\n const eventId = getEventId(event);\n const isDuplicate = eventId !== undefined && eventIds.has(eventId);\n\n if (!isDuplicate) {\n added = true;\n if (eventId !== undefined) {\n eventIds.add(eventId);\n }\n events.push(event);\n uiEvents = handleEventForUI(event, uiEvents);\n }\n }\n\n if (!added) {\n return state;\n }\n\n return sortEventState({\n ...state,\n events,\n eventIds,\n uiEvents,\n });\n }),\n clearEvents: () =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: null,\n })),\n clearEventsForConversation: (conversationId: string | null) =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: conversationId,\n })),\n}));\n\n// In dev builds, expose the store on `window` so that fixture/preview\n// scripts (e.g. .pr/issue-132 demo capture) can inject synthetic events\n// without round-tripping through the agent-server. Tree-shaken in\n// production builds via `import.meta.env.DEV`.\nif (\n typeof window !== \"undefined\" &&\n typeof import.meta !== \"undefined\" &&\n (import.meta as { env?: { DEV?: boolean } }).env?.DEV\n) {\n (\n window as unknown as { __OH_EVENT_STORE__?: typeof useEventStore }\n ).__OH_EVENT_STORE__ = useEventStore;\n}\n"],"mappings":";;;AAQA,IAAM,KAAc,MAClB,QAAQ,IAAQ,EAAM,KAAK,KAAA,GAEvB,KAAqB,MACzB,eAAe,IAAQ,EAAM,YAAY,KAAA,GAMrC,KAA4B,GAAY,MAAuB;CACnE,IAAM,IAAa,EAAkB,EAAE,EACjC,IAAa,EAAkB,EAAE;AAQvC,QALI,CAAC,KAAc,CAAC,IAAmB,IAClC,IACA,IAGE,EAAW,cAAc,EAAW,GAHnB,KADA;GAWpB,KAAgB,GAAmB,MAA+B;AACtE,KAAI,EAAO,WAAW,EAAG,QAAO;CAEhC,IAAM,IAAY,EAAO,EAAO,SAAS,IACnC,IAAgB,EAAkB,EAAU,EAC5C,IAAe,EAAkB,EAAS;AAMhD,QAHI,CAAC,KAAiB,CAAC,IAAqB,KAGrC,IAAe;GAwClB,KAAe,GAAmB,MAA+B;CAErE,IAAM,IAAU,EAAW,EAAM;AACjC,KAAI,MAAY,KAAA,KAAa,EAAM,SAAS,IAAI,EAAQ,CACtD,QAAO;CAGT,IAAM,IACJ,MAAY,KAAA,IAER,EAAM,WADN,IAAI,IAAI,EAAM,SAAS,CAAC,IAAI,EAAQ;AAG1C,QAAO;EACL,GAAG;EACH,QAAQ,CAAC,GAAG,EAAM,QAAQ,EAAM;EAChC,UAAU;EACV,UAAU,EAAiB,GAAO,EAAM,SAAS;EAClD;GAGG,KAAkB,OAAmC;CACzD,GAAG;CACH,QAAQ,CAAC,GAAG,EAAM,OAAO,CAAC,KAAK,EAAyB;CACxD,UAAU,CAAC,GAAG,EAAM,SAAS,CAAC,KAAK,EAAyB;CAC7D,GAEK,KAAiB,GAAmB,MAA+B;CACvE,IAAM,IAAO,EAAY,GAAO,EAAM;AAYtC,QAXI,MAAS,IACJ,IAIP,CAAC,EAAa,EAAM,QAAQ,EAAM,IAClC,CAAC,EAAa,EAAM,UAAU,EAAM,GAE7B,IAGF,EAAe,EAAK;GAGhB,IAAgB,GAAoB,EAAE,OAAS;CAC1D,QAAQ,EAAE;CACV,0BAAU,IAAI,KAAK;CACnB,UAAU,EAAE;CACZ,sBAAsB;CACtB,WAAW,MAAmB,GAAK,MAAU,EAAc,GAAO,EAAM,CAAC;CACzE,YAAY,MACV,GAAK,MAAU;AACb,MAAI,EAAS,WAAW,EAAG,QAAO;EAElC,IAAM,IAAW,IAAI,IAAI,EAAM,SAAS,EAClC,IAAS,CAAC,GAAG,EAAM,OAAO,EAC5B,IAAW,CAAC,GAAG,EAAM,SAAS,EAC9B,IAAQ;AAEZ,OAAK,IAAM,KAAS,GAAU;GAC5B,IAAM,IAAU,EAAW,EAAM;AAGjC,GAFoB,MAAY,KAAA,KAAa,EAAS,IAAI,EAAQ,KAGhE,IAAQ,IACJ,MAAY,KAAA,KACd,EAAS,IAAI,EAAQ,EAEvB,EAAO,KAAK,EAAM,EAClB,IAAW,EAAiB,GAAO,EAAS;;AAQhD,SAJK,IAIE,EAAe;GACpB,GAAG;GACH;GACA;GACA;GACD,CAAC,GARO;GAST;CACJ,mBACE,SAAW;EACT,QAAQ,EAAE;EACV,0BAAU,IAAI,KAAK;EACnB,UAAU,EAAE;EACZ,sBAAsB;EACvB,EAAE;CACL,6BAA6B,MAC3B,SAAW;EACT,QAAQ,EAAE;EACV,0BAAU,IAAI,KAAK;EACnB,UAAU,EAAE;EACZ,sBAAsB;EACvB,EAAE;CACN,EAAE"}
@@ -5,7 +5,7 @@ declare const contextMenuVariants: (props?: ({
5
5
  size?: "default" | "compact" | null | undefined;
6
6
  layout?: "vertical" | null | undefined;
7
7
  position?: "none" | "top" | "bottom" | null | undefined;
8
- spacing?: "none" | "default" | null | undefined;
8
+ spacing?: "default" | "none" | null | undefined;
9
9
  alignment?: "none" | "left" | "right" | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
11
  interface ContextMenuProps {
@@ -1,6 +1,6 @@
1
1
  import { type VariantProps } from "class-variance-authority";
2
2
  declare const helpLinkVariants: (props?: ({
3
- size?: "settings" | "default" | null | undefined;
3
+ size?: "default" | "settings" | null | undefined;
4
4
  linkColor?: "default" | "white" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface HelpLinkProps extends VariantProps<typeof helpLinkVariants> {
@@ -1,2 +1,2 @@
1
- require(`../_virtual/_rolldown/runtime.cjs`);var e=e=>{try{return new URL(e)}catch{return null}};function t(t,n){let r=typeof t==`string`?t:``,i=typeof n==`string`?n:``;if(!r||!i)return!1;let a=e(r),o=e(i);return!a||!o?r.replace(/\/+$/,``)===i.replace(/\/+$/,``):a.protocol===o.protocol&&a.host===o.host&&a.pathname.replace(/\/+$/,``)===o.pathname.replace(/\/+$/,``)}function n(e){return(e.connectionOptions.find(t=>t.id===e.defaultConnectionOptionId)??e.connectionOptions[0])?.transport}function r(e){let t=e.connectionOptions.find(e=>e.transport?.kind===`stdio`);return t?.transport?t.transport:n(e)}function i(e,t){return!e.runtimeAvailability||e.runtimeAvailability===`all`?!0:e.runtimeAvailability===t}function a(e){return e.trim().toLowerCase()}function o(e){return e.map((e,t)=>({entry:e,index:t})).sort((e,t)=>(t.entry.popularityRank??0)-(e.entry.popularityRank??0)||e.index-t.index).map(({entry:e})=>e)}function s(e,t){let n=a(t);return n?[e.name,e.description,e.id,...e.keywords??[]].join(` `).toLowerCase().includes(n):!0}function c(e,t,n){let r=a(n);return r?[e.type,`name`in e?e.name:void 0,`command`in e?e.command:void 0,`args`in e?e.args?.join(` `):void 0,`url`in e?e.url:void 0,t?.name,t?.description,t?.id,...t?.keywords??[]].filter(Boolean).join(` `).toLowerCase().includes(r):!0}function l(e,n){return n.find(n=>{for(let r of n.connectionOptions){let n=r.transport;if(n&&(n.kind===`stdio`&&e.type===`stdio`&&e.name===n.serverName||n.kind===`shttp`&&e.type===`shttp`&&t(e.url,n.url)||n.kind===`sse`&&e.type===`sse`&&t(e.url,n.url)))return!0}return!1})}exports.findCatalogEntryForServer=l,exports.getDefaultTemplate=n,exports.getInstallableTemplate=r,exports.getMarketplaceEntriesByPopularity=o,exports.installedServerMatchesQuery=c,exports.isMarketplaceEntryAvailable=i,exports.marketplaceEntryMatchesQuery=s;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);function e(e){return e.connectionOptions.filter(e=>e.provider===`mcp`&&!!e.transport)}function t(t){let n=e(t);return n.find(e=>e.id===t.defaultConnectionOptionId)??n[0]}function n(e){return e.auth.strategy!==`oauth2`}function r(t){let r=e(t),i=r.find(e=>e.id===t.defaultConnectionOptionId);return i&&n(i)?i:r.find(n)}function i(e){return t(e)?.transport}function a(e){return e.filter(e=>!!t(e))}var o=e=>{try{return new URL(e)}catch{return null}};function s(e,t){let n=typeof e==`string`?e:``,r=typeof t==`string`?t:``;if(!n||!r)return!1;let i=o(n),a=o(r);return!i||!a?n.replace(/\/+$/,``)===r.replace(/\/+$/,``):i.protocol===a.protocol&&i.host===a.host&&i.pathname.replace(/\/+$/,``)===a.pathname.replace(/\/+$/,``)}function c(e,t){if(e.kind===`shttp`){let n=e.url;return t.type===`shttp`&&!!t.url&&s(t.url,n)}if(e.kind===`sse`){let n=e.url;return t.type===`sse`&&!!t.url&&s(t.url,n)}return t.type===`stdio`&&t.name===e.serverName}function l(e,t){return!e.runtimeAvailability||e.runtimeAvailability===`all`?!0:e.runtimeAvailability===t}function u(e){return e.trim().toLowerCase()}function d(e){return e.map((e,t)=>({entry:e,index:t})).sort((e,t)=>(t.entry.popularityRank??0)-(e.entry.popularityRank??0)||e.index-t.index).map(({entry:e})=>e)}function f(e,t){let n=u(t);return n?[e.name,e.description,e.id,...e.keywords??[]].join(` `).toLowerCase().includes(n):!0}function p(e,t,n){let r=u(n);return r?[e.type,`name`in e?e.name:void 0,`command`in e?e.command:void 0,`args`in e?e.args?.join(` `):void 0,`url`in e?e.url:void 0,t?.name,t?.description,t?.id,...t?.keywords??[]].filter(Boolean).join(` `).toLowerCase().includes(r):!0}function m(t,n){return n.find(n=>e(n).some(e=>c(e.transport,t)))}exports.findCatalogEntryForServer=m,exports.getDefaultMcpTransport=i,exports.getInstallableMcpConnectionOption=r,exports.getMarketplaceEntriesByPopularity=d,exports.getMcpMarketplaceCatalog=a,exports.installedServerMatchesQuery=p,exports.isMarketplaceEntryAvailable=l,exports.marketplaceEntryMatchesQuery=f;
2
2
  //# sourceMappingURL=mcp-marketplace-utils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-marketplace-utils.cjs","names":[],"sources":["../../src/utils/mcp-marketplace-utils.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport type {\n IntegrationCatalogEntry as MarketplaceEntry,\n IntegrationTransport as MarketplaceTemplate,\n} from \"@openhands/extensions/integrations\";\n\nconst tryUrl = (raw: string): URL | null => {\n try {\n return new URL(raw);\n } catch {\n return null;\n }\n};\n\n/**\n * Loose URL match that ignores query strings, trailing slashes, and\n * default ports. We want clicking \"Linear\" to flag the entry as\n * installed even if the user pasted the URL with extra trailing slash\n * or a different port-equivalent variant.\n *\n * Defensive against runtime data that doesn't match the static type:\n * if either input is not a string (e.g. parsed from an older settings\n * blob), we fall through the URL parsing path and the safe trim\n * fallback below, never calling `.replace` on undefined.\n */\nexport function urlsMatch(a: unknown, b: unknown): boolean {\n const aStr = typeof a === \"string\" ? a : \"\";\n const bStr = typeof b === \"string\" ? b : \"\";\n if (!aStr || !bStr) return false;\n const left = tryUrl(aStr);\n const right = tryUrl(bStr);\n if (!left || !right) {\n return aStr.replace(/\\/+$/, \"\") === bStr.replace(/\\/+$/, \"\");\n }\n return (\n left.protocol === right.protocol &&\n left.host === right.host &&\n left.pathname.replace(/\\/+$/, \"\") === right.pathname.replace(/\\/+$/, \"\")\n );\n}\n\n/**\n * Get the default transport template from an integration catalog entry.\n * Integrations may have multiple connection options; we use the default\n * one (or the first if no default is specified). Only MCP-backed options\n * have a `transport` field.\n */\nexport function getDefaultTemplate(\n entry: MarketplaceEntry,\n): MarketplaceTemplate | undefined {\n const option =\n entry.connectionOptions.find(\n (o) => o.id === entry.defaultConnectionOptionId,\n ) ?? entry.connectionOptions[0];\n return option?.transport;\n}\n\n/**\n * Get the stdio (API key-based) transport template from an integration entry.\n * Many integrations have multiple connection options (e.g., OAuth + stdio).\n * Since OAuth isn't implemented in the UI yet, the install modal should use\n * this function to get the stdio-based option that can be configured with\n * API keys/tokens.\n *\n * Falls back to getDefaultTemplate if no stdio option exists.\n */\nexport function getInstallableTemplate(\n entry: MarketplaceEntry,\n): MarketplaceTemplate | undefined {\n // First, try to find a stdio option (API key-based, what we can actually install)\n const stdioOption = entry.connectionOptions.find(\n (o) => o.transport?.kind === \"stdio\",\n );\n if (stdioOption?.transport) return stdioOption.transport;\n\n // Fall back to the default template (could be shttp/sse with api_key)\n return getDefaultTemplate(entry);\n}\n\n/**\n * Decide whether a marketplace template is already represented by one\n * of the installed MCP servers. Used to render an \"Installed\" badge on\n * the marketplace tile. Returns the first matching server, or null.\n */\nexport function findInstalledMatch(\n template: MarketplaceTemplate,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n if (template.kind === \"shttp\") {\n const tplUrl = template.url;\n if (!tplUrl) return null;\n return (\n servers.find(\n (s) => s.type === \"shttp\" && !!s.url && urlsMatch(s.url, tplUrl),\n ) ?? null\n );\n }\n\n if (template.kind === \"sse\") {\n const tplUrl = template.url;\n if (!tplUrl) return null;\n return (\n servers.find(\n (s) => s.type === \"sse\" && !!s.url && urlsMatch(s.url, tplUrl),\n ) ?? null\n );\n }\n\n // stdio: match on the registered server name.\n return (\n servers.find((s) => s.type === \"stdio\" && s.name === template.serverName) ??\n null\n );\n}\n\nexport function isMarketplaceEntryAvailable(\n entry: MarketplaceEntry,\n backendKind: \"local\" | \"cloud\",\n): boolean {\n if (!entry.runtimeAvailability || entry.runtimeAvailability === \"all\")\n return true;\n return entry.runtimeAvailability === backendKind;\n}\n\nfunction normalize(query: string): string {\n return query.trim().toLowerCase();\n}\n\n/**\n * Case-insensitive substring match against the catalog entry's\n * user-visible identity (name, description, id, keywords). Empty\n * queries always match.\n */\nexport function getMarketplaceEntriesByPopularity(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog\n .map((entry, index) => ({ entry, index }))\n .sort((a, b) => {\n const byPopularity =\n (b.entry.popularityRank ?? 0) - (a.entry.popularityRank ?? 0);\n return byPopularity || a.index - b.index;\n })\n .map(({ entry }) => entry);\n}\n\nexport function getMarketplaceEntryById(\n id: string,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => entry.id === id);\n}\n\nexport function marketplaceEntryMatchesQuery(\n entry: MarketplaceEntry,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n entry.name,\n entry.description,\n entry.id,\n ...(entry.keywords ?? []),\n ]\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Search match for an installed (already-configured) server. We\n * search the server's own identifying fields and — if it's a catalog\n * entry — its catalog name/keywords too, so typing \"Slack\" matches\n * the installed Slack tile even though the persisted server is just\n * `{ type: \"stdio\", name: \"slack\", ... }`.\n */\nexport function installedServerMatchesQuery(\n server: MCPServerConfig,\n catalogEntry: MarketplaceEntry | undefined,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n server.type,\n \"name\" in server ? server.name : undefined,\n \"command\" in server ? server.command : undefined,\n \"args\" in server ? server.args?.join(\" \") : undefined,\n \"url\" in server ? server.url : undefined,\n catalogEntry?.name,\n catalogEntry?.description,\n catalogEntry?.id,\n ...(catalogEntry?.keywords ?? []),\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Look up the catalog entry that best matches an installed server.\n * Mirrors the lookup used in `installed-server-card.tsx` for\n * rendering the friendly icon.\n *\n * Since an entry may have multiple connection options (e.g., OAuth + stdio),\n * we check ALL templates in the entry's connectionOptions, not just the default.\n */\nexport function findCatalogEntryForServer(\n server: MCPServerConfig,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => {\n // Check all connection options, not just the default\n for (const option of entry.connectionOptions) {\n const tpl = option.transport;\n if (!tpl) continue;\n if (tpl.kind === \"stdio\") {\n if (server.type === \"stdio\" && server.name === tpl.serverName)\n return true;\n }\n // Reuse the same loose URL match as `findInstalledMatch` so a\n // server whose URL was normalized by the backend (trailing slash\n // stripped, query string dropped, etc.) still gets paired with\n // its catalog tile — otherwise the installed-servers list would\n // render the generic icon while the marketplace shows the\n // entry as installed, which is confusing.\n if (tpl.kind === \"shttp\") {\n if (server.type === \"shttp\" && urlsMatch(server.url, tpl.url))\n return true;\n }\n if (tpl.kind === \"sse\") {\n if (server.type === \"sse\" && urlsMatch(server.url, tpl.url))\n return true;\n }\n }\n return false;\n });\n}\n"],"mappings":"6CAMA,IAAM,EAAU,GAA4B,CAC1C,GAAI,CACF,OAAO,IAAI,IAAI,EAAI,MACb,CACN,OAAO,OAeX,SAAgB,EAAU,EAAY,EAAqB,CACzD,IAAM,EAAO,OAAO,GAAM,SAAW,EAAI,GACnC,EAAO,OAAO,GAAM,SAAW,EAAI,GACzC,GAAI,CAAC,GAAQ,CAAC,EAAM,MAAO,GAC3B,IAAM,EAAO,EAAO,EAAK,CACnB,EAAQ,EAAO,EAAK,CAI1B,MAHI,CAAC,GAAQ,CAAC,EACL,EAAK,QAAQ,OAAQ,GAAG,GAAK,EAAK,QAAQ,OAAQ,GAAG,CAG5D,EAAK,WAAa,EAAM,UACxB,EAAK,OAAS,EAAM,MACpB,EAAK,SAAS,QAAQ,OAAQ,GAAG,GAAK,EAAM,SAAS,QAAQ,OAAQ,GAAG,CAU5E,SAAgB,EACd,EACiC,CAKjC,OAHE,EAAM,kBAAkB,KACrB,GAAM,EAAE,KAAO,EAAM,0BACvB,EAAI,EAAM,kBAAkB,KAChB,UAYjB,SAAgB,EACd,EACiC,CAEjC,IAAM,EAAc,EAAM,kBAAkB,KACzC,GAAM,EAAE,WAAW,OAAS,QAC9B,CAID,OAHI,GAAa,UAAkB,EAAY,UAGxC,EAAmB,EAAM,CAuClC,SAAgB,EACd,EACA,EACS,CAGT,MAFI,CAAC,EAAM,qBAAuB,EAAM,sBAAwB,MACvD,GACF,EAAM,sBAAwB,EAGvC,SAAS,EAAU,EAAuB,CACxC,OAAO,EAAM,MAAM,CAAC,aAAa,CAQnC,SAAgB,EACd,EACoB,CACpB,OAAO,EACJ,KAAK,EAAO,KAAW,CAAE,QAAO,QAAO,EAAE,CACzC,MAAM,EAAG,KAEL,EAAE,MAAM,gBAAkB,IAAM,EAAE,MAAM,gBAAkB,IACtC,EAAE,MAAQ,EAAE,MACnC,CACD,KAAK,CAAE,WAAY,EAAM,CAU9B,SAAgB,EACd,EACA,EACS,CACT,IAAM,EAAI,EAAU,EAAS,CAU7B,OATK,EACY,CACf,EAAM,KACN,EAAM,YACN,EAAM,GACN,GAAI,EAAM,UAAY,EAAE,CACzB,CACE,KAAK,IAAI,CACT,aACI,CAAS,SAAS,EAAE,CATZ,GAmBjB,SAAgB,EACd,EACA,EACA,EACS,CACT,IAAM,EAAI,EAAU,EAAS,CAgB7B,OAfK,EACY,CACf,EAAO,KACP,SAAU,EAAS,EAAO,KAAO,IAAA,GACjC,YAAa,EAAS,EAAO,QAAU,IAAA,GACvC,SAAU,EAAS,EAAO,MAAM,KAAK,IAAI,CAAG,IAAA,GAC5C,QAAS,EAAS,EAAO,IAAM,IAAA,GAC/B,GAAc,KACd,GAAc,YACd,GAAc,GACd,GAAI,GAAc,UAAY,EAAE,CACjC,CACE,OAAO,QAAQ,CACf,KAAK,IAAI,CACT,aACI,CAAS,SAAS,EAAE,CAfZ,GA0BjB,SAAgB,EACd,EACA,EAC8B,CAC9B,OAAO,EAAQ,KAAM,GAAU,CAE7B,IAAK,IAAM,KAAU,EAAM,kBAAmB,CAC5C,IAAM,EAAM,EAAO,UACd,OACD,EAAI,OAAS,SACX,EAAO,OAAS,SAAW,EAAO,OAAS,EAAI,YASjD,EAAI,OAAS,SACX,EAAO,OAAS,SAAW,EAAU,EAAO,IAAK,EAAI,IAAI,EAG3D,EAAI,OAAS,OACX,EAAO,OAAS,OAAS,EAAU,EAAO,IAAK,EAAI,IAAI,EACzD,MAAO,GAGb,MAAO,IACP"}
1
+ {"version":3,"file":"mcp-marketplace-utils.cjs","names":[],"sources":["../../src/utils/mcp-marketplace-utils.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport type {\n IntegrationCatalogEntry as MarketplaceEntry,\n IntegrationConnectionOption,\n IntegrationTransport,\n} from \"@openhands/extensions/integrations\";\n\nexport type { MarketplaceEntry };\n\nexport type McpMarketplaceConnectionOption = IntegrationConnectionOption & {\n provider: \"mcp\";\n transport: IntegrationTransport;\n};\n\nexport function getMcpConnectionOptions(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption[] {\n return entry.connectionOptions.filter(\n (option): option is McpMarketplaceConnectionOption =>\n option.provider === \"mcp\" && !!option.transport,\n );\n}\n\nexport function getDefaultMcpConnectionOption(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption | undefined {\n const options = getMcpConnectionOptions(entry);\n return (\n options.find((option) => option.id === entry.defaultConnectionOptionId) ??\n options[0]\n );\n}\n\nfunction isLocallyInstallableMcpOption(\n option: McpMarketplaceConnectionOption,\n): boolean {\n // The local install modal writes static MCP server config. OAuth options\n // describe hosted redirect flows, so prefer an API/stdio fallback when one\n // exists and leave OAuth as the default connection for hosted integrations.\n return option.auth.strategy !== \"oauth2\";\n}\n\nexport function getInstallableMcpConnectionOption(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption | undefined {\n const options = getMcpConnectionOptions(entry);\n const defaultOption = options.find(\n (option) => option.id === entry.defaultConnectionOptionId,\n );\n if (defaultOption && isLocallyInstallableMcpOption(defaultOption)) {\n return defaultOption;\n }\n return options.find(isLocallyInstallableMcpOption);\n}\n\nexport function getDefaultMcpTransport(\n entry: MarketplaceEntry,\n): IntegrationTransport | undefined {\n return getDefaultMcpConnectionOption(entry)?.transport;\n}\n\nexport function getMcpMarketplaceCatalog(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog.filter((entry) => !!getDefaultMcpConnectionOption(entry));\n}\n\nconst tryUrl = (raw: string): URL | null => {\n try {\n return new URL(raw);\n } catch {\n return null;\n }\n};\n\n/**\n * Loose URL match that ignores query strings, trailing slashes, and\n * default ports. We want clicking \"Linear\" to flag the entry as\n * installed even if the user pasted the URL with extra trailing slash\n * or a different port-equivalent variant.\n *\n * Defensive against runtime data that doesn't match the static type:\n * if either input is not a string (e.g. parsed from an older settings\n * blob), we fall through the URL parsing path and the safe trim\n * fallback below, never calling `.replace` on undefined.\n */\nexport function urlsMatch(a: unknown, b: unknown): boolean {\n const aStr = typeof a === \"string\" ? a : \"\";\n const bStr = typeof b === \"string\" ? b : \"\";\n if (!aStr || !bStr) return false;\n const left = tryUrl(aStr);\n const right = tryUrl(bStr);\n if (!left || !right) {\n return aStr.replace(/\\/+$/, \"\") === bStr.replace(/\\/+$/, \"\");\n }\n return (\n left.protocol === right.protocol &&\n left.host === right.host &&\n left.pathname.replace(/\\/+$/, \"\") === right.pathname.replace(/\\/+$/, \"\")\n );\n}\n\n/**\n * Decide whether a marketplace template is already represented by one\n * of the installed MCP servers. Used to render an \"Installed\" badge on\n * the marketplace tile. Returns the first matching server, or null.\n */\nexport function findInstalledMatch(\n transport: IntegrationTransport,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n return (\n servers.find((server) => transportMatchesServer(transport, server)) ?? null\n );\n}\n\nexport function findInstalledEntryMatch(\n entry: MarketplaceEntry,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n for (const option of getMcpConnectionOptions(entry)) {\n const match = findInstalledMatch(option.transport, servers);\n if (match) return match;\n }\n return null;\n}\n\nfunction transportMatchesServer(\n transport: IntegrationTransport,\n server: MCPServerConfig,\n): boolean {\n if (transport.kind === \"shttp\") {\n const tplUrl = transport.url;\n return (\n server.type === \"shttp\" && !!server.url && urlsMatch(server.url, tplUrl)\n );\n }\n\n if (transport.kind === \"sse\") {\n const tplUrl = transport.url;\n return (\n server.type === \"sse\" && !!server.url && urlsMatch(server.url, tplUrl)\n );\n }\n\n // stdio: match on the registered server name.\n return server.type === \"stdio\" && server.name === transport.serverName;\n}\n\nexport function isMarketplaceEntryAvailable(\n entry: MarketplaceEntry,\n backendKind: \"local\" | \"cloud\",\n): boolean {\n if (!entry.runtimeAvailability || entry.runtimeAvailability === \"all\")\n return true;\n return entry.runtimeAvailability === backendKind;\n}\n\nfunction normalize(query: string): string {\n return query.trim().toLowerCase();\n}\n\n/**\n * Case-insensitive substring match against the catalog entry's\n * user-visible identity (name, description, id, keywords). Empty\n * queries always match.\n */\nexport function getMarketplaceEntriesByPopularity(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog\n .map((entry, index) => ({ entry, index }))\n .sort((a, b) => {\n const byPopularity =\n (b.entry.popularityRank ?? 0) - (a.entry.popularityRank ?? 0);\n return byPopularity || a.index - b.index;\n })\n .map(({ entry }) => entry);\n}\n\nexport function getMarketplaceEntryById(\n id: string,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => entry.id === id);\n}\n\nexport function marketplaceEntryMatchesQuery(\n entry: MarketplaceEntry,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n entry.name,\n entry.description,\n entry.id,\n ...(entry.keywords ?? []),\n ]\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Search match for an installed (already-configured) server. We\n * search the server's own identifying fields and — if it's a catalog\n * entry — its catalog name/keywords too, so typing \"Slack\" matches\n * the installed Slack tile even though the persisted server is just\n * `{ type: \"stdio\", name: \"slack\", ... }`.\n */\nexport function installedServerMatchesQuery(\n server: MCPServerConfig,\n catalogEntry: MarketplaceEntry | undefined,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n server.type,\n \"name\" in server ? server.name : undefined,\n \"command\" in server ? server.command : undefined,\n \"args\" in server ? server.args?.join(\" \") : undefined,\n \"url\" in server ? server.url : undefined,\n catalogEntry?.name,\n catalogEntry?.description,\n catalogEntry?.id,\n ...(catalogEntry?.keywords ?? []),\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Look up the catalog entry that best matches an installed server.\n * Mirrors the lookup used in `installed-server-card.tsx` for\n * rendering the friendly icon.\n */\nexport function findCatalogEntryForServer(\n server: MCPServerConfig,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => {\n // Check every MCP option rather than only the default. Some unified\n // integration entries default to OAuth-hosted MCP while still exposing\n // an API/stdio option; existing installed servers should match either.\n return getMcpConnectionOptions(entry).some((option) =>\n transportMatchesServer(option.transport, server),\n );\n });\n}\n"],"mappings":"6CAcA,SAAgB,EACd,EACkC,CAClC,OAAO,EAAM,kBAAkB,OAC5B,GACC,EAAO,WAAa,OAAS,CAAC,CAAC,EAAO,UACzC,CAGH,SAAgB,EACd,EAC4C,CAC5C,IAAM,EAAU,EAAwB,EAAM,CAC9C,OACE,EAAQ,KAAM,GAAW,EAAO,KAAO,EAAM,0BAA0B,EACvE,EAAQ,GAIZ,SAAS,EACP,EACS,CAIT,OAAO,EAAO,KAAK,WAAa,SAGlC,SAAgB,EACd,EAC4C,CAC5C,IAAM,EAAU,EAAwB,EAAM,CACxC,EAAgB,EAAQ,KAC3B,GAAW,EAAO,KAAO,EAAM,0BACjC,CAID,OAHI,GAAiB,EAA8B,EAAc,CACxD,EAEF,EAAQ,KAAK,EAA8B,CAGpD,SAAgB,EACd,EACkC,CAClC,OAAO,EAA8B,EAAM,EAAE,UAG/C,SAAgB,EACd,EACoB,CACpB,OAAO,EAAQ,OAAQ,GAAU,CAAC,CAAC,EAA8B,EAAM,CAAC,CAG1E,IAAM,EAAU,GAA4B,CAC1C,GAAI,CACF,OAAO,IAAI,IAAI,EAAI,MACb,CACN,OAAO,OAeX,SAAgB,EAAU,EAAY,EAAqB,CACzD,IAAM,EAAO,OAAO,GAAM,SAAW,EAAI,GACnC,EAAO,OAAO,GAAM,SAAW,EAAI,GACzC,GAAI,CAAC,GAAQ,CAAC,EAAM,MAAO,GAC3B,IAAM,EAAO,EAAO,EAAK,CACnB,EAAQ,EAAO,EAAK,CAI1B,MAHI,CAAC,GAAQ,CAAC,EACL,EAAK,QAAQ,OAAQ,GAAG,GAAK,EAAK,QAAQ,OAAQ,GAAG,CAG5D,EAAK,WAAa,EAAM,UACxB,EAAK,OAAS,EAAM,MACpB,EAAK,SAAS,QAAQ,OAAQ,GAAG,GAAK,EAAM,SAAS,QAAQ,OAAQ,GAAG,CA6B5E,SAAS,EACP,EACA,EACS,CACT,GAAI,EAAU,OAAS,QAAS,CAC9B,IAAM,EAAS,EAAU,IACzB,OACE,EAAO,OAAS,SAAW,CAAC,CAAC,EAAO,KAAO,EAAU,EAAO,IAAK,EAAO,CAI5E,GAAI,EAAU,OAAS,MAAO,CAC5B,IAAM,EAAS,EAAU,IACzB,OACE,EAAO,OAAS,OAAS,CAAC,CAAC,EAAO,KAAO,EAAU,EAAO,IAAK,EAAO,CAK1E,OAAO,EAAO,OAAS,SAAW,EAAO,OAAS,EAAU,WAG9D,SAAgB,EACd,EACA,EACS,CAGT,MAFI,CAAC,EAAM,qBAAuB,EAAM,sBAAwB,MACvD,GACF,EAAM,sBAAwB,EAGvC,SAAS,EAAU,EAAuB,CACxC,OAAO,EAAM,MAAM,CAAC,aAAa,CAQnC,SAAgB,EACd,EACoB,CACpB,OAAO,EACJ,KAAK,EAAO,KAAW,CAAE,QAAO,QAAO,EAAE,CACzC,MAAM,EAAG,KAEL,EAAE,MAAM,gBAAkB,IAAM,EAAE,MAAM,gBAAkB,IACtC,EAAE,MAAQ,EAAE,MACnC,CACD,KAAK,CAAE,WAAY,EAAM,CAU9B,SAAgB,EACd,EACA,EACS,CACT,IAAM,EAAI,EAAU,EAAS,CAU7B,OATK,EACY,CACf,EAAM,KACN,EAAM,YACN,EAAM,GACN,GAAI,EAAM,UAAY,EAAE,CACzB,CACE,KAAK,IAAI,CACT,aACI,CAAS,SAAS,EAAE,CATZ,GAmBjB,SAAgB,EACd,EACA,EACA,EACS,CACT,IAAM,EAAI,EAAU,EAAS,CAgB7B,OAfK,EACY,CACf,EAAO,KACP,SAAU,EAAS,EAAO,KAAO,IAAA,GACjC,YAAa,EAAS,EAAO,QAAU,IAAA,GACvC,SAAU,EAAS,EAAO,MAAM,KAAK,IAAI,CAAG,IAAA,GAC5C,QAAS,EAAS,EAAO,IAAM,IAAA,GAC/B,GAAc,KACd,GAAc,YACd,GAAc,GACd,GAAI,GAAc,UAAY,EAAE,CACjC,CACE,OAAO,QAAQ,CACf,KAAK,IAAI,CACT,aACI,CAAS,SAAS,EAAE,CAfZ,GAuBjB,SAAgB,EACd,EACA,EAC8B,CAC9B,OAAO,EAAQ,KAAM,GAIZ,EAAwB,EAAM,CAAC,KAAM,GAC1C,EAAuB,EAAO,UAAW,EAAO,CACjD,CACD"}
@@ -1,5 +1,15 @@
1
1
  import { MCPServerConfig } from "#/types/mcp-server";
2
- import type { IntegrationCatalogEntry as MarketplaceEntry, IntegrationTransport as MarketplaceTemplate } from "@openhands/extensions/integrations";
2
+ import type { IntegrationCatalogEntry as MarketplaceEntry, IntegrationConnectionOption, IntegrationTransport } from "@openhands/extensions/integrations";
3
+ export type { MarketplaceEntry };
4
+ export type McpMarketplaceConnectionOption = IntegrationConnectionOption & {
5
+ provider: "mcp";
6
+ transport: IntegrationTransport;
7
+ };
8
+ export declare function getMcpConnectionOptions(entry: MarketplaceEntry): McpMarketplaceConnectionOption[];
9
+ export declare function getDefaultMcpConnectionOption(entry: MarketplaceEntry): McpMarketplaceConnectionOption | undefined;
10
+ export declare function getInstallableMcpConnectionOption(entry: MarketplaceEntry): McpMarketplaceConnectionOption | undefined;
11
+ export declare function getDefaultMcpTransport(entry: MarketplaceEntry): IntegrationTransport | undefined;
12
+ export declare function getMcpMarketplaceCatalog(catalog: MarketplaceEntry[]): MarketplaceEntry[];
3
13
  /**
4
14
  * Loose URL match that ignores query strings, trailing slashes, and
5
15
  * default ports. We want clicking "Linear" to flag the entry as
@@ -12,29 +22,13 @@ import type { IntegrationCatalogEntry as MarketplaceEntry, IntegrationTransport
12
22
  * fallback below, never calling `.replace` on undefined.
13
23
  */
14
24
  export declare function urlsMatch(a: unknown, b: unknown): boolean;
15
- /**
16
- * Get the default transport template from an integration catalog entry.
17
- * Integrations may have multiple connection options; we use the default
18
- * one (or the first if no default is specified). Only MCP-backed options
19
- * have a `transport` field.
20
- */
21
- export declare function getDefaultTemplate(entry: MarketplaceEntry): MarketplaceTemplate | undefined;
22
- /**
23
- * Get the stdio (API key-based) transport template from an integration entry.
24
- * Many integrations have multiple connection options (e.g., OAuth + stdio).
25
- * Since OAuth isn't implemented in the UI yet, the install modal should use
26
- * this function to get the stdio-based option that can be configured with
27
- * API keys/tokens.
28
- *
29
- * Falls back to getDefaultTemplate if no stdio option exists.
30
- */
31
- export declare function getInstallableTemplate(entry: MarketplaceEntry): MarketplaceTemplate | undefined;
32
25
  /**
33
26
  * Decide whether a marketplace template is already represented by one
34
27
  * of the installed MCP servers. Used to render an "Installed" badge on
35
28
  * the marketplace tile. Returns the first matching server, or null.
36
29
  */
37
- export declare function findInstalledMatch(template: MarketplaceTemplate, servers: MCPServerConfig[]): MCPServerConfig | null;
30
+ export declare function findInstalledMatch(transport: IntegrationTransport, servers: MCPServerConfig[]): MCPServerConfig | null;
31
+ export declare function findInstalledEntryMatch(entry: MarketplaceEntry, servers: MCPServerConfig[]): MCPServerConfig | null;
38
32
  export declare function isMarketplaceEntryAvailable(entry: MarketplaceEntry, backendKind: "local" | "cloud"): boolean;
39
33
  /**
40
34
  * Case-insensitive substring match against the catalog entry's
@@ -56,8 +50,5 @@ export declare function installedServerMatchesQuery(server: MCPServerConfig, cat
56
50
  * Look up the catalog entry that best matches an installed server.
57
51
  * Mirrors the lookup used in `installed-server-card.tsx` for
58
52
  * rendering the friendly icon.
59
- *
60
- * Since an entry may have multiple connection options (e.g., OAuth + stdio),
61
- * we check ALL templates in the entry's connectionOptions, not just the default.
62
53
  */
63
54
  export declare function findCatalogEntryForServer(server: MCPServerConfig, catalog: MarketplaceEntry[]): MarketplaceEntry | undefined;
@@ -1,38 +1,62 @@
1
1
  //#region src/utils/mcp-marketplace-utils.ts
2
- var e = (e) => {
2
+ function e(e) {
3
+ return e.connectionOptions.filter((e) => e.provider === "mcp" && !!e.transport);
4
+ }
5
+ function t(t) {
6
+ let n = e(t);
7
+ return n.find((e) => e.id === t.defaultConnectionOptionId) ?? n[0];
8
+ }
9
+ function n(e) {
10
+ return e.auth.strategy !== "oauth2";
11
+ }
12
+ function r(t) {
13
+ let r = e(t), i = r.find((e) => e.id === t.defaultConnectionOptionId);
14
+ return i && n(i) ? i : r.find(n);
15
+ }
16
+ function i(e) {
17
+ return t(e)?.transport;
18
+ }
19
+ function a(e) {
20
+ return e.filter((e) => !!t(e));
21
+ }
22
+ var o = (e) => {
3
23
  try {
4
24
  return new URL(e);
5
25
  } catch {
6
26
  return null;
7
27
  }
8
28
  };
9
- function t(t, n) {
10
- let r = typeof t == "string" ? t : "", i = typeof n == "string" ? n : "";
11
- if (!r || !i) return !1;
12
- let a = e(r), o = e(i);
13
- return !a || !o ? r.replace(/\/+$/, "") === i.replace(/\/+$/, "") : a.protocol === o.protocol && a.host === o.host && a.pathname.replace(/\/+$/, "") === o.pathname.replace(/\/+$/, "");
14
- }
15
- function n(e) {
16
- return (e.connectionOptions.find((t) => t.id === e.defaultConnectionOptionId) ?? e.connectionOptions[0])?.transport;
29
+ function s(e, t) {
30
+ let n = typeof e == "string" ? e : "", r = typeof t == "string" ? t : "";
31
+ if (!n || !r) return !1;
32
+ let i = o(n), a = o(r);
33
+ return !i || !a ? n.replace(/\/+$/, "") === r.replace(/\/+$/, "") : i.protocol === a.protocol && i.host === a.host && i.pathname.replace(/\/+$/, "") === a.pathname.replace(/\/+$/, "");
17
34
  }
18
- function r(e) {
19
- let t = e.connectionOptions.find((e) => e.transport?.kind === "stdio");
20
- return t?.transport ? t.transport : n(e);
35
+ function c(e, t) {
36
+ if (e.kind === "shttp") {
37
+ let n = e.url;
38
+ return t.type === "shttp" && !!t.url && s(t.url, n);
39
+ }
40
+ if (e.kind === "sse") {
41
+ let n = e.url;
42
+ return t.type === "sse" && !!t.url && s(t.url, n);
43
+ }
44
+ return t.type === "stdio" && t.name === e.serverName;
21
45
  }
22
- function i(e, t) {
46
+ function l(e, t) {
23
47
  return !e.runtimeAvailability || e.runtimeAvailability === "all" ? !0 : e.runtimeAvailability === t;
24
48
  }
25
- function a(e) {
49
+ function u(e) {
26
50
  return e.trim().toLowerCase();
27
51
  }
28
- function o(e) {
52
+ function d(e) {
29
53
  return e.map((e, t) => ({
30
54
  entry: e,
31
55
  index: t
32
56
  })).sort((e, t) => (t.entry.popularityRank ?? 0) - (e.entry.popularityRank ?? 0) || e.index - t.index).map(({ entry: e }) => e);
33
57
  }
34
- function s(e, t) {
35
- let n = a(t);
58
+ function f(e, t) {
59
+ let n = u(t);
36
60
  return n ? [
37
61
  e.name,
38
62
  e.description,
@@ -40,8 +64,8 @@ function s(e, t) {
40
64
  ...e.keywords ?? []
41
65
  ].join(" ").toLowerCase().includes(n) : !0;
42
66
  }
43
- function c(e, t, n) {
44
- let r = a(n);
67
+ function p(e, t, n) {
68
+ let r = u(n);
45
69
  return r ? [
46
70
  e.type,
47
71
  "name" in e ? e.name : void 0,
@@ -54,16 +78,10 @@ function c(e, t, n) {
54
78
  ...t?.keywords ?? []
55
79
  ].filter(Boolean).join(" ").toLowerCase().includes(r) : !0;
56
80
  }
57
- function l(e, n) {
58
- return n.find((n) => {
59
- for (let r of n.connectionOptions) {
60
- let n = r.transport;
61
- if (n && (n.kind === "stdio" && e.type === "stdio" && e.name === n.serverName || n.kind === "shttp" && e.type === "shttp" && t(e.url, n.url) || n.kind === "sse" && e.type === "sse" && t(e.url, n.url))) return !0;
62
- }
63
- return !1;
64
- });
81
+ function m(t, n) {
82
+ return n.find((n) => e(n).some((e) => c(e.transport, t)));
65
83
  }
66
84
  //#endregion
67
- export { l as findCatalogEntryForServer, n as getDefaultTemplate, r as getInstallableTemplate, o as getMarketplaceEntriesByPopularity, c as installedServerMatchesQuery, i as isMarketplaceEntryAvailable, s as marketplaceEntryMatchesQuery };
85
+ export { m as findCatalogEntryForServer, i as getDefaultMcpTransport, r as getInstallableMcpConnectionOption, d as getMarketplaceEntriesByPopularity, a as getMcpMarketplaceCatalog, p as installedServerMatchesQuery, l as isMarketplaceEntryAvailable, f as marketplaceEntryMatchesQuery };
68
86
 
69
87
  //# sourceMappingURL=mcp-marketplace-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-marketplace-utils.js","names":[],"sources":["../../src/utils/mcp-marketplace-utils.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport type {\n IntegrationCatalogEntry as MarketplaceEntry,\n IntegrationTransport as MarketplaceTemplate,\n} from \"@openhands/extensions/integrations\";\n\nconst tryUrl = (raw: string): URL | null => {\n try {\n return new URL(raw);\n } catch {\n return null;\n }\n};\n\n/**\n * Loose URL match that ignores query strings, trailing slashes, and\n * default ports. We want clicking \"Linear\" to flag the entry as\n * installed even if the user pasted the URL with extra trailing slash\n * or a different port-equivalent variant.\n *\n * Defensive against runtime data that doesn't match the static type:\n * if either input is not a string (e.g. parsed from an older settings\n * blob), we fall through the URL parsing path and the safe trim\n * fallback below, never calling `.replace` on undefined.\n */\nexport function urlsMatch(a: unknown, b: unknown): boolean {\n const aStr = typeof a === \"string\" ? a : \"\";\n const bStr = typeof b === \"string\" ? b : \"\";\n if (!aStr || !bStr) return false;\n const left = tryUrl(aStr);\n const right = tryUrl(bStr);\n if (!left || !right) {\n return aStr.replace(/\\/+$/, \"\") === bStr.replace(/\\/+$/, \"\");\n }\n return (\n left.protocol === right.protocol &&\n left.host === right.host &&\n left.pathname.replace(/\\/+$/, \"\") === right.pathname.replace(/\\/+$/, \"\")\n );\n}\n\n/**\n * Get the default transport template from an integration catalog entry.\n * Integrations may have multiple connection options; we use the default\n * one (or the first if no default is specified). Only MCP-backed options\n * have a `transport` field.\n */\nexport function getDefaultTemplate(\n entry: MarketplaceEntry,\n): MarketplaceTemplate | undefined {\n const option =\n entry.connectionOptions.find(\n (o) => o.id === entry.defaultConnectionOptionId,\n ) ?? entry.connectionOptions[0];\n return option?.transport;\n}\n\n/**\n * Get the stdio (API key-based) transport template from an integration entry.\n * Many integrations have multiple connection options (e.g., OAuth + stdio).\n * Since OAuth isn't implemented in the UI yet, the install modal should use\n * this function to get the stdio-based option that can be configured with\n * API keys/tokens.\n *\n * Falls back to getDefaultTemplate if no stdio option exists.\n */\nexport function getInstallableTemplate(\n entry: MarketplaceEntry,\n): MarketplaceTemplate | undefined {\n // First, try to find a stdio option (API key-based, what we can actually install)\n const stdioOption = entry.connectionOptions.find(\n (o) => o.transport?.kind === \"stdio\",\n );\n if (stdioOption?.transport) return stdioOption.transport;\n\n // Fall back to the default template (could be shttp/sse with api_key)\n return getDefaultTemplate(entry);\n}\n\n/**\n * Decide whether a marketplace template is already represented by one\n * of the installed MCP servers. Used to render an \"Installed\" badge on\n * the marketplace tile. Returns the first matching server, or null.\n */\nexport function findInstalledMatch(\n template: MarketplaceTemplate,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n if (template.kind === \"shttp\") {\n const tplUrl = template.url;\n if (!tplUrl) return null;\n return (\n servers.find(\n (s) => s.type === \"shttp\" && !!s.url && urlsMatch(s.url, tplUrl),\n ) ?? null\n );\n }\n\n if (template.kind === \"sse\") {\n const tplUrl = template.url;\n if (!tplUrl) return null;\n return (\n servers.find(\n (s) => s.type === \"sse\" && !!s.url && urlsMatch(s.url, tplUrl),\n ) ?? null\n );\n }\n\n // stdio: match on the registered server name.\n return (\n servers.find((s) => s.type === \"stdio\" && s.name === template.serverName) ??\n null\n );\n}\n\nexport function isMarketplaceEntryAvailable(\n entry: MarketplaceEntry,\n backendKind: \"local\" | \"cloud\",\n): boolean {\n if (!entry.runtimeAvailability || entry.runtimeAvailability === \"all\")\n return true;\n return entry.runtimeAvailability === backendKind;\n}\n\nfunction normalize(query: string): string {\n return query.trim().toLowerCase();\n}\n\n/**\n * Case-insensitive substring match against the catalog entry's\n * user-visible identity (name, description, id, keywords). Empty\n * queries always match.\n */\nexport function getMarketplaceEntriesByPopularity(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog\n .map((entry, index) => ({ entry, index }))\n .sort((a, b) => {\n const byPopularity =\n (b.entry.popularityRank ?? 0) - (a.entry.popularityRank ?? 0);\n return byPopularity || a.index - b.index;\n })\n .map(({ entry }) => entry);\n}\n\nexport function getMarketplaceEntryById(\n id: string,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => entry.id === id);\n}\n\nexport function marketplaceEntryMatchesQuery(\n entry: MarketplaceEntry,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n entry.name,\n entry.description,\n entry.id,\n ...(entry.keywords ?? []),\n ]\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Search match for an installed (already-configured) server. We\n * search the server's own identifying fields and — if it's a catalog\n * entry — its catalog name/keywords too, so typing \"Slack\" matches\n * the installed Slack tile even though the persisted server is just\n * `{ type: \"stdio\", name: \"slack\", ... }`.\n */\nexport function installedServerMatchesQuery(\n server: MCPServerConfig,\n catalogEntry: MarketplaceEntry | undefined,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n server.type,\n \"name\" in server ? server.name : undefined,\n \"command\" in server ? server.command : undefined,\n \"args\" in server ? server.args?.join(\" \") : undefined,\n \"url\" in server ? server.url : undefined,\n catalogEntry?.name,\n catalogEntry?.description,\n catalogEntry?.id,\n ...(catalogEntry?.keywords ?? []),\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Look up the catalog entry that best matches an installed server.\n * Mirrors the lookup used in `installed-server-card.tsx` for\n * rendering the friendly icon.\n *\n * Since an entry may have multiple connection options (e.g., OAuth + stdio),\n * we check ALL templates in the entry's connectionOptions, not just the default.\n */\nexport function findCatalogEntryForServer(\n server: MCPServerConfig,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => {\n // Check all connection options, not just the default\n for (const option of entry.connectionOptions) {\n const tpl = option.transport;\n if (!tpl) continue;\n if (tpl.kind === \"stdio\") {\n if (server.type === \"stdio\" && server.name === tpl.serverName)\n return true;\n }\n // Reuse the same loose URL match as `findInstalledMatch` so a\n // server whose URL was normalized by the backend (trailing slash\n // stripped, query string dropped, etc.) still gets paired with\n // its catalog tile — otherwise the installed-servers list would\n // render the generic icon while the marketplace shows the\n // entry as installed, which is confusing.\n if (tpl.kind === \"shttp\") {\n if (server.type === \"shttp\" && urlsMatch(server.url, tpl.url))\n return true;\n }\n if (tpl.kind === \"sse\") {\n if (server.type === \"sse\" && urlsMatch(server.url, tpl.url))\n return true;\n }\n }\n return false;\n });\n}\n"],"mappings":";AAMA,IAAM,KAAU,MAA4B;AAC1C,KAAI;AACF,SAAO,IAAI,IAAI,EAAI;SACb;AACN,SAAO;;;AAeX,SAAgB,EAAU,GAAY,GAAqB;CACzD,IAAM,IAAO,OAAO,KAAM,WAAW,IAAI,IACnC,IAAO,OAAO,KAAM,WAAW,IAAI;AACzC,KAAI,CAAC,KAAQ,CAAC,EAAM,QAAO;CAC3B,IAAM,IAAO,EAAO,EAAK,EACnB,IAAQ,EAAO,EAAK;AAI1B,QAHI,CAAC,KAAQ,CAAC,IACL,EAAK,QAAQ,QAAQ,GAAG,KAAK,EAAK,QAAQ,QAAQ,GAAG,GAG5D,EAAK,aAAa,EAAM,YACxB,EAAK,SAAS,EAAM,QACpB,EAAK,SAAS,QAAQ,QAAQ,GAAG,KAAK,EAAM,SAAS,QAAQ,QAAQ,GAAG;;AAU5E,SAAgB,EACd,GACiC;AAKjC,SAHE,EAAM,kBAAkB,MACrB,MAAM,EAAE,OAAO,EAAM,0BACvB,IAAI,EAAM,kBAAkB,KAChB;;AAYjB,SAAgB,EACd,GACiC;CAEjC,IAAM,IAAc,EAAM,kBAAkB,MACzC,MAAM,EAAE,WAAW,SAAS,QAC9B;AAID,QAHI,GAAa,YAAkB,EAAY,YAGxC,EAAmB,EAAM;;AAuClC,SAAgB,EACd,GACA,GACS;AAGT,QAFI,CAAC,EAAM,uBAAuB,EAAM,wBAAwB,QACvD,KACF,EAAM,wBAAwB;;AAGvC,SAAS,EAAU,GAAuB;AACxC,QAAO,EAAM,MAAM,CAAC,aAAa;;AAQnC,SAAgB,EACd,GACoB;AACpB,QAAO,EACJ,KAAK,GAAO,OAAW;EAAE;EAAO;EAAO,EAAE,CACzC,MAAM,GAAG,OAEL,EAAE,MAAM,kBAAkB,MAAM,EAAE,MAAM,kBAAkB,MACtC,EAAE,QAAQ,EAAE,MACnC,CACD,KAAK,EAAE,eAAY,EAAM;;AAU9B,SAAgB,EACd,GACA,GACS;CACT,IAAM,IAAI,EAAU,EAAS;AAU7B,QATK,IACY;EACf,EAAM;EACN,EAAM;EACN,EAAM;EACN,GAAI,EAAM,YAAY,EAAE;EACzB,CACE,KAAK,IAAI,CACT,aACI,CAAS,SAAS,EAAE,GATZ;;AAmBjB,SAAgB,EACd,GACA,GACA,GACS;CACT,IAAM,IAAI,EAAU,EAAS;AAgB7B,QAfK,IACY;EACf,EAAO;EACP,UAAU,IAAS,EAAO,OAAO,KAAA;EACjC,aAAa,IAAS,EAAO,UAAU,KAAA;EACvC,UAAU,IAAS,EAAO,MAAM,KAAK,IAAI,GAAG,KAAA;EAC5C,SAAS,IAAS,EAAO,MAAM,KAAA;EAC/B,GAAc;EACd,GAAc;EACd,GAAc;EACd,GAAI,GAAc,YAAY,EAAE;EACjC,CACE,OAAO,QAAQ,CACf,KAAK,IAAI,CACT,aACI,CAAS,SAAS,EAAE,GAfZ;;AA0BjB,SAAgB,EACd,GACA,GAC8B;AAC9B,QAAO,EAAQ,MAAM,MAAU;AAE7B,OAAK,IAAM,KAAU,EAAM,mBAAmB;GAC5C,IAAM,IAAM,EAAO;AACd,aACD,EAAI,SAAS,WACX,EAAO,SAAS,WAAW,EAAO,SAAS,EAAI,cASjD,EAAI,SAAS,WACX,EAAO,SAAS,WAAW,EAAU,EAAO,KAAK,EAAI,IAAI,IAG3D,EAAI,SAAS,SACX,EAAO,SAAS,SAAS,EAAU,EAAO,KAAK,EAAI,IAAI,EACzD,QAAO;;AAGb,SAAO;GACP"}
1
+ {"version":3,"file":"mcp-marketplace-utils.js","names":[],"sources":["../../src/utils/mcp-marketplace-utils.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport type {\n IntegrationCatalogEntry as MarketplaceEntry,\n IntegrationConnectionOption,\n IntegrationTransport,\n} from \"@openhands/extensions/integrations\";\n\nexport type { MarketplaceEntry };\n\nexport type McpMarketplaceConnectionOption = IntegrationConnectionOption & {\n provider: \"mcp\";\n transport: IntegrationTransport;\n};\n\nexport function getMcpConnectionOptions(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption[] {\n return entry.connectionOptions.filter(\n (option): option is McpMarketplaceConnectionOption =>\n option.provider === \"mcp\" && !!option.transport,\n );\n}\n\nexport function getDefaultMcpConnectionOption(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption | undefined {\n const options = getMcpConnectionOptions(entry);\n return (\n options.find((option) => option.id === entry.defaultConnectionOptionId) ??\n options[0]\n );\n}\n\nfunction isLocallyInstallableMcpOption(\n option: McpMarketplaceConnectionOption,\n): boolean {\n // The local install modal writes static MCP server config. OAuth options\n // describe hosted redirect flows, so prefer an API/stdio fallback when one\n // exists and leave OAuth as the default connection for hosted integrations.\n return option.auth.strategy !== \"oauth2\";\n}\n\nexport function getInstallableMcpConnectionOption(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption | undefined {\n const options = getMcpConnectionOptions(entry);\n const defaultOption = options.find(\n (option) => option.id === entry.defaultConnectionOptionId,\n );\n if (defaultOption && isLocallyInstallableMcpOption(defaultOption)) {\n return defaultOption;\n }\n return options.find(isLocallyInstallableMcpOption);\n}\n\nexport function getDefaultMcpTransport(\n entry: MarketplaceEntry,\n): IntegrationTransport | undefined {\n return getDefaultMcpConnectionOption(entry)?.transport;\n}\n\nexport function getMcpMarketplaceCatalog(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog.filter((entry) => !!getDefaultMcpConnectionOption(entry));\n}\n\nconst tryUrl = (raw: string): URL | null => {\n try {\n return new URL(raw);\n } catch {\n return null;\n }\n};\n\n/**\n * Loose URL match that ignores query strings, trailing slashes, and\n * default ports. We want clicking \"Linear\" to flag the entry as\n * installed even if the user pasted the URL with extra trailing slash\n * or a different port-equivalent variant.\n *\n * Defensive against runtime data that doesn't match the static type:\n * if either input is not a string (e.g. parsed from an older settings\n * blob), we fall through the URL parsing path and the safe trim\n * fallback below, never calling `.replace` on undefined.\n */\nexport function urlsMatch(a: unknown, b: unknown): boolean {\n const aStr = typeof a === \"string\" ? a : \"\";\n const bStr = typeof b === \"string\" ? b : \"\";\n if (!aStr || !bStr) return false;\n const left = tryUrl(aStr);\n const right = tryUrl(bStr);\n if (!left || !right) {\n return aStr.replace(/\\/+$/, \"\") === bStr.replace(/\\/+$/, \"\");\n }\n return (\n left.protocol === right.protocol &&\n left.host === right.host &&\n left.pathname.replace(/\\/+$/, \"\") === right.pathname.replace(/\\/+$/, \"\")\n );\n}\n\n/**\n * Decide whether a marketplace template is already represented by one\n * of the installed MCP servers. Used to render an \"Installed\" badge on\n * the marketplace tile. Returns the first matching server, or null.\n */\nexport function findInstalledMatch(\n transport: IntegrationTransport,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n return (\n servers.find((server) => transportMatchesServer(transport, server)) ?? null\n );\n}\n\nexport function findInstalledEntryMatch(\n entry: MarketplaceEntry,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n for (const option of getMcpConnectionOptions(entry)) {\n const match = findInstalledMatch(option.transport, servers);\n if (match) return match;\n }\n return null;\n}\n\nfunction transportMatchesServer(\n transport: IntegrationTransport,\n server: MCPServerConfig,\n): boolean {\n if (transport.kind === \"shttp\") {\n const tplUrl = transport.url;\n return (\n server.type === \"shttp\" && !!server.url && urlsMatch(server.url, tplUrl)\n );\n }\n\n if (transport.kind === \"sse\") {\n const tplUrl = transport.url;\n return (\n server.type === \"sse\" && !!server.url && urlsMatch(server.url, tplUrl)\n );\n }\n\n // stdio: match on the registered server name.\n return server.type === \"stdio\" && server.name === transport.serverName;\n}\n\nexport function isMarketplaceEntryAvailable(\n entry: MarketplaceEntry,\n backendKind: \"local\" | \"cloud\",\n): boolean {\n if (!entry.runtimeAvailability || entry.runtimeAvailability === \"all\")\n return true;\n return entry.runtimeAvailability === backendKind;\n}\n\nfunction normalize(query: string): string {\n return query.trim().toLowerCase();\n}\n\n/**\n * Case-insensitive substring match against the catalog entry's\n * user-visible identity (name, description, id, keywords). Empty\n * queries always match.\n */\nexport function getMarketplaceEntriesByPopularity(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog\n .map((entry, index) => ({ entry, index }))\n .sort((a, b) => {\n const byPopularity =\n (b.entry.popularityRank ?? 0) - (a.entry.popularityRank ?? 0);\n return byPopularity || a.index - b.index;\n })\n .map(({ entry }) => entry);\n}\n\nexport function getMarketplaceEntryById(\n id: string,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => entry.id === id);\n}\n\nexport function marketplaceEntryMatchesQuery(\n entry: MarketplaceEntry,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n entry.name,\n entry.description,\n entry.id,\n ...(entry.keywords ?? []),\n ]\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Search match for an installed (already-configured) server. We\n * search the server's own identifying fields and — if it's a catalog\n * entry — its catalog name/keywords too, so typing \"Slack\" matches\n * the installed Slack tile even though the persisted server is just\n * `{ type: \"stdio\", name: \"slack\", ... }`.\n */\nexport function installedServerMatchesQuery(\n server: MCPServerConfig,\n catalogEntry: MarketplaceEntry | undefined,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n server.type,\n \"name\" in server ? server.name : undefined,\n \"command\" in server ? server.command : undefined,\n \"args\" in server ? server.args?.join(\" \") : undefined,\n \"url\" in server ? server.url : undefined,\n catalogEntry?.name,\n catalogEntry?.description,\n catalogEntry?.id,\n ...(catalogEntry?.keywords ?? []),\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Look up the catalog entry that best matches an installed server.\n * Mirrors the lookup used in `installed-server-card.tsx` for\n * rendering the friendly icon.\n */\nexport function findCatalogEntryForServer(\n server: MCPServerConfig,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => {\n // Check every MCP option rather than only the default. Some unified\n // integration entries default to OAuth-hosted MCP while still exposing\n // an API/stdio option; existing installed servers should match either.\n return getMcpConnectionOptions(entry).some((option) =>\n transportMatchesServer(option.transport, server),\n );\n });\n}\n"],"mappings":";AAcA,SAAgB,EACd,GACkC;AAClC,QAAO,EAAM,kBAAkB,QAC5B,MACC,EAAO,aAAa,SAAS,CAAC,CAAC,EAAO,UACzC;;AAGH,SAAgB,EACd,GAC4C;CAC5C,IAAM,IAAU,EAAwB,EAAM;AAC9C,QACE,EAAQ,MAAM,MAAW,EAAO,OAAO,EAAM,0BAA0B,IACvE,EAAQ;;AAIZ,SAAS,EACP,GACS;AAIT,QAAO,EAAO,KAAK,aAAa;;AAGlC,SAAgB,EACd,GAC4C;CAC5C,IAAM,IAAU,EAAwB,EAAM,EACxC,IAAgB,EAAQ,MAC3B,MAAW,EAAO,OAAO,EAAM,0BACjC;AAID,QAHI,KAAiB,EAA8B,EAAc,GACxD,IAEF,EAAQ,KAAK,EAA8B;;AAGpD,SAAgB,EACd,GACkC;AAClC,QAAO,EAA8B,EAAM,EAAE;;AAG/C,SAAgB,EACd,GACoB;AACpB,QAAO,EAAQ,QAAQ,MAAU,CAAC,CAAC,EAA8B,EAAM,CAAC;;AAG1E,IAAM,KAAU,MAA4B;AAC1C,KAAI;AACF,SAAO,IAAI,IAAI,EAAI;SACb;AACN,SAAO;;;AAeX,SAAgB,EAAU,GAAY,GAAqB;CACzD,IAAM,IAAO,OAAO,KAAM,WAAW,IAAI,IACnC,IAAO,OAAO,KAAM,WAAW,IAAI;AACzC,KAAI,CAAC,KAAQ,CAAC,EAAM,QAAO;CAC3B,IAAM,IAAO,EAAO,EAAK,EACnB,IAAQ,EAAO,EAAK;AAI1B,QAHI,CAAC,KAAQ,CAAC,IACL,EAAK,QAAQ,QAAQ,GAAG,KAAK,EAAK,QAAQ,QAAQ,GAAG,GAG5D,EAAK,aAAa,EAAM,YACxB,EAAK,SAAS,EAAM,QACpB,EAAK,SAAS,QAAQ,QAAQ,GAAG,KAAK,EAAM,SAAS,QAAQ,QAAQ,GAAG;;AA6B5E,SAAS,EACP,GACA,GACS;AACT,KAAI,EAAU,SAAS,SAAS;EAC9B,IAAM,IAAS,EAAU;AACzB,SACE,EAAO,SAAS,WAAW,CAAC,CAAC,EAAO,OAAO,EAAU,EAAO,KAAK,EAAO;;AAI5E,KAAI,EAAU,SAAS,OAAO;EAC5B,IAAM,IAAS,EAAU;AACzB,SACE,EAAO,SAAS,SAAS,CAAC,CAAC,EAAO,OAAO,EAAU,EAAO,KAAK,EAAO;;AAK1E,QAAO,EAAO,SAAS,WAAW,EAAO,SAAS,EAAU;;AAG9D,SAAgB,EACd,GACA,GACS;AAGT,QAFI,CAAC,EAAM,uBAAuB,EAAM,wBAAwB,QACvD,KACF,EAAM,wBAAwB;;AAGvC,SAAS,EAAU,GAAuB;AACxC,QAAO,EAAM,MAAM,CAAC,aAAa;;AAQnC,SAAgB,EACd,GACoB;AACpB,QAAO,EACJ,KAAK,GAAO,OAAW;EAAE;EAAO;EAAO,EAAE,CACzC,MAAM,GAAG,OAEL,EAAE,MAAM,kBAAkB,MAAM,EAAE,MAAM,kBAAkB,MACtC,EAAE,QAAQ,EAAE,MACnC,CACD,KAAK,EAAE,eAAY,EAAM;;AAU9B,SAAgB,EACd,GACA,GACS;CACT,IAAM,IAAI,EAAU,EAAS;AAU7B,QATK,IACY;EACf,EAAM;EACN,EAAM;EACN,EAAM;EACN,GAAI,EAAM,YAAY,EAAE;EACzB,CACE,KAAK,IAAI,CACT,aACI,CAAS,SAAS,EAAE,GATZ;;AAmBjB,SAAgB,EACd,GACA,GACA,GACS;CACT,IAAM,IAAI,EAAU,EAAS;AAgB7B,QAfK,IACY;EACf,EAAO;EACP,UAAU,IAAS,EAAO,OAAO,KAAA;EACjC,aAAa,IAAS,EAAO,UAAU,KAAA;EACvC,UAAU,IAAS,EAAO,MAAM,KAAK,IAAI,GAAG,KAAA;EAC5C,SAAS,IAAS,EAAO,MAAM,KAAA;EAC/B,GAAc;EACd,GAAc;EACd,GAAc;EACd,GAAI,GAAc,YAAY,EAAE;EACjC,CACE,OAAO,QAAQ,CACf,KAAK,IAAI,CACT,aACI,CAAS,SAAS,EAAE,GAfZ;;AAuBjB,SAAgB,EACd,GACA,GAC8B;AAC9B,QAAO,EAAQ,MAAM,MAIZ,EAAwB,EAAM,CAAC,MAAM,MAC1C,EAAuB,EAAO,WAAW,EAAO,CACjD,CACD"}
@@ -1,2 +1,2 @@
1
- require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`./sdk-settings-field-metadata.cjs`);var t=new Set([`llm.model`,`llm.api_key`,`llm.base_url`]),n={basic:new Set([`critical`]),advanced:new Set([`critical`,`major`]),all:new Set([`critical`,`major`,`minor`])};function r(e){return!!e&&Array.isArray(e.sections)}function i(e){return r(e)?e.sections.flatMap(e=>e.fields):[]}function a(e,t){if(!e)return;let n=t.split(`.`),r=e;for(let e of n){if(typeof r!=`object`||!r)return;r=r[e]}return r}function o(e,t,n){let r=t.split(`.`),i=e;for(let e=0;e<r.length-1;e+=1){let t=r[e];(i[t]==null||typeof i[t]!=`object`||Array.isArray(i[t]))&&(i[t]={}),i=i[t]}i[r[r.length-1]]=n}function s(e,t,n=`agent_settings`){return a(e[n],t)??null}function c(e,t){return s(e,t,`agent_settings`)}function l(e){return e.choices.length>0}function u(e){return e.prominence===`critical`}function d(e){return e.prominence===`minor`}function f(e,t){let n=t??e.default;return l(e)?n==null?``:String(n):e.value_type===`boolean`?!!(n??!1):n==null?``:e.value_type===`array`||e.value_type===`object`?JSON.stringify(n,null,2):String(n)}function p(e,t){if(t===void 0)return null;if(e.value_type===`boolean`){if(typeof t==`string`){if(t===`true`)return!0;if(t===`false`)return!1}return t===null?null:!!t}if(e.value_type===`integer`||e.value_type===`number`){if(t===``||t===null)return null;let e=typeof t==`number`?t:Number(String(t));return Number.isNaN(e)?null:e}if(e.value_type===`array`||e.value_type===`object`){if(t===null||e.value_type===`object`&&typeof t==`object`&&!Array.isArray(t)&&Object.keys(t).length===0)return null;if(typeof t==`string`){let n=t.trim();if(!n)return null;try{let t=JSON.parse(n);return e.value_type===`object`&&typeof t==`object`&&t&&!Array.isArray(t)&&Object.keys(t).length===0?null:JSON.stringify(t)}catch{return n}}return JSON.stringify(t)}return t===null?null:String(t)}function m(e,t,n=`agent_settings`){let r=t??(n===`conversation_settings`?e.conversation_settings_schema:e.agent_settings_schema);return r?Object.fromEntries(i(r).map(t=>[t.key,f(t,s(e,t.key,n))])):{}}function h(e,t,n=`agent_settings`){let r=t??(n===`conversation_settings`?e.conversation_settings_schema:e.agent_settings_schema);if(!r)return`basic`;let a=!1,o=!1;for(let t of i(r))u(t)||p(t,s(e,t.key,n)??t.default??null)!==p(t,t.default??null)&&(d(t)?a=!0:o=!0);return a?`all`:o?`advanced`:`basic`}function g(e,t){return e.depends_on.every(e=>t[e]===!0)}function _(e){if(typeof e==`boolean`)return e;let t=e.trim().toLowerCase();if(!t)return null;if(t===`true`)return!0;if(t===`false`)return!1;throw Error(`Expected a boolean value, received: ${e}`)}function v(t,n){if(t.value_type===`boolean`)return _(n);if(t.value_type===`integer`||t.value_type===`number`){let r=String(n).trim();if(!r)return null;let i=Number(r);if(Number.isNaN(i))throw Error(`Expected a numeric value, received: ${r}`);if(t.value_type===`integer`&&!Number.isInteger(i))throw Error(`Expected an integer value, received: ${r}`);let a=e.getSettingsFieldConstraints(t.key);if(a?.min!=null&&i<a.min)throw Error(`${t.label} must be at least ${a.min}`);if(a?.max!=null&&i>a.max)throw Error(`${t.label} must be at most ${a.max}`);return i}if(t.value_type===`array`||t.value_type===`object`){let e=String(n).trim();if(!e)return null;let r;try{r=JSON.parse(e)}catch{throw Error(`Invalid JSON for ${t.label}`)}if(t.value_type===`array`){if(!Array.isArray(r))throw Error(`${t.label} must be a JSON array`);return r}if(r===null||Array.isArray(r)||typeof r!=`object`)throw Error(`${t.label} must be a JSON object`);return r}let r=String(n);return r===``&&!t.secret?null:r}function y(e,t,n){let r={};for(let a of i(e))n[a.key]&&o(r,a.key,v(a,t[a.key]));return r}function b(e,t){return n[t].has(e.prominence)}function x(e,t,n,r){let a=y(e,t,n);for(let t of i(e))b(t,r)||o(a,t.key,t.default??null);return a}function S(e,n,i,a=t){return r(e)?e.sections.map(e=>({...e,fields:e.fields.filter(e=>!a.has(e.key)&&b(e,i)&&g(e,n))})).filter(e=>e.fields.length>0):[]}function C(e){return e?i(e).some(e=>e.prominence===`major`):!1}function w(e){return e?i(e).some(e=>e.prominence===`minor`):!1}exports.buildInitialSettingsFormValues=m,exports.buildSdkSettingsPayloadForView=x,exports.getAgentSettingValue=c,exports.getVisibleSettingsSections=S,exports.hasAdvancedSettings=C,exports.hasMinorSettings=w,exports.inferInitialView=h,exports.isValidSettingsSchema=r;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`./sdk-settings-field-metadata.cjs`);var t=new Set([`llm.model`,`llm.api_key`,`llm.base_url`]),n={basic:new Set([`critical`]),advanced:new Set([`critical`,`major`]),all:new Set([`critical`,`major`,`minor`])};function r(e){return!!e&&Array.isArray(e.sections)}function i(e){return r(e)?e.sections.flatMap(e=>e.fields):[]}function a(e,t){if(!e)return;let n=t.split(`.`),r=e;for(let e of n){if(typeof r!=`object`||!r)return;r=r[e]}return r}function o(e,t,n){let r=t.split(`.`),i=e;for(let e=0;e<r.length-1;e+=1){let t=r[e];(i[t]==null||typeof i[t]!=`object`||Array.isArray(i[t]))&&(i[t]={}),i=i[t]}i[r[r.length-1]]=n}function s(e,t,n=`agent_settings`){return a(e[n],t)??null}function c(e,t){return s(e,t,`agent_settings`)}function l(e){return e.choices.length>0}function u(e){return e.prominence===`critical`}function d(e){return e.prominence===`minor`}function f(e,t){let n=t??e.default;return l(e)?n==null?``:String(n):e.value_type===`boolean`?!!(n??!1):n==null?``:e.value_type===`array`||e.value_type===`object`?JSON.stringify(n,null,2):String(n)}function p(e,t){if(t===void 0)return null;if(e.value_type===`boolean`){if(typeof t==`string`){if(t===`true`)return!0;if(t===`false`)return!1}return t===null?null:!!t}if(e.value_type===`integer`||e.value_type===`number`){if(t===``||t===null)return null;let e=typeof t==`number`?t:Number(String(t));return Number.isNaN(e)?null:e}if(e.value_type===`array`||e.value_type===`object`){if(t===null||e.value_type===`object`&&typeof t==`object`&&!Array.isArray(t)&&Object.keys(t).length===0)return null;if(typeof t==`string`){let n=t.trim();if(!n)return null;try{let t=JSON.parse(n);return e.value_type===`object`&&typeof t==`object`&&t&&!Array.isArray(t)&&Object.keys(t).length===0?null:JSON.stringify(t)}catch{return n}}return JSON.stringify(t)}return t===null?null:String(t)}function m(e,t,n=`agent_settings`){let r=t??(n===`conversation_settings`?e.conversation_settings_schema:e.agent_settings_schema);return r?Object.fromEntries(i(r).map(t=>[t.key,f(t,s(e,t.key,n))])):{}}function h(e,t,n=`agent_settings`){let r=t??(n===`conversation_settings`?e.conversation_settings_schema:e.agent_settings_schema);if(!r)return`basic`;let a=!1,o=!1;for(let t of i(r))u(t)||p(t,s(e,t.key,n)??t.default??null)!==p(t,t.default??null)&&(d(t)?a=!0:o=!0);return a?`all`:o?`advanced`:`basic`}function g(e,t){return e.depends_on.every(e=>t[e]===!0)}function _(e){if(typeof e==`boolean`)return e;let t=e.trim().toLowerCase();if(!t)return null;if(t===`true`)return!0;if(t===`false`)return!1;throw Error(`Expected a boolean value, received: ${e}`)}function v(t,n){if(t.value_type===`boolean`)return _(n);if(t.value_type===`integer`||t.value_type===`number`){let r=String(n).trim();if(!r)return null;let i=Number(r);if(Number.isNaN(i))throw Error(`Expected a numeric value, received: ${r}`);if(t.value_type===`integer`&&!Number.isInteger(i))throw Error(`Expected an integer value, received: ${r}`);let a=e.getSettingsFieldConstraints(t.key);if(a?.min!=null&&i<a.min)throw Error(`${t.label} must be at least ${a.min}`);if(a?.max!=null&&i>a.max)throw Error(`${t.label} must be at most ${a.max}`);return i}if(t.value_type===`array`||t.value_type===`object`){let e=String(n).trim();if(!e)return null;let r;try{r=JSON.parse(e)}catch{throw Error(`Invalid JSON for ${t.label}`)}if(t.value_type===`array`){if(!Array.isArray(r))throw Error(`${t.label} must be a JSON array`);return r}if(r===null||Array.isArray(r)||typeof r!=`object`)throw Error(`${t.label} must be a JSON object`);return r}let r=String(n);return r===``&&!t.secret?null:r}function y(e,t,n){let r={};for(let a of i(e))n[a.key]&&o(r,a.key,v(a,t[a.key]));return r}function b(e,t){return n[t].has(e.prominence)}function x(e,t,n,r){let a=y(e,t,n);for(let t of i(e))b(t,r)||o(a,t.key,t.default??null);return a}function S(e,n,i,a=t){return r(e)?e.sections.map(e=>({...e,fields:e.fields.filter(e=>!a.has(e.key)&&b(e,i)&&g(e,n))})).filter(e=>e.fields.length>0):[]}function C(e){return e?i(e).some(e=>e.prominence===`major`):!1}function w(e){return e?i(e).some(e=>e.prominence===`minor`):!1}exports.buildInitialSettingsFormValues=m,exports.buildSdkSettingsPayload=y,exports.buildSdkSettingsPayloadForView=x,exports.getAgentSettingValue=c,exports.getVisibleSettingsSections=S,exports.hasAdvancedSettings=C,exports.hasMinorSettings=w,exports.inferInitialView=h,exports.isValidSettingsSchema=r;
2
2
  //# sourceMappingURL=sdk-settings-schema.cjs.map