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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (600) hide show
  1. package/README.md +46 -31
  2. package/README.windows.md +2 -2
  3. package/bin/agent-canvas.mjs +3 -1
  4. package/build/assets/Trans-CBypf0vt.js +1 -0
  5. package/build/assets/{acp-providers-BqUvf67Q.js → acp-providers-BMyF5WII.js} +1 -1
  6. package/build/assets/active-backend-context-Cy_Ui5d7.js +1 -0
  7. package/build/assets/add-backend-modal-CUw7x0zm.js +1 -0
  8. package/build/assets/agent-server-client-options-FoO1qgAQ.js +1 -0
  9. package/build/assets/agent-server-conversation-service.api-D6Cmbnhd.js +5 -0
  10. package/build/assets/{agent-settings-DoHXHcoA.js → agent-settings-DW4vwOaz.js} +2 -2
  11. package/build/assets/{alert-banner-Bsvys-hT.js → alert-banner-BHmoArV2.js} +1 -1
  12. package/build/assets/{analytics-consent-form-modal-CVhsAREx.js → analytics-consent-form-modal-BNK0yu4L.js} +1 -1
  13. package/build/assets/api-key-entry-screen-BjVajIjf.js +1 -0
  14. package/build/assets/app-settings-C-5X6RzZ.js +1 -0
  15. package/build/assets/automation-detail-uDMoYmAc.js +1 -0
  16. package/build/assets/automations-list-DBCKCbId.js +1 -0
  17. package/build/assets/{back-nav-button-D20Avel_.js → back-nav-button-Bckz5Wh6.js} +1 -1
  18. package/build/assets/backend-form-modal-D2-XqJA4.js +1 -0
  19. package/build/assets/{backend-synced-settings-badge-Dcd4712m.js → backend-synced-settings-badge-Dx16CR_k.js} +1 -1
  20. package/build/assets/{base-modal-DvuGYyu1.js → base-modal-7QRNzf6r.js} +1 -1
  21. package/build/assets/{brand-button-9SOzmqPP.js → brand-button-CKLvirxz.js} +1 -1
  22. package/build/assets/browser-D1ZybR7v.js +5 -0
  23. package/build/assets/browser-store-hzasvz9c.js +1 -0
  24. package/build/assets/{browser-tab-bveyJhG9.js → browser-tab-iOQjXzmI.js} +1 -1
  25. package/build/assets/chat-send-button-_Uc9iFpG.js +1 -0
  26. package/build/assets/check-DfvhVBub.js +1 -0
  27. package/build/assets/{checkmark--JWAesua.js → checkmark-BHPhb50L.js} +1 -1
  28. package/build/assets/{chevron-down-B9j6lcFg.js → chevron-down-CMkqyPEY.js} +1 -1
  29. package/build/assets/{circle-plus-check-toggle-AO9DWyDX.js → circle-plus-check-toggle-ChmnRlpR.js} +1 -1
  30. package/build/assets/{close--W-tHn1h.js → close-DBs90sI6.js} +1 -1
  31. package/build/assets/{code-tag-chmctf4w.js → code-tag-BtHOqHwB.js} +1 -1
  32. package/build/assets/{color-themes-0biOprdo.js → color-themes-CBikRn3w.js} +1 -1
  33. package/build/assets/{combobox-caret-C9BNHfSu.js → combobox-caret-evUAh6YW.js} +1 -1
  34. package/build/assets/{command-store-UzKGSUC2.js → command-store-DZfR8QtE.js} +1 -1
  35. package/build/assets/condenser-settings-0tw3yXX8.js +1 -0
  36. package/build/assets/{confirmation-modal-Cq3eqvqH.js → confirmation-modal-DWuxe1zG.js} +1 -1
  37. package/build/assets/{context-menu-list-item-BQU0jZPq.js → context-menu-list-item-BbHFxHaq.js} +1 -1
  38. package/build/assets/conversation-B1v96pQf.js +19 -0
  39. package/build/assets/conversation-DgfcjCPm.js +1 -0
  40. package/build/assets/{conversation-local-storage-an3MiIiJ.js → conversation-local-storage-Btum2xBJ.js} +1 -1
  41. package/build/assets/conversation-panel-TJ7swm6a.js +1 -0
  42. package/build/assets/conversation-service.api-DXawvmNN.js +1 -0
  43. package/build/assets/conversation-state-store-DmuPn875.js +1 -0
  44. package/build/assets/{conversation-store-FQmakEd-.js → conversation-store-Bu89Yxg1.js} +1 -1
  45. package/build/assets/{conversation-tab-empty-state-C8tztloo.js → conversation-tab-empty-state-7js7FjYM.js} +1 -1
  46. package/build/assets/{copy-CdKN5rtJ.js → copy-B0SkXenR.js} +1 -1
  47. package/build/assets/copy-to-clipboard-button-CfH2EQz9.js +1 -0
  48. package/build/assets/{createLucideIcon-CkGTf9Qj.js → createLucideIcon-Bazr-V-B.js} +1 -1
  49. package/build/assets/declaration-Cn-NXHEy.js +1 -0
  50. package/build/assets/{device-verify-YuGXnpRk.js → device-verify-CWKyQ680.js} +1 -1
  51. package/build/assets/{dist-5dObF4p4.js → dist-Cbu6eGx8.js} +1 -1
  52. package/build/assets/dist-Dg2JWLGc.js +1 -0
  53. package/build/assets/{dropdown-classes-CVp0kVdt.js → dropdown-classes-BI1bI1ho.js} +1 -1
  54. package/build/assets/edit-automation-modal-DdAG6kFZ.js +1 -0
  55. package/build/assets/{ellipsis-button-k7HLwYth.js → ellipsis-button-DdCsOBHV.js} +1 -1
  56. package/build/assets/entry.client-DXyvKj5G.js +2 -0
  57. package/build/assets/{enum-filter-dropdown-COqUq3mB.js → enum-filter-dropdown-WROAIeys.js} +1 -1
  58. package/build/assets/{environment-switch-overlay-wB5LxfYZ.js → environment-switch-overlay-Dv0imPE6.js} +1 -1
  59. package/build/assets/{extensions-hub-CoWO3SzQ.js → extensions-hub-CkyDPXvn.js} +1 -1
  60. package/build/assets/{extensions-navigation-D5w2sFQe.js → extensions-navigation-gMR-la1J.js} +1 -1
  61. package/build/assets/{external-link-BATfjnkz.js → external-link-FsutnU75.js} +1 -1
  62. package/build/assets/files-tab-DNUoveJZ.js +1 -0
  63. package/build/assets/files-tab-store-SyvkH3ic.js +1 -0
  64. package/build/assets/{folder-DPTBlxWb.js → folder-DusyBXDE.js} +1 -1
  65. package/build/assets/{globe-Dpgjreyy.js → globe-BUcin4kT.js} +1 -1
  66. package/build/assets/home-D809jWVS.js +1 -0
  67. package/build/assets/{iconBase-BVhFI-0E.js → iconBase-B6CXDDaE.js} +1 -1
  68. package/build/assets/index-redirect-nMYgOTBX.js +1 -0
  69. package/build/assets/install-server-modal-D92Qy57I.js +1 -0
  70. package/build/assets/{launch-fU77utLO.js → launch-BnCG2g2a.js} +1 -1
  71. package/build/assets/lesson-plan-j-ZaE8vK.js +3 -0
  72. package/build/assets/{lightbulb-S616sGEL.js → lightbulb-BJ5tWmAh.js} +1 -1
  73. package/build/assets/{link-external-ooNztGLp.js → link-external-DUtmHAHi.js} +1 -1
  74. package/build/assets/{list-todo-nCa9zxVE.js → list-todo-C-gekh-4.js} +1 -1
  75. package/build/assets/{llm-client-P9f2gKeW.js → llm-client-BZUkZYlc.js} +1 -1
  76. package/build/assets/llm-not-configured-banner-PlvJ99rE.js +27 -0
  77. package/build/assets/llm-settings-Dgk32xRW.js +1 -0
  78. package/build/assets/llm-settings-Dxo7owKm.js +1 -0
  79. package/build/assets/llm-subscription-Bvu8BLXc.js +1 -0
  80. package/build/assets/llm-subscription-service-Bq6z3Wkv.js +1 -0
  81. package/build/assets/loader-circle-Cw7_q9hg.js +1 -0
  82. package/build/assets/{loading-spinner-D7bOU6I7.js → loading-spinner-RF3IlMF5.js} +1 -1
  83. package/build/assets/manage-backends-modal-BT66a6e0.js +1 -0
  84. package/build/assets/manifest-b8402d59.js +1 -0
  85. package/build/assets/markdown-renderer-Tw6FR2ad.js +2 -0
  86. package/build/assets/mcp-B_pA7vZg.js +9 -0
  87. package/build/assets/mcp-client-ppbYplKX.js +1 -0
  88. package/build/assets/messages-Cg9aNVi6.js +41 -0
  89. package/build/assets/{modal-backdrop-_VbcKk5K.js → modal-backdrop-DRpBVVW5.js} +1 -1
  90. package/build/assets/{modal-body-Cd-PtMHr.js → modal-body-CEV2O67p.js} +1 -1
  91. package/build/assets/{modal-classes-CNm4ovfm.js → modal-classes-B2DuQ9qD.js} +1 -1
  92. package/build/assets/{modal-close-button-Dbo0NAMt.js → modal-close-button-gtb7RHFB.js} +1 -1
  93. package/build/assets/model-selector-CAv3RrJ3.js +1 -0
  94. package/build/assets/{mouse-pointer-click-D0QNGSIo.js → mouse-pointer-click-B3i6HcaF.js} +1 -1
  95. package/build/assets/{navigation-context-BpAOh_t5.js → navigation-context-CM9JmTvm.js} +1 -1
  96. package/build/assets/{navigation-link-Dci7Nuyf.js → navigation-link-FmXzqR1J.js} +1 -1
  97. package/build/assets/onboarding-Q9CzuVWy.js +1 -0
  98. package/build/assets/{openhands-logo-Dzm-d_V_.js → openhands-logo-B4aEO2sf.js} +1 -1
  99. package/build/assets/option-service.api-C-s2BYZp.js +1 -0
  100. package/build/assets/organization-service.api-CXTKpFnV.js +1 -0
  101. package/build/assets/{path-utils-YlkpoImJ.js → path-utils-CE98fIFP.js} +1 -1
  102. package/build/assets/{pencil-DPEtl5QD.js → pencil-bWrxRWlr.js} +1 -1
  103. package/build/assets/{plan-components-CutRBi0G.js → plan-components-BF-X7vnQ.js} +1 -1
  104. package/build/assets/{planner-tab-DjIFIQDk.js → planner-tab-jkyaGiRc.js} +1 -1
  105. package/build/assets/plus-BTCK8WtR.js +1 -0
  106. package/build/assets/{profiles-client-BNvHck-I.js → profiles-client-CQ9L1yHR.js} +1 -1
  107. package/build/assets/providers-B3hkV58J.js +1 -0
  108. package/build/assets/proxy-BOcEWkCD.js +1 -0
  109. package/build/assets/{react-dom-hVBnwgwZ.js → react-dom-BBXlXOZX.js} +1 -1
  110. package/build/assets/{react-CM_dJw1Z.js → react-uQK8du1l.js} +1 -1
  111. package/build/assets/{react-Dy05vyj5.js → react-x84lbX6s.js} +1 -1
  112. package/build/assets/{recommended-automations-launcher-DCi0RXIj.js → recommended-automations-launcher-DMk35aFI.js} +11 -11
  113. package/build/assets/root-CV0kyxfP.js +2 -0
  114. package/build/assets/root-PB2Jnj61.css +1 -0
  115. package/build/assets/root-layout-Bl4Qzg5J.js +2 -0
  116. package/build/assets/{runtime-waiting-state-8i0Z3A_i.js → runtime-waiting-state-C1hJpXxl.js} +1 -1
  117. package/build/assets/sdk-section-page-DHbC2_j3.js +1 -0
  118. package/build/assets/sdk-settings-schema-z51bBmMl.js +1 -0
  119. package/build/assets/{search-C_l_XW4e.js → search-Dxm1MUMi.js} +1 -1
  120. package/build/assets/{secrets-service-D6Gcymcs.js → secrets-service-DlScPUwy.js} +1 -1
  121. package/build/assets/secrets-settings-B97-JKBe.js +1 -0
  122. package/build/assets/settings-BpmooyBV.js +1 -0
  123. package/build/assets/{settings-dropdown-input-BP-arqjU.js → settings-dropdown-input-hIIAlmHb.js} +1 -1
  124. package/build/assets/{settings-gear-f-zWpxfI.js → settings-gear-DQLTCnFx.js} +1 -1
  125. package/build/assets/{settings-index-B035V2pu.js → settings-index-kKASonWk.js} +1 -1
  126. package/build/assets/{settings-input-BM8ANxDb.js → settings-input-BlOzCtP3.js} +1 -1
  127. package/build/assets/{settings-list-classes-B9uo5WES.js → settings-list-classes-BFRbCy4U.js} +1 -1
  128. package/build/assets/settings-modal-C4pZwj-l.js +1 -0
  129. package/build/assets/{settings-section-header-context-Cid25HM2.js → settings-section-header-context-iz4Go0dL.js} +1 -1
  130. package/build/assets/settings-service.api-DVLwi82B.js +1 -0
  131. package/build/assets/{settings-switch-xa6q4M6j.js → settings-switch-6cXIH-UE.js} +1 -1
  132. package/build/assets/{settings-utils--H_EvFjx.js → settings-utils-CfScEaPn.js} +1 -1
  133. package/build/assets/shared-conversation-CLkayApe.js +1 -0
  134. package/build/assets/sidebar-mobile-menu-toggle-C8uT4Mhd.js +1 -0
  135. package/build/assets/{sidebar-nav-link-CPWuQ_c6.js → sidebar-nav-link-BKZiAQxQ.js} +1 -1
  136. package/build/assets/{skill-card-pill-row-DeodrwPG.js → skill-card-pill-row-D13WRKIE.js} +1 -1
  137. package/build/assets/{skills-BE5HJuGM.js → skills-NpRk6v79.js} +1 -1
  138. package/build/assets/{skills-client-Ui_9FvXp.js → skills-client-DsqwQEwj.js} +1 -1
  139. package/build/assets/{skills-plugins-Dc4ec3CW.js → skills-plugins-bkwxItAw.js} +1 -1
  140. package/build/assets/{skills-settings-B6HwvA0S.js → skills-settings-DMbrlB2b.js} +2 -2
  141. package/build/assets/{square-chevron-right-BWAB7NPp.js → square-chevron-right-DxZKcVq4.js} +1 -1
  142. package/build/assets/{styled-tooltip-X61SEwqk.js → styled-tooltip-DG1ofAcS.js} +1 -1
  143. package/build/assets/suspense-sOGeme-f.js +1 -0
  144. package/build/assets/{switch-skeleton-BbIWemkK.js → switch-skeleton-CZ6XCVZk.js} +1 -1
  145. package/build/assets/{task-list-tab-BFnyHGsn.js → task-list-tab-CddMPNCD.js} +1 -1
  146. package/build/assets/telemetry-CX4mTZUw.js +2 -0
  147. package/build/assets/{terminal-CltMeLv4.js → terminal-BOAVxAej.js} +1 -1
  148. package/build/assets/{terminal-B92mACR5.js → terminal-DbtwHqt4.js} +1 -1
  149. package/build/assets/{toggle-switch-CL_wRiGs.js → toggle-switch-C0t6ge2m.js} +1 -1
  150. package/build/assets/{trash-2-C-RvV8WA.js → trash-2-Dmprz0A0.js} +1 -1
  151. package/build/assets/{typography-BLSEGjph.js → typography-CNHbO1eE.js} +1 -1
  152. package/build/assets/{u-check-circle-dG_VJqx5.js → u-check-circle-CX66WzfT.js} +1 -1
  153. package/build/assets/{u-check-circle-half-D-5ZPRnd.js → u-check-circle-half-Bj853Ub0.js} +1 -1
  154. package/build/assets/{u-circuit-BXoisXOA.js → u-circuit-B9bLCP7N.js} +1 -1
  155. package/build/assets/{u-edit-DIgADxLH.js → u-edit-D7JyvoWm.js} +1 -1
  156. package/build/assets/use-acp-credential-form-Cj_oUYd5.js +1 -0
  157. package/build/assets/{use-active-conversation-B0_rXnqa.js → use-active-conversation-B64-yy2X.js} +1 -1
  158. package/build/assets/use-agent-settings-schema-BzbQYEzb.js +1 -0
  159. package/build/assets/{use-agent-state-BuOzLeFN.js → use-agent-state-DxFk391r.js} +1 -1
  160. package/build/assets/{use-breakpoint-DpxHDmuH.js → use-breakpoint-Cent5lpI.js} +1 -1
  161. package/build/assets/{use-click-outside-element-DhxCUyWl.js → use-click-outside-element-DOYR26nw.js} +1 -1
  162. package/build/assets/{use-cloud-current-user-id-DVZXa_WH.js → use-cloud-current-user-id-BVf7pb8n.js} +1 -1
  163. package/build/assets/use-config-CvdJUq4E.js +1 -0
  164. package/build/assets/use-create-conversation-Cnouynop.js +1 -0
  165. package/build/assets/{use-create-secret-ClPpLBgD.js → use-create-secret-Cgu4uxyr.js} +1 -1
  166. package/build/assets/{use-event-store-DNSmyzUh.js → use-event-store-Nv92IYMU.js} +1 -1
  167. package/build/assets/use-handle-plan-click-C7824bbe.js +1 -0
  168. package/build/assets/use-is-authed-mnJtd2bw.js +1 -0
  169. package/build/assets/{use-launch-skill-in-chat-BTgXIu3N.js → use-launch-skill-in-chat-NDUo_UCA.js} +1 -1
  170. package/build/assets/use-llm-profiles-9eH3Y3Ef.js +1 -0
  171. package/build/assets/use-runtime-is-ready-DEnxJH7W.js +1 -0
  172. package/build/assets/use-save-settings-DrClHnX7.js +1 -0
  173. package/build/assets/use-settings-BNAoCX93.js +1 -0
  174. package/build/assets/{use-settings-nav-items-DVrMNk0I.js → use-settings-nav-items-CLIW2p2n.js} +2 -2
  175. package/build/assets/{use-skills-Bv8LXrys.js → use-skills-B9gOHrOj.js} +1 -1
  176. package/build/assets/{use-task-list-E5t-jtgs.js → use-task-list-DLKPnq3I.js} +1 -1
  177. package/build/assets/{use-tracking-BhyV5A9i.js → use-tracking-Dph_dRlL.js} +1 -1
  178. package/build/assets/{use-user-conversation-6yFVEXiG.js → use-user-conversation-leIsQH1B.js} +1 -1
  179. package/build/assets/useMutation-cXADjWAv.js +1 -0
  180. package/build/assets/useQuery-BQr6A6ry.js +1 -0
  181. package/build/assets/useTranslation-Bp1btAjp.js +1 -0
  182. package/build/assets/{utils-eXk6yerZ.js → utils-o-OXNtEy.js} +1 -1
  183. package/build/assets/v4-DOSjXdIn.js +1 -0
  184. package/build/assets/{vendor~browser-tab-BGJDGHyS.js → vendor~browser-tab-642bOcE1.js} +1 -1
  185. package/build/assets/{vendor~conversation-panel~conversation-D98u43rl.js → vendor~conversation-panel~conversation-DqmbnXRn.js} +1 -1
  186. package/build/assets/{vendor~conversation-panel~conversation~alert-banner-CtjHOMO_.js → vendor~conversation-panel~conversation~alert-banner-CSDVP1nw.js} +1 -1
  187. package/build/assets/{vendor~conversation-panel~conversation~index-BXt2-3Cc.js → vendor~conversation-panel~conversation~index-DITarr3F.js} +1 -1
  188. package/build/assets/{vendor~entry.client-DrAdOKqA.js → vendor~entry.client-CQ77DucR.js} +1 -1
  189. package/build/assets/{vendor~entry.client-EcEhmoJW.js → vendor~entry.client-ePAFmSwg.js} +1 -1
  190. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~f0pn7nss-DUk-MwlC.js +177 -0
  191. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~lb3qbbk3-BvmOva8I.js +1 -0
  192. package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~lpdshwee-Cl4h-NSi.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~lpdshwee-BkQQxq_q.js} +1 -1
  193. package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~oli4dvxu-BmOMM-6e.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~oli4dvxu-8xF3WeB9.js} +1 -1
  194. package/build/assets/{vendor~entry.client~root~root-layout~index-redirect~home~conversation-panel~conversation~la~cnj3raoq-BjMt2JeR.js → vendor~entry.client~root~root-layout~index-redirect~home~conversation-panel~conversation~la~nyk4vv4g-UzR6fe3Y.js} +1 -1
  195. package/build/assets/{vendor~files-tab--640YzQO.js → vendor~files-tab-Cko7Kr5k.js} +1 -1
  196. package/build/assets/{vendor~home~conversation-panel~conversation-dhKA2JVJ.js → vendor~home~conversation-panel~conversation-D3otw5Jn.js} +1 -1
  197. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DMOoHVBy.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CMdKUYP2.js} +1 -1
  198. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BoE6JRJj.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DSR6Mugx.js} +1 -1
  199. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dqpi2xyZ.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DdOzMvB1.js} +1 -1
  200. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DX2CmDJ0.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dj2eezvL.js} +1 -1
  201. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BNExlCqT.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DyN8jeZz.js} +1 -1
  202. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DCn1M97v.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-lpEWrbMA.js} +1 -1
  203. package/build/assets/{vendor~launch-C0VvUe2W.js → vendor~launch-DoYO54df.js} +1 -1
  204. package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-COLUW-6M.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-BGwU2SbU.js} +1 -1
  205. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation-eG1kjEbj.js → vendor~root-layout~home~conversation-panel~conversation-Iqs0j4Wo.js} +1 -1
  206. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-7eqKdLSQ.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-BdS6bbw9.js} +1 -1
  207. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-BP3MGAAY.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-Fd3F3jth.js} +1 -1
  208. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ceijy54y-Dkd2wxJA.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ceijy54y-Cg0AqwMN.js} +2 -2
  209. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~bxn4ldi1-_LydBB1e.js → vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~bxn4ldi1-BlsbqRyZ.js} +1 -1
  210. package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~ec6pk3ix-CBICqfKC.js +48 -0
  211. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~skills-settings~mcp~automati~dou5smdo-DavKsAvB.js → vendor~root-layout~home~conversation-panel~conversation~launch~skills-settings~automations-~nv6afr11-BYNoKMU0.js} +97 -69
  212. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-CsO1gYxF.js → vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-BspUuSsP.js} +1 -1
  213. package/build/assets/{vendor~root-layout~home~mcp~automations-list-D40jgugf.js → vendor~root-layout~home~mcp~automations-list-C9U_eOqU.js} +1 -1
  214. package/build/assets/{vendor~root-layout~home~mcp~automations-list-cNHi83v_.js → vendor~root-layout~home~mcp~automations-list-DhQ3LPVw.js} +1 -1
  215. package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-xCM3k6Rd.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BEbhHQYg.js} +1 -1
  216. package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BIm99UaF.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-DdgDNzMc.js} +2 -2
  217. package/build/assets/{verification-settings-Be6NxjuT.js → verification-settings-D7R-Ne6L.js} +1 -1
  218. package/build/assets/x-DCZg87p7.js +1 -0
  219. package/build/assets/{x-mark-C42svtnu.js → x-mark-jrBTJbnP.js} +1 -1
  220. package/build/index.html +4 -4
  221. package/build/locales/ar/openhands.json +22 -0
  222. package/build/locales/ca/openhands.json +22 -0
  223. package/build/locales/de/openhands.json +22 -0
  224. package/build/locales/en/openhands.json +22 -0
  225. package/build/locales/es/openhands.json +22 -0
  226. package/build/locales/fr/openhands.json +22 -0
  227. package/build/locales/it/openhands.json +22 -0
  228. package/build/locales/ja/openhands.json +22 -0
  229. package/build/locales/ko-KR/openhands.json +22 -0
  230. package/build/locales/no/openhands.json +22 -0
  231. package/build/locales/pt/openhands.json +22 -0
  232. package/build/locales/tr/openhands.json +22 -0
  233. package/build/locales/uk/openhands.json +22 -0
  234. package/build/locales/zh-CN/openhands.json +22 -0
  235. package/build/locales/zh-TW/openhands.json +22 -0
  236. package/config/defaults.json +4 -7
  237. package/dist/api/agent-server-adapter.cjs +3 -3
  238. package/dist/api/agent-server-adapter.cjs.map +1 -1
  239. package/dist/api/agent-server-adapter.d.ts +8 -0
  240. package/dist/api/agent-server-adapter.js +82 -80
  241. package/dist/api/agent-server-adapter.js.map +1 -1
  242. package/dist/api/agent-server-compatibility.cjs +1 -1
  243. package/dist/api/agent-server-compatibility.cjs.map +1 -1
  244. package/dist/api/agent-server-compatibility.d.ts +10 -0
  245. package/dist/api/agent-server-compatibility.js +69 -25
  246. package/dist/api/agent-server-compatibility.js.map +1 -1
  247. package/dist/api/backend-registry/default-backend.cjs +1 -1
  248. package/dist/api/backend-registry/default-backend.cjs.map +1 -1
  249. package/dist/api/backend-registry/default-backend.d.ts +6 -5
  250. package/dist/api/backend-registry/default-backend.js +1 -1
  251. package/dist/api/backend-registry/default-backend.js.map +1 -1
  252. package/dist/api/backend-registry/storage.cjs +1 -1
  253. package/dist/api/backend-registry/storage.cjs.map +1 -1
  254. package/dist/api/backend-registry/storage.js +38 -68
  255. package/dist/api/backend-registry/storage.js.map +1 -1
  256. package/dist/api/cloud/proxy.cjs +1 -1
  257. package/dist/api/cloud/proxy.cjs.map +1 -1
  258. package/dist/api/cloud/proxy.d.ts +3 -16
  259. package/dist/api/cloud/proxy.js +1 -1
  260. package/dist/api/cloud/proxy.js.map +1 -1
  261. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  262. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  263. package/dist/api/conversation-service/agent-server-conversation-service.api.js +75 -75
  264. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  265. package/dist/api/llm-subscription-service.cjs +2 -0
  266. package/dist/api/llm-subscription-service.cjs.map +1 -0
  267. package/dist/api/llm-subscription-service.d.ts +23 -0
  268. package/dist/api/llm-subscription-service.js +109 -0
  269. package/dist/api/llm-subscription-service.js.map +1 -0
  270. package/dist/api/mcp-service/mcp-service.api.cjs +1 -1
  271. package/dist/api/mcp-service/mcp-service.api.cjs.map +1 -1
  272. package/dist/api/mcp-service/mcp-service.api.d.ts +2 -3
  273. package/dist/api/mcp-service/mcp-service.api.js +38 -13
  274. package/dist/api/mcp-service/mcp-service.api.js.map +1 -1
  275. package/dist/api/settings-service/settings-service.api.cjs +1 -1
  276. package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
  277. package/dist/api/settings-service/settings-service.api.js +41 -42
  278. package/dist/api/settings-service/settings-service.api.js.map +1 -1
  279. package/dist/components/conversation-events/chat/messages.cjs +1 -1
  280. package/dist/components/conversation-events/chat/messages.cjs.map +1 -1
  281. package/dist/components/conversation-events/chat/messages.js +4 -4
  282. package/dist/components/conversation-events/chat/messages.js.map +1 -1
  283. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  284. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  285. package/dist/components/features/backends/backend-form-modal.js +136 -135
  286. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  287. package/dist/components/features/backends/backend-selector.cjs +1 -1
  288. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  289. package/dist/components/features/backends/backend-selector.js +103 -91
  290. package/dist/components/features/backends/backend-selector.js.map +1 -1
  291. package/dist/components/features/backends/manage-backends-modal.cjs +1 -1
  292. package/dist/components/features/backends/manage-backends-modal.cjs.map +1 -1
  293. package/dist/components/features/backends/manage-backends-modal.js +14 -8
  294. package/dist/components/features/backends/manage-backends-modal.js.map +1 -1
  295. package/dist/components/features/chat/path-component.cjs +1 -1
  296. package/dist/components/features/chat/path-component.cjs.map +1 -1
  297. package/dist/components/features/chat/path-component.js +1 -1
  298. package/dist/components/features/chat/path-component.js.map +1 -1
  299. package/dist/components/features/home/workspace-dropdown/folder-browser-modal.cjs +1 -1
  300. package/dist/components/features/home/workspace-dropdown/folder-browser-modal.cjs.map +1 -1
  301. package/dist/components/features/home/workspace-dropdown/folder-browser-modal.js +77 -66
  302. package/dist/components/features/home/workspace-dropdown/folder-browser-modal.js.map +1 -1
  303. package/dist/components/features/mcp-page/custom-server-editor.cjs +1 -1
  304. package/dist/components/features/mcp-page/custom-server-editor.cjs.map +1 -1
  305. package/dist/components/features/mcp-page/custom-server-editor.js +1 -0
  306. package/dist/components/features/mcp-page/custom-server-editor.js.map +1 -1
  307. package/dist/components/features/mcp-page/install-server-modal.cjs +1 -1
  308. package/dist/components/features/mcp-page/install-server-modal.cjs.map +1 -1
  309. package/dist/components/features/mcp-page/install-server-modal.js +73 -46
  310. package/dist/components/features/mcp-page/install-server-modal.js.map +1 -1
  311. package/dist/components/features/mcp-page/marketplace-card.cjs +1 -1
  312. package/dist/components/features/mcp-page/marketplace-card.cjs.map +1 -1
  313. package/dist/components/features/mcp-page/marketplace-card.js +6 -2
  314. package/dist/components/features/mcp-page/marketplace-card.js.map +1 -1
  315. package/dist/components/features/settings/brand-button.cjs +1 -1
  316. package/dist/components/features/settings/brand-button.cjs.map +1 -1
  317. package/dist/components/features/settings/brand-button.js +1 -1
  318. package/dist/components/features/settings/brand-button.js.map +1 -1
  319. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.cjs +2 -0
  320. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.cjs.map +1 -0
  321. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.d.ts +5 -0
  322. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.js +167 -0
  323. package/dist/components/features/settings/llm-settings/openai-subscription-auth-card.js.map +1 -0
  324. package/dist/components/shared/modals/settings/model-selector.cjs +1 -1
  325. package/dist/components/shared/modals/settings/model-selector.cjs.map +1 -1
  326. package/dist/components/shared/modals/settings/model-selector.d.ts +1 -2
  327. package/dist/components/shared/modals/settings/model-selector.js +59 -65
  328. package/dist/components/shared/modals/settings/model-selector.js.map +1 -1
  329. package/dist/components/shared/modals/settings/settings-form.cjs +1 -1
  330. package/dist/components/shared/modals/settings/settings-form.cjs.map +1 -1
  331. package/dist/components/shared/modals/settings/settings-form.js +1 -2
  332. package/dist/components/shared/modals/settings/settings-form.js.map +1 -1
  333. package/dist/config/defaults.cjs +2 -0
  334. package/dist/config/defaults.cjs.map +1 -0
  335. package/dist/config/defaults.js +35 -0
  336. package/dist/config/defaults.js.map +1 -0
  337. package/dist/constants/llm-subscription.cjs +2 -0
  338. package/dist/constants/llm-subscription.cjs.map +1 -0
  339. package/dist/constants/llm-subscription.d.ts +16 -0
  340. package/dist/constants/llm-subscription.js +47 -0
  341. package/dist/constants/llm-subscription.js.map +1 -0
  342. package/dist/contexts/conversation-websocket-context.cjs +1 -1
  343. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  344. package/dist/contexts/conversation-websocket-context.js +1 -1
  345. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  346. package/dist/hooks/mutation/use-llm-subscription-auth.cjs +2 -0
  347. package/dist/hooks/mutation/use-llm-subscription-auth.cjs.map +1 -0
  348. package/dist/hooks/mutation/use-llm-subscription-auth.d.ts +3 -0
  349. package/dist/hooks/mutation/use-llm-subscription-auth.js +30 -0
  350. package/dist/hooks/mutation/use-llm-subscription-auth.js.map +1 -0
  351. package/dist/hooks/mutation/use-save-fields-as-secrets.cjs +1 -1
  352. package/dist/hooks/mutation/use-save-fields-as-secrets.cjs.map +1 -1
  353. package/dist/hooks/mutation/use-save-fields-as-secrets.d.ts +8 -6
  354. package/dist/hooks/mutation/use-save-fields-as-secrets.js +1 -1
  355. package/dist/hooks/mutation/use-save-fields-as-secrets.js.map +1 -1
  356. package/dist/hooks/mutation/use-test-mcp-server.d.ts +1 -1
  357. package/dist/hooks/query/query-keys.cjs +1 -1
  358. package/dist/hooks/query/query-keys.cjs.map +1 -1
  359. package/dist/hooks/query/query-keys.d.ts +5 -0
  360. package/dist/hooks/query/query-keys.js +14 -2
  361. package/dist/hooks/query/query-keys.js.map +1 -1
  362. package/dist/hooks/query/use-agent-settings-schema.cjs +1 -1
  363. package/dist/hooks/query/use-agent-settings-schema.cjs.map +1 -1
  364. package/dist/hooks/query/use-agent-settings-schema.d.ts +2 -2
  365. package/dist/hooks/query/use-agent-settings-schema.js +21 -19
  366. package/dist/hooks/query/use-agent-settings-schema.js.map +1 -1
  367. package/dist/hooks/query/use-backends-health.cjs +1 -1
  368. package/dist/hooks/query/use-backends-health.cjs.map +1 -1
  369. package/dist/hooks/query/use-backends-health.js +38 -36
  370. package/dist/hooks/query/use-backends-health.js.map +1 -1
  371. package/dist/hooks/query/use-llm-subscription-models.cjs +2 -0
  372. package/dist/hooks/query/use-llm-subscription-models.cjs.map +1 -0
  373. package/dist/hooks/query/use-llm-subscription-models.d.ts +3 -0
  374. package/dist/hooks/query/use-llm-subscription-models.js +19 -0
  375. package/dist/hooks/query/use-llm-subscription-models.js.map +1 -0
  376. package/dist/hooks/query/use-llm-subscription-status.cjs +2 -0
  377. package/dist/hooks/query/use-llm-subscription-status.cjs.map +1 -0
  378. package/dist/hooks/query/use-llm-subscription-status.d.ts +3 -0
  379. package/dist/hooks/query/use-llm-subscription-status.js +19 -0
  380. package/dist/hooks/query/use-llm-subscription-status.js.map +1 -0
  381. package/dist/hooks/use-websocket.cjs +1 -1
  382. package/dist/hooks/use-websocket.cjs.map +1 -1
  383. package/dist/hooks/use-websocket.d.ts +0 -1
  384. package/dist/hooks/use-websocket.js +37 -38
  385. package/dist/hooks/use-websocket.js.map +1 -1
  386. package/dist/i18n/declaration.cjs +1 -1
  387. package/dist/i18n/declaration.cjs.map +1 -1
  388. package/dist/i18n/declaration.d.ts +22 -0
  389. package/dist/i18n/declaration.js +1 -1
  390. package/dist/i18n/declaration.js.map +1 -1
  391. package/dist/i18n/translation.cjs +2 -2
  392. package/dist/i18n/translation.cjs.map +1 -1
  393. package/dist/i18n/translation.js +374 -0
  394. package/dist/i18n/translation.js.map +1 -1
  395. package/dist/locales/ar/openhands.json +22 -0
  396. package/dist/locales/ca/openhands.json +22 -0
  397. package/dist/locales/de/openhands.json +22 -0
  398. package/dist/locales/en/openhands.json +22 -0
  399. package/dist/locales/es/openhands.json +22 -0
  400. package/dist/locales/fr/openhands.json +22 -0
  401. package/dist/locales/it/openhands.json +22 -0
  402. package/dist/locales/ja/openhands.json +22 -0
  403. package/dist/locales/ko-KR/openhands.json +22 -0
  404. package/dist/locales/no/openhands.json +22 -0
  405. package/dist/locales/pt/openhands.json +22 -0
  406. package/dist/locales/tr/openhands.json +22 -0
  407. package/dist/locales/uk/openhands.json +22 -0
  408. package/dist/locales/zh-CN/openhands.json +22 -0
  409. package/dist/locales/zh-TW/openhands.json +22 -0
  410. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.cjs +1 -1
  411. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.cjs.map +1 -1
  412. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.js +10 -22
  413. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.js.map +1 -1
  414. package/dist/node_modules/@openhands/extensions/skills/index.cjs +97 -69
  415. package/dist/node_modules/@openhands/extensions/skills/index.cjs.map +1 -1
  416. package/dist/node_modules/@openhands/extensions/skills/index.js +3 -3
  417. package/dist/node_modules/@openhands/extensions/skills/index.js.map +1 -1
  418. package/dist/package.cjs +1 -1
  419. package/dist/package.cjs.map +1 -1
  420. package/dist/package.js +3 -5
  421. package/dist/package.js.map +1 -1
  422. package/dist/routes/files-tab.cjs +1 -1
  423. package/dist/routes/files-tab.cjs.map +1 -1
  424. package/dist/routes/files-tab.js +28 -17
  425. package/dist/routes/files-tab.js.map +1 -1
  426. package/dist/routes/llm-settings.cjs +1 -1
  427. package/dist/routes/llm-settings.cjs.map +1 -1
  428. package/dist/routes/llm-settings.js +142 -85
  429. package/dist/routes/llm-settings.js.map +1 -1
  430. package/dist/services/canvas-ui.cjs +1 -1
  431. package/dist/services/canvas-ui.cjs.map +1 -1
  432. package/dist/services/canvas-ui.d.ts +1 -1
  433. package/dist/services/canvas-ui.js +2 -2
  434. package/dist/services/canvas-ui.js.map +1 -1
  435. package/dist/stores/files-tab-store.cjs +1 -1
  436. package/dist/stores/files-tab-store.cjs.map +1 -1
  437. package/dist/stores/files-tab-store.d.ts +2 -1
  438. package/dist/stores/files-tab-store.js +5 -1
  439. package/dist/stores/files-tab-store.js.map +1 -1
  440. package/dist/stores/use-event-store.cjs +1 -1
  441. package/dist/stores/use-event-store.cjs.map +1 -1
  442. package/dist/stores/use-event-store.js +39 -34
  443. package/dist/stores/use-event-store.js.map +1 -1
  444. package/dist/types/mcp-server.d.ts +21 -0
  445. package/dist/ui/dropdown/dropdown.cjs +1 -1
  446. package/dist/ui/dropdown/dropdown.cjs.map +1 -1
  447. package/dist/ui/dropdown/dropdown.js +6 -2
  448. package/dist/ui/dropdown/dropdown.js.map +1 -1
  449. package/dist/utils/handle-event-for-ui.cjs +1 -1
  450. package/dist/utils/handle-event-for-ui.cjs.map +1 -1
  451. package/dist/utils/handle-event-for-ui.d.ts +2 -0
  452. package/dist/utils/handle-event-for-ui.js +1 -1
  453. package/dist/utils/handle-event-for-ui.js.map +1 -1
  454. package/dist/utils/llm-subscription-schema.cjs +2 -0
  455. package/dist/utils/llm-subscription-schema.cjs.map +1 -0
  456. package/dist/utils/llm-subscription-schema.d.ts +2 -0
  457. package/dist/utils/llm-subscription-schema.js +22 -0
  458. package/dist/utils/llm-subscription-schema.js.map +1 -0
  459. package/dist/utils/mcp-config.cjs +1 -1
  460. package/dist/utils/mcp-config.cjs.map +1 -1
  461. package/dist/utils/mcp-config.js +32 -17
  462. package/dist/utils/mcp-config.js.map +1 -1
  463. package/dist/utils/mcp-credential-validation.cjs +2 -0
  464. package/dist/utils/mcp-credential-validation.cjs.map +1 -0
  465. package/dist/utils/mcp-credential-validation.d.ts +22 -0
  466. package/dist/utils/mcp-credential-validation.js +31 -0
  467. package/dist/utils/mcp-credential-validation.js.map +1 -0
  468. package/dist/utils/mcp-marketplace-utils.cjs +1 -1
  469. package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
  470. package/dist/utils/mcp-marketplace-utils.d.ts +7 -2
  471. package/dist/utils/mcp-marketplace-utils.js +33 -48
  472. package/dist/utils/mcp-marketplace-utils.js.map +1 -1
  473. package/dist/utils/sdk-settings-schema.cjs +1 -1
  474. package/dist/utils/sdk-settings-schema.cjs.map +1 -1
  475. package/dist/utils/sdk-settings-schema.js +64 -61
  476. package/dist/utils/sdk-settings-schema.js.map +1 -1
  477. package/dist/utils/websocket-url.cjs +1 -1
  478. package/dist/utils/websocket-url.cjs.map +1 -1
  479. package/dist/utils/websocket-url.js +2 -2
  480. package/dist/utils/websocket-url.js.map +1 -1
  481. package/package.json +3 -5
  482. package/scripts/check-sdk-version-sync.mjs +9 -27
  483. package/scripts/dev-with-automation.mjs +1 -3
  484. package/scripts/docker-build.mjs +0 -4
  485. package/scripts/run-staged-typecheck.mjs +44 -0
  486. package/build/assets/QueryClientProvider-XcSjsfpb.js +0 -1
  487. package/build/assets/Trans-sy4l4TyQ.js +0 -1
  488. package/build/assets/active-backend-context-CNF2lPkA.js +0 -1
  489. package/build/assets/add-backend-modal-BywVr6uq.js +0 -1
  490. package/build/assets/agent-server-adapter-k8veH-1O.js +0 -5
  491. package/build/assets/agent-server-client-options-B-C8JaTb.js +0 -1
  492. package/build/assets/agent-server-conversation-service.api-CGbkhsLj.js +0 -1
  493. package/build/assets/api-key-entry-screen-Dnr5TcV0.js +0 -1
  494. package/build/assets/app-settings-Bgm2-yOM.js +0 -1
  495. package/build/assets/automation-detail-86M_yMqz.js +0 -1
  496. package/build/assets/automations-list-BWUILrqY.js +0 -1
  497. package/build/assets/backend-form-modal-Dl29wkF5.js +0 -1
  498. package/build/assets/browser-store-JRrcGdlk.js +0 -1
  499. package/build/assets/browser-wz8UzRmF.js +0 -5
  500. package/build/assets/chat-send-button-BhoO7T0s.js +0 -1
  501. package/build/assets/check-BhnsMsWy.js +0 -1
  502. package/build/assets/condenser-settings-DgQ0rsAf.js +0 -1
  503. package/build/assets/context-CI9FqkUp.js +0 -1
  504. package/build/assets/conversation-CayU5Lrr.js +0 -19
  505. package/build/assets/conversation-erinyBqT.js +0 -1
  506. package/build/assets/conversation-panel-DiI4q-8-.js +0 -1
  507. package/build/assets/conversation-service.api-shHWtFwu.js +0 -1
  508. package/build/assets/conversation-state-store-NEktJKfL.js +0 -1
  509. package/build/assets/declaration-D9ucT0fq.js +0 -1
  510. package/build/assets/dist-BCpEDEJq.js +0 -1
  511. package/build/assets/edit-automation-modal-8y9Uctxo.js +0 -1
  512. package/build/assets/entry.client-D_wtryHT.js +0 -2
  513. package/build/assets/files-tab-CxdtgkIe.js +0 -1
  514. package/build/assets/files-tab-store-DSKUhziI.js +0 -1
  515. package/build/assets/home-3joHMYSR.js +0 -1
  516. package/build/assets/index-redirect-BQ_55qN6.js +0 -1
  517. package/build/assets/install-server-modal-_7l1gcjd.js +0 -1
  518. package/build/assets/lesson-plan-CHGc-B8Y.js +0 -3
  519. package/build/assets/llm-not-configured-banner-BMGzpBPV.js +0 -27
  520. package/build/assets/llm-settings-BRotm_g6.js +0 -1
  521. package/build/assets/llm-settings-yDgbXP6Q.js +0 -1
  522. package/build/assets/loader-circle-1XB9nbwF.js +0 -1
  523. package/build/assets/manage-backends-modal-tfdzTXWU.js +0 -1
  524. package/build/assets/manifest-bbc1374e.js +0 -1
  525. package/build/assets/markdown-renderer-z446oW0Z.js +0 -2
  526. package/build/assets/mcp-Dopstf0t.js +0 -9
  527. package/build/assets/mcp-settings-redirect-l3Zbmc0F.js +0 -1
  528. package/build/assets/messages-CsFUrAen.js +0 -41
  529. package/build/assets/model-selector-sioC8xP4.js +0 -1
  530. package/build/assets/onboarding-CQh50i2z.js +0 -1
  531. package/build/assets/option-service.api-DjPvPAtd.js +0 -1
  532. package/build/assets/organization-service.api-BNc9Kn6n.js +0 -1
  533. package/build/assets/plus-DKUID4kd.js +0 -1
  534. package/build/assets/providers-BiWSYwRT.js +0 -1
  535. package/build/assets/proxy--Uy1iGsW.js +0 -1
  536. package/build/assets/query-client-config-Ca3tW1AH.js +0 -1
  537. package/build/assets/root-CRkz_h2T.css +0 -1
  538. package/build/assets/root-DrHpnAqU.js +0 -2
  539. package/build/assets/root-layout-Cf7FC9DC.js +0 -2
  540. package/build/assets/sdk-section-page-COlGKv4E.js +0 -1
  541. package/build/assets/sdk-settings-schema-Big50zDH.js +0 -1
  542. package/build/assets/secrets-settings-DYfTc5c8.js +0 -1
  543. package/build/assets/settings-DRmVFdOc.js +0 -1
  544. package/build/assets/settings-modal-CeRvS5Bc.js +0 -1
  545. package/build/assets/settings-service.api-CFt5HmXD.js +0 -1
  546. package/build/assets/shared-conversation-CBbpyPaD.js +0 -1
  547. package/build/assets/sidebar-mobile-menu-toggle-SKcwsEtD.js +0 -1
  548. package/build/assets/suspense-Bqb0V0q1.js +0 -1
  549. package/build/assets/telemetry-DCFAqpIA.js +0 -2
  550. package/build/assets/use-acp-credential-form-LjCIhMSK.js +0 -1
  551. package/build/assets/use-agent-settings-schema-D8x5LJVS.js +0 -1
  552. package/build/assets/use-config-5zEjywCL.js +0 -1
  553. package/build/assets/use-create-conversation-pV2eA-e9.js +0 -1
  554. package/build/assets/use-handle-plan-click-CwN_nNib.js +0 -1
  555. package/build/assets/use-is-authed-B8eLO-Xq.js +0 -1
  556. package/build/assets/use-llm-profiles-CWLfWjav.js +0 -1
  557. package/build/assets/use-runtime-is-ready-Bbory9-s.js +0 -1
  558. package/build/assets/use-save-settings-Xp8DD-jf.js +0 -1
  559. package/build/assets/use-settings-0SeqaDeW.js +0 -1
  560. package/build/assets/useMutation-Dm84WAKR.js +0 -1
  561. package/build/assets/useQuery-DGRSm7IT.js +0 -1
  562. package/build/assets/useTranslation-CL6FTf1b.js +0 -1
  563. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~dqseey1n-D_BSEDk4.js +0 -1
  564. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~jfbjdnyv-C5ZIbuq8.js +0 -177
  565. package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~mcp~settings~settings-index~~m3acswdn-CGm-Jxp5.js +0 -48
  566. package/build/assets/x-CCfDppg5.js +0 -1
  567. package/dist/api/settings-service/legacy-app-preferences-migration.cjs +0 -2
  568. package/dist/api/settings-service/legacy-app-preferences-migration.cjs.map +0 -1
  569. package/dist/api/settings-service/legacy-app-preferences-migration.d.ts +0 -34
  570. package/dist/api/settings-service/legacy-app-preferences-migration.js +0 -52
  571. package/dist/api/settings-service/legacy-app-preferences-migration.js.map +0 -1
  572. package/dist/hooks/query/use-openhands-verified-models.cjs +0 -2
  573. package/dist/hooks/query/use-openhands-verified-models.cjs.map +0 -1
  574. package/dist/hooks/query/use-openhands-verified-models.d.ts +0 -1
  575. package/dist/hooks/query/use-openhands-verified-models.js +0 -14
  576. package/dist/hooks/query/use-openhands-verified-models.js.map +0 -1
  577. package/dist/utils/normalize-display-model.cjs +0 -2
  578. package/dist/utils/normalize-display-model.cjs.map +0 -1
  579. package/dist/utils/normalize-display-model.d.ts +0 -9
  580. package/dist/utils/normalize-display-model.js +0 -13
  581. package/dist/utils/normalize-display-model.js.map +0 -1
  582. package/dist/utils/openhands-llm.cjs +0 -2
  583. package/dist/utils/openhands-llm.cjs.map +0 -1
  584. package/dist/utils/openhands-llm.d.ts +0 -15
  585. package/dist/utils/openhands-llm.js +0 -15
  586. package/dist/utils/openhands-llm.js.map +0 -1
  587. /package/build/assets/{agent-server-ui-style-scope-BwIZYdC1.js → agent-server-ui-style-scope-1MuaCC1k.js} +0 -0
  588. /package/build/assets/{common-Dh7OqABE.js → common-DqjLSBOt.js} +0 -0
  589. /package/build/assets/{environment-switch-store-CiurvTtK.js → environment-switch-store-CBXkf5bS.js} +0 -0
  590. /package/build/assets/{extension-module-card-classes-DP5J6ngE.js → extension-module-card-classes-CVGfXtoL.js} +0 -0
  591. /package/build/assets/{git-status-mapper-BI8FyUVp.js → git-status-mapper-DVFXF1_X.js} +0 -0
  592. /package/build/assets/{handle-capture-consent-CPMXU29K.js → handle-capture-consent-BTvatyfE.js} +0 -0
  593. /package/build/assets/{map-provider-BM2SJarp.js → map-provider-COgsZR9p.js} +0 -0
  594. /package/build/assets/{objectWithoutPropertiesLoose-DSQKyRhw.js → objectWithoutPropertiesLoose-B80nTzVc.js} +0 -0
  595. /package/build/assets/{preload-helper-CT1Z6Pdu.js → preload-helper-D_sPnjeL.js} +0 -0
  596. /package/build/assets/{rolldown-runtime-BFRubm34.js → rolldown-runtime-wFmyQBCi.js} +0 -0
  597. /package/build/assets/{sdk-settings-field-metadata-p2GVlft7.js → sdk-settings-field-metadata-Cc4D-ut7.js} +0 -0
  598. /package/build/assets/{settings-wh2sLLJM.js → settings-1287uR2J.js} +0 -0
  599. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~nh19ovoo-DJTFnsA1.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~cg1m3bv7-aUIHudsf.js} +0 -0
  600. /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ntycl9e1-BmZcy_kH.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ntycl9e1-DPPGSgT1.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-settings-schema.js","names":[],"sources":["../../../src/hooks/query/use-agent-settings-schema.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport { isNoBackend } from \"#/api/backend-registry/active-store\";\nimport SettingsService from \"#/api/settings-service/settings-service.api\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { SettingsSchema } from \"#/types/settings\";\nimport { useIsAuthed } from \"./use-is-authed\";\n\nconst useSettingsSchema = (\n type: \"agent\" | \"conversation\",\n fallbackSchema?: SettingsSchema | null,\n) => {\n const { data: userIsAuthenticated } = useIsAuthed();\n const { backend, orgId } = useActiveBackend();\n const hasBackend = !isNoBackend(backend);\n const { data, error, isLoading, isFetching } = useQuery({\n queryKey: [\n \"settings-schema\",\n type,\n backend.id,\n orgId,\n backend.kind,\n backend.host,\n backend.apiKey,\n ],\n queryFn:\n type === \"conversation\"\n ? SettingsService.getConversationSettingsSchema\n : SettingsService.getSettingsSchema,\n retry: false,\n refetchOnWindowFocus: false,\n staleTime: 1000 * 60 * 5,\n gcTime: 1000 * 60 * 15,\n enabled: !fallbackSchema && !!userIsAuthenticated && hasBackend,\n meta: {\n disableToast: true,\n },\n });\n\n if (fallbackSchema) {\n return {\n data: fallbackSchema,\n error: null,\n isLoading: false,\n isFetching: false,\n };\n }\n\n return {\n data,\n error,\n isLoading,\n isFetching,\n };\n};\n\nexport const useAgentSettingsSchema = (\n fallbackSchema?: SettingsSchema | null,\n) => useSettingsSchema(\"agent\", fallbackSchema);\n\nexport const useConversationSettingsSchema = (\n fallbackSchema?: SettingsSchema | null,\n) => useSettingsSchema(\"conversation\", fallbackSchema);\n"],"mappings":";;;;;;AAOA,IAAM,KACJ,GACA,MACG;CACH,IAAM,EAAE,MAAM,MAAwB,GAAa,EAC7C,EAAE,YAAS,aAAU,GAAkB,EACvC,IAAa,CAAC,EAAY,EAAQ,EAClC,EAAE,SAAM,UAAO,cAAW,kBAAe,EAAS;EACtD,UAAU;GACR;GACA;GACA,EAAQ;GACR;GACA,EAAQ;GACR,EAAQ;GACR,EAAQ;GACT;EACD,SACE,MAAS,iBACL,EAAgB,gCAChB,EAAgB;EACtB,OAAO;EACP,sBAAsB;EACtB,WAAW,MAAO,KAAK;EACvB,QAAQ,MAAO,KAAK;EACpB,SAAS,CAAC,KAAkB,CAAC,CAAC,KAAuB;EACrD,MAAM,EACJ,cAAc,IACf;EACF,CAAC;AAWF,QATI,IACK;EACL,MAAM;EACN,OAAO;EACP,WAAW;EACX,YAAY;EACb,GAGI;EACL;EACA;EACA;EACA;EACD;GAGU,KACX,MACG,EAAkB,SAAS,EAAe,EAElC,KACX,MACG,EAAkB,gBAAgB,EAAe"}
1
+ {"version":3,"file":"use-agent-settings-schema.js","names":[],"sources":["../../../src/hooks/query/use-agent-settings-schema.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { isNoBackend } from \"#/api/backend-registry/active-store\";\nimport SettingsService from \"#/api/settings-service/settings-service.api\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { SettingsSchema } from \"#/types/settings\";\nimport { withLlmSubscriptionSchemaFields } from \"#/utils/llm-subscription-schema\";\nimport { useIsAuthed } from \"./use-is-authed\";\n\nconst useSettingsSchema = (\n type: \"agent\" | \"conversation\",\n fallbackSchema?: SettingsSchema | null,\n) => {\n const { data: userIsAuthenticated } = useIsAuthed();\n const { backend, orgId } = useActiveBackend();\n const hasBackend = !isNoBackend(backend);\n const { data, error, isLoading, isFetching } = useQuery({\n queryKey: [\n \"settings-schema\",\n type,\n backend.id,\n orgId,\n backend.kind,\n backend.host,\n backend.apiKey,\n ],\n queryFn:\n type === \"conversation\"\n ? SettingsService.getConversationSettingsSchema\n : SettingsService.getSettingsSchema,\n retry: false,\n refetchOnWindowFocus: false,\n staleTime: 1000 * 60 * 5,\n gcTime: 1000 * 60 * 15,\n enabled: !fallbackSchema && !!userIsAuthenticated && hasBackend,\n meta: {\n disableToast: true,\n },\n });\n\n const fallbackData = useMemo(\n () =>\n type === \"agent\"\n ? withLlmSubscriptionSchemaFields(fallbackSchema)\n : fallbackSchema,\n [fallbackSchema, type],\n );\n\n const queryData = useMemo(\n () => (type === \"agent\" ? withLlmSubscriptionSchemaFields(data) : data),\n [data, type],\n );\n\n if (fallbackSchema) {\n return {\n data: fallbackData,\n error: null,\n isLoading: false,\n isFetching: false,\n };\n }\n\n return {\n data: queryData,\n error,\n isLoading,\n isFetching,\n };\n};\n\nexport const useAgentSettingsSchema = (\n fallbackSchema?: SettingsSchema | null,\n) => useSettingsSchema(\"agent\", fallbackSchema);\n\nexport const useConversationSettingsSchema = (\n fallbackSchema?: SettingsSchema | null,\n) => useSettingsSchema(\"conversation\", fallbackSchema);\n"],"mappings":";;;;;;;;AASA,IAAM,KACJ,GACA,MACG;CACH,IAAM,EAAE,MAAM,MAAwB,GAAa,EAC7C,EAAE,YAAS,aAAU,GAAkB,EACvC,IAAa,CAAC,EAAY,EAAQ,EAClC,EAAE,SAAM,UAAO,cAAW,kBAAe,EAAS;EACtD,UAAU;GACR;GACA;GACA,EAAQ;GACR;GACA,EAAQ;GACR,EAAQ;GACR,EAAQ;GACT;EACD,SACE,MAAS,iBACL,EAAgB,gCAChB,EAAgB;EACtB,OAAO;EACP,sBAAsB;EACtB,WAAW,MAAO,KAAK;EACvB,QAAQ,MAAO,KAAK;EACpB,SAAS,CAAC,KAAkB,CAAC,CAAC,KAAuB;EACrD,MAAM,EACJ,cAAc,IACf;EACF,CAAC,EAEI,IAAe,QAEjB,MAAS,UACL,EAAgC,EAAe,GAC/C,GACN,CAAC,GAAgB,EAAK,CACvB,EAEK,IAAY,QACT,MAAS,UAAU,EAAgC,EAAK,GAAG,GAClE,CAAC,GAAM,EAAK,CACb;AAWD,QATI,IACK;EACL,MAAM;EACN,OAAO;EACP,WAAW;EACX,YAAY;EACb,GAGI;EACL,MAAM;EACN;EACA;EACA;EACD;GAGU,KACX,MACG,EAAkB,SAAS,EAAe,EAElC,KACX,MACG,EAAkB,gBAAgB,EAAe"}
@@ -1,2 +1,2 @@
1
- const e=require(`../../_virtual/_rolldown/runtime.cjs`),t=require(`../../node_modules/@openhands/typescript-client/dist/client/settings-client.cjs`),n=require(`../../api/agent-server-client-options.cjs`),r=require(`../../api/agent-server-compatibility.cjs`),i=require(`../../api/backend-registry/health-store.cjs`),a=require(`../../node_modules/@tanstack/react-query/build/modern/useQueries.cjs`),o=require(`../../api/cloud/organization-service.api.cjs`);let s=require(`react`);s=e.__toESM(s,1);var c=1e4,l=4e3,u=`Invalid API key`;function d(e){return e===u}async function f(e){if(e.kind===`cloud`)return await o.getCurrentCloudApiKey(e),!0;try{await new t.SettingsClient(n.getAgentServerClientOptions({host:e.host,sessionApiKey:e.apiKey||null,timeout:l})).getSettings()}catch(e){throw r.isSdkHttpStatusError(e,401)?Error(u):e}return!0}function p(e,t={}){let{probeDisabledOnce:n=!1}=t,r=s.default.useSyncExternalStore(i.subscribeBackendHealth,i.getHealthSnapshot,i.getHealthSnapshot),o=a.useQueries({queries:e.map(e=>{let t=r[e.id]?.disabled===!0,a=!t||n;return{queryKey:[`backend-health`,e.id,e.kind,e.host,e.apiKey??``],queryFn:async()=>{try{let t=await f(e);return i.recordBackendSuccess(e.id),t}catch(t){throw i.recordBackendFailure(e.id,t),t}},enabled:a,refetchInterval:t?!1:c,refetchIntervalInBackground:!1,refetchOnMount:t&&n?`always`:!0,refetchOnReconnect:!t,refetchOnWindowFocus:!t,retry:!1,staleTime:t?0:c,meta:{disableToast:!0}}})}),l={};return e.forEach((e,t)=>{let n=o[t],i=r[e.id],a=i?.disabled===!0,s=i?.consecutiveFailures??0,c=i?.lastError??null,u;u=a?!1:n.isSuccess?!0:n.isError?!1:null,l[e.id]={isConnected:u,consecutiveFailures:s,lastError:c,disabled:a}}),l}exports.isInvalidBackendApiKeyHealthError=d,exports.useBackendsHealth=p;
1
+ const e=require(`../../_virtual/_rolldown/runtime.cjs`),t=require(`../../node_modules/@openhands/typescript-client/dist/client/server-client.cjs`),n=require(`../../node_modules/@openhands/typescript-client/dist/client/settings-client.cjs`),r=require(`../../api/agent-server-client-options.cjs`),i=require(`../../api/agent-server-compatibility.cjs`),a=require(`../../api/backend-registry/health-store.cjs`),o=require(`../../node_modules/@tanstack/react-query/build/modern/useQueries.cjs`),s=require(`../../api/cloud/organization-service.api.cjs`);let c=require(`react`);c=e.__toESM(c,1);var l=1e4,u=4e3,d=`Invalid API key`;function f(e){return e===d}async function p(e){if(e.kind===`cloud`)return await s.getCurrentCloudApiKey(e),!0;try{let a=r.getAgentServerClientOptions({host:e.host,sessionApiKey:e.apiKey||null,timeout:u});await new n.SettingsClient(a).getSettings(),i.assertAgentServerVersionIsSupported(await new t.ServerClient(a).getServerInfo())}catch(e){throw i.isSdkHttpStatusError(e,401)?Error(d):e}return!0}function m(e,t={}){let{probeDisabledOnce:n=!1}=t,r=c.default.useSyncExternalStore(a.subscribeBackendHealth,a.getHealthSnapshot,a.getHealthSnapshot),i=o.useQueries({queries:e.map(e=>{let t=r[e.id]?.disabled===!0,i=!t||n;return{queryKey:[`backend-health`,e.id,e.kind,e.host,e.apiKey??``],queryFn:async()=>{try{let t=await p(e);return a.recordBackendSuccess(e.id),t}catch(t){throw a.recordBackendFailure(e.id,t),t}},enabled:i,refetchInterval:t?!1:l,refetchIntervalInBackground:!1,refetchOnMount:t&&n?`always`:!0,refetchOnReconnect:!t,refetchOnWindowFocus:!t,retry:!1,staleTime:t?0:l,meta:{disableToast:!0}}})}),s={};return e.forEach((e,t)=>{let n=i[t],a=r[e.id],o=a?.disabled===!0,c=a?.consecutiveFailures??0,l=a?.lastError??null,u;u=o?!1:n.isSuccess?!0:n.isError?!1:null,s[e.id]={isConnected:u,consecutiveFailures:c,lastError:l,disabled:o}}),s}exports.isInvalidBackendApiKeyHealthError=f,exports.useBackendsHealth=m;
2
2
  //# sourceMappingURL=use-backends-health.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-backends-health.cjs","names":[],"sources":["../../../src/hooks/query/use-backends-health.ts"],"sourcesContent":["import React from \"react\";\nimport { useQueries } from \"@tanstack/react-query\";\nimport { SettingsClient } from \"@openhands/typescript-client/clients\";\nimport { getCurrentCloudApiKey } from \"#/api/cloud/organization-service.api\";\nimport { isSdkHttpStatusError } from \"#/api/agent-server-compatibility\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport {\n getHealthSnapshot,\n recordBackendFailure,\n recordBackendSuccess,\n subscribeBackendHealth,\n} from \"#/api/backend-registry/health-store\";\nimport { MAX_CONSECUTIVE_FAILURES } from \"#/api/backend-registry/health-storage\";\n\nconst REFRESH_INTERVAL_MS = 10000;\nconst PROBE_TIMEOUT_MS = 4000;\nexport const INVALID_BACKEND_API_KEY_ERROR = \"Invalid API key\";\n\nexport function isInvalidBackendApiKeyHealthError(\n error: string | null | undefined,\n): boolean {\n return error === INVALID_BACKEND_API_KEY_ERROR;\n}\n\n/**\n * Probe a single backend for connectivity. The probe path differs by\n * backend kind:\n *\n * - Local agent-server: GET `/api/settings` via the typescript-client.\n * Unlike `/server_info`, this validates the configured session API key.\n * - Cloud: GET `/api/keys/current` via the bundled local\n * agent-server's `/api/cloud-proxy`. That endpoint is lightweight,\n * requires auth, and `getCurrentCloudApiKey` already absorbs the\n * legacy-key 400 fallback so we treat that as \"connected\" too.\n * Any other failure (network, 401, 5xx, …) means the backend is\n * not reachable / not usable from the GUI.\n *\n * Throws on failure so React Query marks the query as errored — the\n * dropdown reads `isSuccess` to flip the indicator green.\n */\nasync function probeBackend(backend: Backend): Promise<true> {\n if (backend.kind === \"cloud\") {\n await getCurrentCloudApiKey(backend);\n return true;\n }\n\n try {\n await new SettingsClient(\n getAgentServerClientOptions({\n host: backend.host,\n sessionApiKey: backend.apiKey || null,\n timeout: PROBE_TIMEOUT_MS,\n }),\n ).getSettings();\n } catch (error) {\n if (isSdkHttpStatusError(error, 401)) {\n throw new Error(INVALID_BACKEND_API_KEY_ERROR);\n }\n throw error;\n }\n return true;\n}\n\nexport interface BackendHealth {\n /** `null` while the first probe is in flight; then `true` / `false`. */\n isConnected: boolean | null;\n /** Number of consecutive failed probes since the last success. */\n consecutiveFailures: number;\n /** Last error message captured from a failed probe, if any. */\n lastError: string | null;\n /**\n * `true` once `consecutiveFailures` reaches the cap. While disabled,\n * ordinary background polling stops and survives a page refresh in\n * that state.\n */\n disabled: boolean;\n}\n\nexport interface UseBackendsHealthOptions {\n /**\n * Re-probe disabled backends once when the hook mounts. Used by the\n * Manage Backends modal so a recovered backend can clear its stale\n * persisted error state without forcing the user to edit the config.\n */\n probeDisabledOnce?: boolean;\n}\n\n/**\n * Poll every backend in `backends` once every 10s and report a simple\n * connected / disconnected verdict per backend id.\n *\n * The query key includes `host` and `apiKey` so editing a backend's\n * connection details re-keys the query and triggers an immediate\n * refetch instead of waiting for the next tick.\n *\n * After `MAX_CONSECUTIVE_FAILURES` failures in a row, ordinary polling\n * stops for that backend until the user updates its host / apiKey.\n * Callers can still opt into a one-shot recheck for disabled backends\n * (for example when the user explicitly opens Manage Backends). The\n * failure count and last error live in localStorage so a page refresh\n * does not silently re-arm polling against a backend that's known to\n * be unreachable.\n */\nexport function useBackendsHealth(\n backends: Backend[],\n options: UseBackendsHealthOptions = {},\n): Record<string, BackendHealth> {\n const { probeDisabledOnce = false } = options;\n const healthMap = React.useSyncExternalStore(\n subscribeBackendHealth,\n getHealthSnapshot,\n getHealthSnapshot,\n );\n\n const results = useQueries({\n queries: backends.map((b) => {\n const isDisabled = healthMap[b.id]?.disabled === true;\n const shouldProbe = !isDisabled || probeDisabledOnce;\n return {\n queryKey: [\n \"backend-health\",\n b.id,\n b.kind,\n b.host,\n b.apiKey ?? \"\",\n ] as const,\n queryFn: async () => {\n try {\n const result = await probeBackend(b);\n recordBackendSuccess(b.id);\n return result;\n } catch (err) {\n recordBackendFailure(b.id, err);\n throw err;\n }\n },\n enabled: shouldProbe,\n refetchInterval: isDisabled ? (false as const) : REFRESH_INTERVAL_MS,\n refetchIntervalInBackground: false,\n refetchOnMount: isDisabled && probeDisabledOnce ? \"always\" : true,\n refetchOnReconnect: !isDisabled,\n refetchOnWindowFocus: !isDisabled,\n retry: false,\n // Keep the previous verdict visible while the next probe is in\n // flight so the indicator doesn't flicker on routine polling.\n staleTime: isDisabled ? 0 : REFRESH_INTERVAL_MS,\n meta: { disableToast: true },\n };\n }),\n });\n\n const out: Record<string, BackendHealth> = {};\n backends.forEach((b, i) => {\n const r = results[i];\n const entry = healthMap[b.id];\n const disabled = entry?.disabled === true;\n const consecutiveFailures = entry?.consecutiveFailures ?? 0;\n const lastError = entry?.lastError ?? null;\n\n let isConnected: boolean | null;\n if (disabled) {\n // Polling stopped after hitting the cap — treat as disconnected\n // so existing consumers (dot, badge) render red without needing\n // to know about the new fields.\n isConnected = false;\n } else if (r.isSuccess) isConnected = true;\n else if (r.isError) isConnected = false;\n else isConnected = null;\n\n out[b.id] = { isConnected, consecutiveFailures, lastError, disabled };\n });\n return out;\n}\n\nexport { MAX_CONSECUTIVE_FAILURES };\n"],"mappings":"+eAeA,IAAM,EAAsB,IACtB,EAAmB,IACZ,EAAgC,kBAE7C,SAAgB,EACd,EACS,CACT,OAAO,IAAU,EAmBnB,eAAe,EAAa,EAAiC,CAC3D,GAAI,EAAQ,OAAS,QAEnB,OADA,MAAM,EAAA,sBAAsB,EAAQ,CAC7B,GAGT,GAAI,CACF,MAAM,IAAI,EAAA,eACR,EAAA,4BAA4B,CAC1B,KAAM,EAAQ,KACd,cAAe,EAAQ,QAAU,KACjC,QAAS,EACV,CAAC,CACH,CAAC,aAAa,OACR,EAAO,CAId,MAHI,EAAA,qBAAqB,EAAO,IAAI,CACxB,MAAM,EAA8B,CAE1C,EAER,MAAO,GA2CT,SAAgB,EACd,EACA,EAAoC,EAAE,CACP,CAC/B,GAAM,CAAE,oBAAoB,IAAU,EAChC,EAAY,EAAA,QAAM,qBACtB,EAAA,uBACA,EAAA,kBACA,EAAA,kBACD,CAEK,EAAU,EAAA,WAAW,CACzB,QAAS,EAAS,IAAK,GAAM,CAC3B,IAAM,EAAa,EAAU,EAAE,KAAK,WAAa,GAC3C,EAAc,CAAC,GAAc,EACnC,MAAO,CACL,SAAU,CACR,iBACA,EAAE,GACF,EAAE,KACF,EAAE,KACF,EAAE,QAAU,GACb,CACD,QAAS,SAAY,CACnB,GAAI,CACF,IAAM,EAAS,MAAM,EAAa,EAAE,CAEpC,OADA,EAAA,qBAAqB,EAAE,GAAG,CACnB,QACA,EAAK,CAEZ,MADA,EAAA,qBAAqB,EAAE,GAAI,EAAI,CACzB,IAGV,QAAS,EACT,gBAAiB,EAAc,GAAkB,EACjD,4BAA6B,GAC7B,eAAgB,GAAc,EAAoB,SAAW,GAC7D,mBAAoB,CAAC,EACrB,qBAAsB,CAAC,EACvB,MAAO,GAGP,UAAW,EAAa,EAAI,EAC5B,KAAM,CAAE,aAAc,GAAM,CAC7B,EACD,CACH,CAAC,CAEI,EAAqC,EAAE,CAoB7C,OAnBA,EAAS,SAAS,EAAG,IAAM,CACzB,IAAM,EAAI,EAAQ,GACZ,EAAQ,EAAU,EAAE,IACpB,EAAW,GAAO,WAAa,GAC/B,EAAsB,GAAO,qBAAuB,EACpD,EAAY,GAAO,WAAa,KAElC,EACJ,AAOK,EAPD,EAIY,GACL,EAAE,UAAyB,GAC7B,EAAE,QAAuB,GACf,KAEnB,EAAI,EAAE,IAAM,CAAE,cAAa,sBAAqB,YAAW,WAAU,EACrE,CACK"}
1
+ {"version":3,"file":"use-backends-health.cjs","names":[],"sources":["../../../src/hooks/query/use-backends-health.ts"],"sourcesContent":["import React from \"react\";\nimport { useQueries } from \"@tanstack/react-query\";\nimport {\n ServerClient,\n SettingsClient,\n} from \"@openhands/typescript-client/clients\";\nimport { getCurrentCloudApiKey } from \"#/api/cloud/organization-service.api\";\nimport {\n assertAgentServerVersionIsSupported,\n isSdkHttpStatusError,\n} from \"#/api/agent-server-compatibility\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport {\n getHealthSnapshot,\n recordBackendFailure,\n recordBackendSuccess,\n subscribeBackendHealth,\n} from \"#/api/backend-registry/health-store\";\nimport { MAX_CONSECUTIVE_FAILURES } from \"#/api/backend-registry/health-storage\";\n\nconst REFRESH_INTERVAL_MS = 10000;\nconst PROBE_TIMEOUT_MS = 4000;\nexport const INVALID_BACKEND_API_KEY_ERROR = \"Invalid API key\";\n\nexport function isInvalidBackendApiKeyHealthError(\n error: string | null | undefined,\n): boolean {\n return error === INVALID_BACKEND_API_KEY_ERROR;\n}\n\n/**\n * Probe a single backend for connectivity. The probe path differs by\n * backend kind:\n *\n * - Local agent-server: GET `/api/settings`, then `/server_info` via the\n * typescript-client. The settings call validates the configured session\n * API key; the server info call validates the version compatibility floor.\n * - Cloud: GET `/api/keys/current` via the bundled local\n * agent-server's `/api/cloud-proxy`. That endpoint is lightweight,\n * requires auth, and `getCurrentCloudApiKey` already absorbs the\n * legacy-key 400 fallback so we treat that as \"connected\" too.\n * Any other failure (network, 401, 5xx, …) means the backend is\n * not reachable / not usable from the GUI.\n *\n * Throws on failure so React Query marks the query as errored — the\n * dropdown reads `isSuccess` to flip the indicator green.\n */\nasync function probeBackend(backend: Backend): Promise<true> {\n if (backend.kind === \"cloud\") {\n await getCurrentCloudApiKey(backend);\n return true;\n }\n\n try {\n const clientOptions = getAgentServerClientOptions({\n host: backend.host,\n sessionApiKey: backend.apiKey || null,\n timeout: PROBE_TIMEOUT_MS,\n });\n\n await new SettingsClient(clientOptions).getSettings();\n const serverInfo = await new ServerClient(clientOptions).getServerInfo();\n assertAgentServerVersionIsSupported(serverInfo);\n } catch (error) {\n if (isSdkHttpStatusError(error, 401)) {\n throw new Error(INVALID_BACKEND_API_KEY_ERROR);\n }\n throw error;\n }\n return true;\n}\n\nexport interface BackendHealth {\n /** `null` while the first probe is in flight; then `true` / `false`. */\n isConnected: boolean | null;\n /** Number of consecutive failed probes since the last success. */\n consecutiveFailures: number;\n /** Last error message captured from a failed probe, if any. */\n lastError: string | null;\n /**\n * `true` once `consecutiveFailures` reaches the cap. While disabled,\n * ordinary background polling stops and survives a page refresh in\n * that state.\n */\n disabled: boolean;\n}\n\nexport interface UseBackendsHealthOptions {\n /**\n * Re-probe disabled backends once when the hook mounts. Used by the\n * Manage Backends modal so a recovered backend can clear its stale\n * persisted error state without forcing the user to edit the config.\n */\n probeDisabledOnce?: boolean;\n}\n\n/**\n * Poll every backend in `backends` once every 10s and report a simple\n * connected / disconnected verdict per backend id.\n *\n * The query key includes `host` and `apiKey` so editing a backend's\n * connection details re-keys the query and triggers an immediate\n * refetch instead of waiting for the next tick.\n *\n * After `MAX_CONSECUTIVE_FAILURES` failures in a row, ordinary polling\n * stops for that backend until the user updates its host / apiKey.\n * Callers can still opt into a one-shot recheck for disabled backends\n * (for example when the user explicitly opens Manage Backends). The\n * failure count and last error live in localStorage so a page refresh\n * does not silently re-arm polling against a backend that's known to\n * be unreachable.\n */\nexport function useBackendsHealth(\n backends: Backend[],\n options: UseBackendsHealthOptions = {},\n): Record<string, BackendHealth> {\n const { probeDisabledOnce = false } = options;\n const healthMap = React.useSyncExternalStore(\n subscribeBackendHealth,\n getHealthSnapshot,\n getHealthSnapshot,\n );\n\n const results = useQueries({\n queries: backends.map((b) => {\n const isDisabled = healthMap[b.id]?.disabled === true;\n const shouldProbe = !isDisabled || probeDisabledOnce;\n return {\n queryKey: [\n \"backend-health\",\n b.id,\n b.kind,\n b.host,\n b.apiKey ?? \"\",\n ] as const,\n queryFn: async () => {\n try {\n const result = await probeBackend(b);\n recordBackendSuccess(b.id);\n return result;\n } catch (err) {\n recordBackendFailure(b.id, err);\n throw err;\n }\n },\n enabled: shouldProbe,\n refetchInterval: isDisabled ? (false as const) : REFRESH_INTERVAL_MS,\n refetchIntervalInBackground: false,\n refetchOnMount: isDisabled && probeDisabledOnce ? \"always\" : true,\n refetchOnReconnect: !isDisabled,\n refetchOnWindowFocus: !isDisabled,\n retry: false,\n // Keep the previous verdict visible while the next probe is in\n // flight so the indicator doesn't flicker on routine polling.\n staleTime: isDisabled ? 0 : REFRESH_INTERVAL_MS,\n meta: { disableToast: true },\n };\n }),\n });\n\n const out: Record<string, BackendHealth> = {};\n backends.forEach((b, i) => {\n const r = results[i];\n const entry = healthMap[b.id];\n const disabled = entry?.disabled === true;\n const consecutiveFailures = entry?.consecutiveFailures ?? 0;\n const lastError = entry?.lastError ?? null;\n\n let isConnected: boolean | null;\n if (disabled) {\n // Polling stopped after hitting the cap — treat as disconnected\n // so existing consumers (dot, badge) render red without needing\n // to know about the new fields.\n isConnected = false;\n } else if (r.isSuccess) isConnected = true;\n else if (r.isError) isConnected = false;\n else isConnected = null;\n\n out[b.id] = { isConnected, consecutiveFailures, lastError, disabled };\n });\n return out;\n}\n\nexport { MAX_CONSECUTIVE_FAILURES };\n"],"mappings":"0kBAqBA,IAAM,EAAsB,IACtB,EAAmB,IACZ,EAAgC,kBAE7C,SAAgB,EACd,EACS,CACT,OAAO,IAAU,EAoBnB,eAAe,EAAa,EAAiC,CAC3D,GAAI,EAAQ,OAAS,QAEnB,OADA,MAAM,EAAA,sBAAsB,EAAQ,CAC7B,GAGT,GAAI,CACF,IAAM,EAAgB,EAAA,4BAA4B,CAChD,KAAM,EAAQ,KACd,cAAe,EAAQ,QAAU,KACjC,QAAS,EACV,CAAC,CAEF,MAAM,IAAI,EAAA,eAAe,EAAc,CAAC,aAAa,CAErD,EAAA,oCAAoC,MADX,IAAI,EAAA,aAAa,EAAc,CAAC,eAAe,CACzB,OACxC,EAAO,CAId,MAHI,EAAA,qBAAqB,EAAO,IAAI,CACxB,MAAM,EAA8B,CAE1C,EAER,MAAO,GA2CT,SAAgB,EACd,EACA,EAAoC,EAAE,CACP,CAC/B,GAAM,CAAE,oBAAoB,IAAU,EAChC,EAAY,EAAA,QAAM,qBACtB,EAAA,uBACA,EAAA,kBACA,EAAA,kBACD,CAEK,EAAU,EAAA,WAAW,CACzB,QAAS,EAAS,IAAK,GAAM,CAC3B,IAAM,EAAa,EAAU,EAAE,KAAK,WAAa,GAC3C,EAAc,CAAC,GAAc,EACnC,MAAO,CACL,SAAU,CACR,iBACA,EAAE,GACF,EAAE,KACF,EAAE,KACF,EAAE,QAAU,GACb,CACD,QAAS,SAAY,CACnB,GAAI,CACF,IAAM,EAAS,MAAM,EAAa,EAAE,CAEpC,OADA,EAAA,qBAAqB,EAAE,GAAG,CACnB,QACA,EAAK,CAEZ,MADA,EAAA,qBAAqB,EAAE,GAAI,EAAI,CACzB,IAGV,QAAS,EACT,gBAAiB,EAAc,GAAkB,EACjD,4BAA6B,GAC7B,eAAgB,GAAc,EAAoB,SAAW,GAC7D,mBAAoB,CAAC,EACrB,qBAAsB,CAAC,EACvB,MAAO,GAGP,UAAW,EAAa,EAAI,EAC5B,KAAM,CAAE,aAAc,GAAM,CAC7B,EACD,CACH,CAAC,CAEI,EAAqC,EAAE,CAoB7C,OAnBA,EAAS,SAAS,EAAG,IAAM,CACzB,IAAM,EAAI,EAAQ,GACZ,EAAQ,EAAU,EAAE,IACpB,EAAW,GAAO,WAAa,GAC/B,EAAsB,GAAO,qBAAuB,EACpD,EAAY,GAAO,WAAa,KAElC,EACJ,AAOK,EAPD,EAIY,GACL,EAAE,UAAyB,GAC7B,EAAE,QAAuB,GACf,KAEnB,EAAI,EAAE,IAAM,CAAE,cAAa,sBAAqB,YAAW,WAAU,EACrE,CACK"}
@@ -1,31 +1,33 @@
1
- import { SettingsClient as e } from "../../node_modules/@openhands/typescript-client/dist/client/settings-client.js";
2
- import { getAgentServerClientOptions as t } from "../../api/agent-server-client-options.js";
3
- import { isSdkHttpStatusError as n } from "../../api/agent-server-compatibility.js";
4
- import { getHealthSnapshot as r, recordBackendFailure as i, recordBackendSuccess as a, subscribeBackendHealth as o } from "../../api/backend-registry/health-store.js";
5
- import { useQueries as s } from "../../node_modules/@tanstack/react-query/build/modern/useQueries.js";
6
- import { getCurrentCloudApiKey as c } from "../../api/cloud/organization-service.api.js";
7
- import l from "react";
1
+ import { ServerClient as e } from "../../node_modules/@openhands/typescript-client/dist/client/server-client.js";
2
+ import { SettingsClient as t } from "../../node_modules/@openhands/typescript-client/dist/client/settings-client.js";
3
+ import { getAgentServerClientOptions as n } from "../../api/agent-server-client-options.js";
4
+ import { assertAgentServerVersionIsSupported as r, isSdkHttpStatusError as i } from "../../api/agent-server-compatibility.js";
5
+ import { getHealthSnapshot as a, recordBackendFailure as o, recordBackendSuccess as s, subscribeBackendHealth as c } from "../../api/backend-registry/health-store.js";
6
+ import { useQueries as l } from "../../node_modules/@tanstack/react-query/build/modern/useQueries.js";
7
+ import { getCurrentCloudApiKey as u } from "../../api/cloud/organization-service.api.js";
8
+ import d from "react";
8
9
  //#region src/hooks/query/use-backends-health.ts
9
- var u = 1e4, d = 4e3, f = "Invalid API key";
10
- function p(e) {
11
- return e === f;
10
+ var f = 1e4, p = 4e3, m = "Invalid API key";
11
+ function h(e) {
12
+ return e === m;
12
13
  }
13
- async function m(r) {
14
- if (r.kind === "cloud") return await c(r), !0;
14
+ async function g(a) {
15
+ if (a.kind === "cloud") return await u(a), !0;
15
16
  try {
16
- await new e(t({
17
- host: r.host,
18
- sessionApiKey: r.apiKey || null,
19
- timeout: d
20
- })).getSettings();
17
+ let i = n({
18
+ host: a.host,
19
+ sessionApiKey: a.apiKey || null,
20
+ timeout: p
21
+ });
22
+ await new t(i).getSettings(), r(await new e(i).getServerInfo());
21
23
  } catch (e) {
22
- throw n(e, 401) ? Error(f) : e;
24
+ throw i(e, 401) ? Error(m) : e;
23
25
  }
24
26
  return !0;
25
27
  }
26
- function h(e, t = {}) {
27
- let { probeDisabledOnce: n = !1 } = t, c = l.useSyncExternalStore(o, r, r), d = s({ queries: e.map((e) => {
28
- let t = c[e.id]?.disabled === !0, r = !t || n;
28
+ function _(e, t = {}) {
29
+ let { probeDisabledOnce: n = !1 } = t, r = d.useSyncExternalStore(c, a, a), i = l({ queries: e.map((e) => {
30
+ let t = r[e.id]?.disabled === !0, i = !t || n;
29
31
  return {
30
32
  queryKey: [
31
33
  "backend-health",
@@ -36,34 +38,34 @@ function h(e, t = {}) {
36
38
  ],
37
39
  queryFn: async () => {
38
40
  try {
39
- let t = await m(e);
40
- return a(e.id), t;
41
+ let t = await g(e);
42
+ return s(e.id), t;
41
43
  } catch (t) {
42
- throw i(e.id, t), t;
44
+ throw o(e.id, t), t;
43
45
  }
44
46
  },
45
- enabled: r,
46
- refetchInterval: t ? !1 : u,
47
+ enabled: i,
48
+ refetchInterval: t ? !1 : f,
47
49
  refetchIntervalInBackground: !1,
48
50
  refetchOnMount: t && n ? "always" : !0,
49
51
  refetchOnReconnect: !t,
50
52
  refetchOnWindowFocus: !t,
51
53
  retry: !1,
52
- staleTime: t ? 0 : u,
54
+ staleTime: t ? 0 : f,
53
55
  meta: { disableToast: !0 }
54
56
  };
55
- }) }), f = {};
57
+ }) }), u = {};
56
58
  return e.forEach((e, t) => {
57
- let n = d[t], r = c[e.id], i = r?.disabled === !0, a = r?.consecutiveFailures ?? 0, o = r?.lastError ?? null, s;
58
- s = i ? !1 : n.isSuccess ? !0 : n.isError ? !1 : null, f[e.id] = {
59
- isConnected: s,
60
- consecutiveFailures: a,
61
- lastError: o,
62
- disabled: i
59
+ let n = i[t], a = r[e.id], o = a?.disabled === !0, s = a?.consecutiveFailures ?? 0, c = a?.lastError ?? null, l;
60
+ l = o ? !1 : n.isSuccess ? !0 : n.isError ? !1 : null, u[e.id] = {
61
+ isConnected: l,
62
+ consecutiveFailures: s,
63
+ lastError: c,
64
+ disabled: o
63
65
  };
64
- }), f;
66
+ }), u;
65
67
  }
66
68
  //#endregion
67
- export { p as isInvalidBackendApiKeyHealthError, h as useBackendsHealth };
69
+ export { h as isInvalidBackendApiKeyHealthError, _ as useBackendsHealth };
68
70
 
69
71
  //# sourceMappingURL=use-backends-health.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-backends-health.js","names":[],"sources":["../../../src/hooks/query/use-backends-health.ts"],"sourcesContent":["import React from \"react\";\nimport { useQueries } from \"@tanstack/react-query\";\nimport { SettingsClient } from \"@openhands/typescript-client/clients\";\nimport { getCurrentCloudApiKey } from \"#/api/cloud/organization-service.api\";\nimport { isSdkHttpStatusError } from \"#/api/agent-server-compatibility\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport {\n getHealthSnapshot,\n recordBackendFailure,\n recordBackendSuccess,\n subscribeBackendHealth,\n} from \"#/api/backend-registry/health-store\";\nimport { MAX_CONSECUTIVE_FAILURES } from \"#/api/backend-registry/health-storage\";\n\nconst REFRESH_INTERVAL_MS = 10000;\nconst PROBE_TIMEOUT_MS = 4000;\nexport const INVALID_BACKEND_API_KEY_ERROR = \"Invalid API key\";\n\nexport function isInvalidBackendApiKeyHealthError(\n error: string | null | undefined,\n): boolean {\n return error === INVALID_BACKEND_API_KEY_ERROR;\n}\n\n/**\n * Probe a single backend for connectivity. The probe path differs by\n * backend kind:\n *\n * - Local agent-server: GET `/api/settings` via the typescript-client.\n * Unlike `/server_info`, this validates the configured session API key.\n * - Cloud: GET `/api/keys/current` via the bundled local\n * agent-server's `/api/cloud-proxy`. That endpoint is lightweight,\n * requires auth, and `getCurrentCloudApiKey` already absorbs the\n * legacy-key 400 fallback so we treat that as \"connected\" too.\n * Any other failure (network, 401, 5xx, …) means the backend is\n * not reachable / not usable from the GUI.\n *\n * Throws on failure so React Query marks the query as errored — the\n * dropdown reads `isSuccess` to flip the indicator green.\n */\nasync function probeBackend(backend: Backend): Promise<true> {\n if (backend.kind === \"cloud\") {\n await getCurrentCloudApiKey(backend);\n return true;\n }\n\n try {\n await new SettingsClient(\n getAgentServerClientOptions({\n host: backend.host,\n sessionApiKey: backend.apiKey || null,\n timeout: PROBE_TIMEOUT_MS,\n }),\n ).getSettings();\n } catch (error) {\n if (isSdkHttpStatusError(error, 401)) {\n throw new Error(INVALID_BACKEND_API_KEY_ERROR);\n }\n throw error;\n }\n return true;\n}\n\nexport interface BackendHealth {\n /** `null` while the first probe is in flight; then `true` / `false`. */\n isConnected: boolean | null;\n /** Number of consecutive failed probes since the last success. */\n consecutiveFailures: number;\n /** Last error message captured from a failed probe, if any. */\n lastError: string | null;\n /**\n * `true` once `consecutiveFailures` reaches the cap. While disabled,\n * ordinary background polling stops and survives a page refresh in\n * that state.\n */\n disabled: boolean;\n}\n\nexport interface UseBackendsHealthOptions {\n /**\n * Re-probe disabled backends once when the hook mounts. Used by the\n * Manage Backends modal so a recovered backend can clear its stale\n * persisted error state without forcing the user to edit the config.\n */\n probeDisabledOnce?: boolean;\n}\n\n/**\n * Poll every backend in `backends` once every 10s and report a simple\n * connected / disconnected verdict per backend id.\n *\n * The query key includes `host` and `apiKey` so editing a backend's\n * connection details re-keys the query and triggers an immediate\n * refetch instead of waiting for the next tick.\n *\n * After `MAX_CONSECUTIVE_FAILURES` failures in a row, ordinary polling\n * stops for that backend until the user updates its host / apiKey.\n * Callers can still opt into a one-shot recheck for disabled backends\n * (for example when the user explicitly opens Manage Backends). The\n * failure count and last error live in localStorage so a page refresh\n * does not silently re-arm polling against a backend that's known to\n * be unreachable.\n */\nexport function useBackendsHealth(\n backends: Backend[],\n options: UseBackendsHealthOptions = {},\n): Record<string, BackendHealth> {\n const { probeDisabledOnce = false } = options;\n const healthMap = React.useSyncExternalStore(\n subscribeBackendHealth,\n getHealthSnapshot,\n getHealthSnapshot,\n );\n\n const results = useQueries({\n queries: backends.map((b) => {\n const isDisabled = healthMap[b.id]?.disabled === true;\n const shouldProbe = !isDisabled || probeDisabledOnce;\n return {\n queryKey: [\n \"backend-health\",\n b.id,\n b.kind,\n b.host,\n b.apiKey ?? \"\",\n ] as const,\n queryFn: async () => {\n try {\n const result = await probeBackend(b);\n recordBackendSuccess(b.id);\n return result;\n } catch (err) {\n recordBackendFailure(b.id, err);\n throw err;\n }\n },\n enabled: shouldProbe,\n refetchInterval: isDisabled ? (false as const) : REFRESH_INTERVAL_MS,\n refetchIntervalInBackground: false,\n refetchOnMount: isDisabled && probeDisabledOnce ? \"always\" : true,\n refetchOnReconnect: !isDisabled,\n refetchOnWindowFocus: !isDisabled,\n retry: false,\n // Keep the previous verdict visible while the next probe is in\n // flight so the indicator doesn't flicker on routine polling.\n staleTime: isDisabled ? 0 : REFRESH_INTERVAL_MS,\n meta: { disableToast: true },\n };\n }),\n });\n\n const out: Record<string, BackendHealth> = {};\n backends.forEach((b, i) => {\n const r = results[i];\n const entry = healthMap[b.id];\n const disabled = entry?.disabled === true;\n const consecutiveFailures = entry?.consecutiveFailures ?? 0;\n const lastError = entry?.lastError ?? null;\n\n let isConnected: boolean | null;\n if (disabled) {\n // Polling stopped after hitting the cap — treat as disconnected\n // so existing consumers (dot, badge) render red without needing\n // to know about the new fields.\n isConnected = false;\n } else if (r.isSuccess) isConnected = true;\n else if (r.isError) isConnected = false;\n else isConnected = null;\n\n out[b.id] = { isConnected, consecutiveFailures, lastError, disabled };\n });\n return out;\n}\n\nexport { MAX_CONSECUTIVE_FAILURES };\n"],"mappings":";;;;;;;;AAeA,IAAM,IAAsB,KACtB,IAAmB,KACZ,IAAgC;AAE7C,SAAgB,EACd,GACS;AACT,QAAO,MAAU;;AAmBnB,eAAe,EAAa,GAAiC;AAC3D,KAAI,EAAQ,SAAS,QAEnB,QADA,MAAM,EAAsB,EAAQ,EAC7B;AAGT,KAAI;AACF,QAAM,IAAI,EACR,EAA4B;GAC1B,MAAM,EAAQ;GACd,eAAe,EAAQ,UAAU;GACjC,SAAS;GACV,CAAC,CACH,CAAC,aAAa;UACR,GAAO;AAId,QAHI,EAAqB,GAAO,IAAI,GACxB,MAAM,EAA8B,GAE1C;;AAER,QAAO;;AA2CT,SAAgB,EACd,GACA,IAAoC,EAAE,EACP;CAC/B,IAAM,EAAE,uBAAoB,OAAU,GAChC,IAAY,EAAM,qBACtB,GACA,GACA,EACD,EAEK,IAAU,EAAW,EACzB,SAAS,EAAS,KAAK,MAAM;EAC3B,IAAM,IAAa,EAAU,EAAE,KAAK,aAAa,IAC3C,IAAc,CAAC,KAAc;AACnC,SAAO;GACL,UAAU;IACR;IACA,EAAE;IACF,EAAE;IACF,EAAE;IACF,EAAE,UAAU;IACb;GACD,SAAS,YAAY;AACnB,QAAI;KACF,IAAM,IAAS,MAAM,EAAa,EAAE;AAEpC,YADA,EAAqB,EAAE,GAAG,EACnB;aACA,GAAK;AAEZ,WADA,EAAqB,EAAE,IAAI,EAAI,EACzB;;;GAGV,SAAS;GACT,iBAAiB,IAAc,KAAkB;GACjD,6BAA6B;GAC7B,gBAAgB,KAAc,IAAoB,WAAW;GAC7D,oBAAoB,CAAC;GACrB,sBAAsB,CAAC;GACvB,OAAO;GAGP,WAAW,IAAa,IAAI;GAC5B,MAAM,EAAE,cAAc,IAAM;GAC7B;GACD,EACH,CAAC,EAEI,IAAqC,EAAE;AAoB7C,QAnBA,EAAS,SAAS,GAAG,MAAM;EACzB,IAAM,IAAI,EAAQ,IACZ,IAAQ,EAAU,EAAE,KACpB,IAAW,GAAO,aAAa,IAC/B,IAAsB,GAAO,uBAAuB,GACpD,IAAY,GAAO,aAAa,MAElC;AAUJ,EATA,AAOK,IAPD,IAIY,KACL,EAAE,YAAyB,KAC7B,EAAE,UAAuB,KACf,MAEnB,EAAI,EAAE,MAAM;GAAE;GAAa;GAAqB;GAAW;GAAU;GACrE,EACK"}
1
+ {"version":3,"file":"use-backends-health.js","names":[],"sources":["../../../src/hooks/query/use-backends-health.ts"],"sourcesContent":["import React from \"react\";\nimport { useQueries } from \"@tanstack/react-query\";\nimport {\n ServerClient,\n SettingsClient,\n} from \"@openhands/typescript-client/clients\";\nimport { getCurrentCloudApiKey } from \"#/api/cloud/organization-service.api\";\nimport {\n assertAgentServerVersionIsSupported,\n isSdkHttpStatusError,\n} from \"#/api/agent-server-compatibility\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport {\n getHealthSnapshot,\n recordBackendFailure,\n recordBackendSuccess,\n subscribeBackendHealth,\n} from \"#/api/backend-registry/health-store\";\nimport { MAX_CONSECUTIVE_FAILURES } from \"#/api/backend-registry/health-storage\";\n\nconst REFRESH_INTERVAL_MS = 10000;\nconst PROBE_TIMEOUT_MS = 4000;\nexport const INVALID_BACKEND_API_KEY_ERROR = \"Invalid API key\";\n\nexport function isInvalidBackendApiKeyHealthError(\n error: string | null | undefined,\n): boolean {\n return error === INVALID_BACKEND_API_KEY_ERROR;\n}\n\n/**\n * Probe a single backend for connectivity. The probe path differs by\n * backend kind:\n *\n * - Local agent-server: GET `/api/settings`, then `/server_info` via the\n * typescript-client. The settings call validates the configured session\n * API key; the server info call validates the version compatibility floor.\n * - Cloud: GET `/api/keys/current` via the bundled local\n * agent-server's `/api/cloud-proxy`. That endpoint is lightweight,\n * requires auth, and `getCurrentCloudApiKey` already absorbs the\n * legacy-key 400 fallback so we treat that as \"connected\" too.\n * Any other failure (network, 401, 5xx, …) means the backend is\n * not reachable / not usable from the GUI.\n *\n * Throws on failure so React Query marks the query as errored — the\n * dropdown reads `isSuccess` to flip the indicator green.\n */\nasync function probeBackend(backend: Backend): Promise<true> {\n if (backend.kind === \"cloud\") {\n await getCurrentCloudApiKey(backend);\n return true;\n }\n\n try {\n const clientOptions = getAgentServerClientOptions({\n host: backend.host,\n sessionApiKey: backend.apiKey || null,\n timeout: PROBE_TIMEOUT_MS,\n });\n\n await new SettingsClient(clientOptions).getSettings();\n const serverInfo = await new ServerClient(clientOptions).getServerInfo();\n assertAgentServerVersionIsSupported(serverInfo);\n } catch (error) {\n if (isSdkHttpStatusError(error, 401)) {\n throw new Error(INVALID_BACKEND_API_KEY_ERROR);\n }\n throw error;\n }\n return true;\n}\n\nexport interface BackendHealth {\n /** `null` while the first probe is in flight; then `true` / `false`. */\n isConnected: boolean | null;\n /** Number of consecutive failed probes since the last success. */\n consecutiveFailures: number;\n /** Last error message captured from a failed probe, if any. */\n lastError: string | null;\n /**\n * `true` once `consecutiveFailures` reaches the cap. While disabled,\n * ordinary background polling stops and survives a page refresh in\n * that state.\n */\n disabled: boolean;\n}\n\nexport interface UseBackendsHealthOptions {\n /**\n * Re-probe disabled backends once when the hook mounts. Used by the\n * Manage Backends modal so a recovered backend can clear its stale\n * persisted error state without forcing the user to edit the config.\n */\n probeDisabledOnce?: boolean;\n}\n\n/**\n * Poll every backend in `backends` once every 10s and report a simple\n * connected / disconnected verdict per backend id.\n *\n * The query key includes `host` and `apiKey` so editing a backend's\n * connection details re-keys the query and triggers an immediate\n * refetch instead of waiting for the next tick.\n *\n * After `MAX_CONSECUTIVE_FAILURES` failures in a row, ordinary polling\n * stops for that backend until the user updates its host / apiKey.\n * Callers can still opt into a one-shot recheck for disabled backends\n * (for example when the user explicitly opens Manage Backends). The\n * failure count and last error live in localStorage so a page refresh\n * does not silently re-arm polling against a backend that's known to\n * be unreachable.\n */\nexport function useBackendsHealth(\n backends: Backend[],\n options: UseBackendsHealthOptions = {},\n): Record<string, BackendHealth> {\n const { probeDisabledOnce = false } = options;\n const healthMap = React.useSyncExternalStore(\n subscribeBackendHealth,\n getHealthSnapshot,\n getHealthSnapshot,\n );\n\n const results = useQueries({\n queries: backends.map((b) => {\n const isDisabled = healthMap[b.id]?.disabled === true;\n const shouldProbe = !isDisabled || probeDisabledOnce;\n return {\n queryKey: [\n \"backend-health\",\n b.id,\n b.kind,\n b.host,\n b.apiKey ?? \"\",\n ] as const,\n queryFn: async () => {\n try {\n const result = await probeBackend(b);\n recordBackendSuccess(b.id);\n return result;\n } catch (err) {\n recordBackendFailure(b.id, err);\n throw err;\n }\n },\n enabled: shouldProbe,\n refetchInterval: isDisabled ? (false as const) : REFRESH_INTERVAL_MS,\n refetchIntervalInBackground: false,\n refetchOnMount: isDisabled && probeDisabledOnce ? \"always\" : true,\n refetchOnReconnect: !isDisabled,\n refetchOnWindowFocus: !isDisabled,\n retry: false,\n // Keep the previous verdict visible while the next probe is in\n // flight so the indicator doesn't flicker on routine polling.\n staleTime: isDisabled ? 0 : REFRESH_INTERVAL_MS,\n meta: { disableToast: true },\n };\n }),\n });\n\n const out: Record<string, BackendHealth> = {};\n backends.forEach((b, i) => {\n const r = results[i];\n const entry = healthMap[b.id];\n const disabled = entry?.disabled === true;\n const consecutiveFailures = entry?.consecutiveFailures ?? 0;\n const lastError = entry?.lastError ?? null;\n\n let isConnected: boolean | null;\n if (disabled) {\n // Polling stopped after hitting the cap — treat as disconnected\n // so existing consumers (dot, badge) render red without needing\n // to know about the new fields.\n isConnected = false;\n } else if (r.isSuccess) isConnected = true;\n else if (r.isError) isConnected = false;\n else isConnected = null;\n\n out[b.id] = { isConnected, consecutiveFailures, lastError, disabled };\n });\n return out;\n}\n\nexport { MAX_CONSECUTIVE_FAILURES };\n"],"mappings":";;;;;;;;;AAqBA,IAAM,IAAsB,KACtB,IAAmB,KACZ,IAAgC;AAE7C,SAAgB,EACd,GACS;AACT,QAAO,MAAU;;AAoBnB,eAAe,EAAa,GAAiC;AAC3D,KAAI,EAAQ,SAAS,QAEnB,QADA,MAAM,EAAsB,EAAQ,EAC7B;AAGT,KAAI;EACF,IAAM,IAAgB,EAA4B;GAChD,MAAM,EAAQ;GACd,eAAe,EAAQ,UAAU;GACjC,SAAS;GACV,CAAC;AAIF,EAFA,MAAM,IAAI,EAAe,EAAc,CAAC,aAAa,EAErD,EAAoC,MADX,IAAI,EAAa,EAAc,CAAC,eAAe,CACzB;UACxC,GAAO;AAId,QAHI,EAAqB,GAAO,IAAI,GACxB,MAAM,EAA8B,GAE1C;;AAER,QAAO;;AA2CT,SAAgB,EACd,GACA,IAAoC,EAAE,EACP;CAC/B,IAAM,EAAE,uBAAoB,OAAU,GAChC,IAAY,EAAM,qBACtB,GACA,GACA,EACD,EAEK,IAAU,EAAW,EACzB,SAAS,EAAS,KAAK,MAAM;EAC3B,IAAM,IAAa,EAAU,EAAE,KAAK,aAAa,IAC3C,IAAc,CAAC,KAAc;AACnC,SAAO;GACL,UAAU;IACR;IACA,EAAE;IACF,EAAE;IACF,EAAE;IACF,EAAE,UAAU;IACb;GACD,SAAS,YAAY;AACnB,QAAI;KACF,IAAM,IAAS,MAAM,EAAa,EAAE;AAEpC,YADA,EAAqB,EAAE,GAAG,EACnB;aACA,GAAK;AAEZ,WADA,EAAqB,EAAE,IAAI,EAAI,EACzB;;;GAGV,SAAS;GACT,iBAAiB,IAAc,KAAkB;GACjD,6BAA6B;GAC7B,gBAAgB,KAAc,IAAoB,WAAW;GAC7D,oBAAoB,CAAC;GACrB,sBAAsB,CAAC;GACvB,OAAO;GAGP,WAAW,IAAa,IAAI;GAC5B,MAAM,EAAE,cAAc,IAAM;GAC7B;GACD,EACH,CAAC,EAEI,IAAqC,EAAE;AAoB7C,QAnBA,EAAS,SAAS,GAAG,MAAM;EACzB,IAAM,IAAI,EAAQ,IACZ,IAAQ,EAAU,EAAE,KACpB,IAAW,GAAO,aAAa,IAC/B,IAAsB,GAAO,uBAAuB,GACpD,IAAY,GAAO,aAAa,MAElC;AAUJ,EATA,AAOK,IAPD,IAIY,KACL,EAAE,YAAyB,KAC7B,EAAE,UAAuB,KACf,MAEnB,EAAI,EAAE,MAAM;GAAE;GAAa;GAAqB;GAAW;GAAU;GACrE,EACK"}
@@ -0,0 +1,2 @@
1
+ require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`./query-keys.cjs`),t=require(`../../node_modules/@tanstack/react-query/build/modern/useQuery.cjs`),n=require(`../../api/llm-subscription-service.cjs`);function r({enabled:r=!0}={}){return t.useQuery({queryKey:e.LLM_SUBSCRIPTION_QUERY_KEYS.openaiModels,queryFn:n.default.getOpenAIModels,enabled:r,retry:!1,refetchOnWindowFocus:!1,staleTime:1e3*60*5,meta:{disableToast:!0}})}exports.useOpenAISubscriptionModels=r;
2
+ //# sourceMappingURL=use-llm-subscription-models.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-llm-subscription-models.cjs","names":[],"sources":["../../../src/hooks/query/use-llm-subscription-models.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport LLMSubscriptionService from \"#/api/llm-subscription-service\";\nimport { LLM_SUBSCRIPTION_QUERY_KEYS } from \"#/hooks/query/query-keys\";\n\nexport function useOpenAISubscriptionModels({\n enabled = true,\n}: { enabled?: boolean } = {}) {\n return useQuery({\n queryKey: LLM_SUBSCRIPTION_QUERY_KEYS.openaiModels,\n queryFn: LLMSubscriptionService.getOpenAIModels,\n enabled,\n retry: false,\n refetchOnWindowFocus: false,\n staleTime: 1000 * 60 * 5,\n meta: {\n disableToast: true,\n },\n });\n}\n"],"mappings":"wNAIA,SAAgB,EAA4B,CAC1C,UAAU,IACe,EAAE,CAAE,CAC7B,OAAO,EAAA,SAAS,CACd,SAAU,EAAA,4BAA4B,aACtC,QAAS,EAAA,QAAuB,gBAChC,UACA,MAAO,GACP,qBAAsB,GACtB,UAAW,IAAO,GAAK,EACvB,KAAM,CACJ,aAAc,GACf,CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function useOpenAISubscriptionModels({ enabled, }?: {
2
+ enabled?: boolean;
3
+ }): import("@tanstack/react-query").UseQueryResult<string[], import("axios").AxiosError<unknown, any>>;
@@ -0,0 +1,19 @@
1
+ import { LLM_SUBSCRIPTION_QUERY_KEYS as e } from "./query-keys.js";
2
+ import { useQuery as t } from "../../node_modules/@tanstack/react-query/build/modern/useQuery.js";
3
+ import n from "../../api/llm-subscription-service.js";
4
+ //#region src/hooks/query/use-llm-subscription-models.ts
5
+ function r({ enabled: r = !0 } = {}) {
6
+ return t({
7
+ queryKey: e.openaiModels,
8
+ queryFn: n.getOpenAIModels,
9
+ enabled: r,
10
+ retry: !1,
11
+ refetchOnWindowFocus: !1,
12
+ staleTime: 1e3 * 60 * 5,
13
+ meta: { disableToast: !0 }
14
+ });
15
+ }
16
+ //#endregion
17
+ export { r as useOpenAISubscriptionModels };
18
+
19
+ //# sourceMappingURL=use-llm-subscription-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-llm-subscription-models.js","names":[],"sources":["../../../src/hooks/query/use-llm-subscription-models.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport LLMSubscriptionService from \"#/api/llm-subscription-service\";\nimport { LLM_SUBSCRIPTION_QUERY_KEYS } from \"#/hooks/query/query-keys\";\n\nexport function useOpenAISubscriptionModels({\n enabled = true,\n}: { enabled?: boolean } = {}) {\n return useQuery({\n queryKey: LLM_SUBSCRIPTION_QUERY_KEYS.openaiModels,\n queryFn: LLMSubscriptionService.getOpenAIModels,\n enabled,\n retry: false,\n refetchOnWindowFocus: false,\n staleTime: 1000 * 60 * 5,\n meta: {\n disableToast: true,\n },\n });\n}\n"],"mappings":";;;;AAIA,SAAgB,EAA4B,EAC1C,aAAU,OACe,EAAE,EAAE;AAC7B,QAAO,EAAS;EACd,UAAU,EAA4B;EACtC,SAAS,EAAuB;EAChC;EACA,OAAO;EACP,sBAAsB;EACtB,WAAW,MAAO,KAAK;EACvB,MAAM,EACJ,cAAc,IACf;EACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`./query-keys.cjs`),t=require(`../../node_modules/@tanstack/react-query/build/modern/useQuery.cjs`),n=require(`../../api/llm-subscription-service.cjs`);function r({enabled:r=!0}={}){return t.useQuery({queryKey:e.LLM_SUBSCRIPTION_QUERY_KEYS.openaiStatus,queryFn:n.default.getOpenAIStatus,enabled:r,retry:!1,refetchOnWindowFocus:!1,staleTime:1e3*60*5,meta:{disableToast:!0}})}exports.useOpenAISubscriptionStatus=r;
2
+ //# sourceMappingURL=use-llm-subscription-status.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-llm-subscription-status.cjs","names":[],"sources":["../../../src/hooks/query/use-llm-subscription-status.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport LLMSubscriptionService from \"#/api/llm-subscription-service\";\nimport { LLM_SUBSCRIPTION_QUERY_KEYS } from \"#/hooks/query/query-keys\";\n\nexport function useOpenAISubscriptionStatus({\n enabled = true,\n}: { enabled?: boolean } = {}) {\n return useQuery({\n queryKey: LLM_SUBSCRIPTION_QUERY_KEYS.openaiStatus,\n queryFn: LLMSubscriptionService.getOpenAIStatus,\n enabled,\n retry: false,\n refetchOnWindowFocus: false,\n staleTime: 1000 * 60 * 5,\n meta: {\n disableToast: true,\n },\n });\n}\n"],"mappings":"wNAIA,SAAgB,EAA4B,CAC1C,UAAU,IACe,EAAE,CAAE,CAC7B,OAAO,EAAA,SAAS,CACd,SAAU,EAAA,4BAA4B,aACtC,QAAS,EAAA,QAAuB,gBAChC,UACA,MAAO,GACP,qBAAsB,GACtB,UAAW,IAAO,GAAK,EACvB,KAAM,CACJ,aAAc,GACf,CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function useOpenAISubscriptionStatus({ enabled, }?: {
2
+ enabled?: boolean;
3
+ }): import("@tanstack/react-query").UseQueryResult<import("#/api/llm-subscription-service").LLMSubscriptionStatus, import("axios").AxiosError<unknown, any>>;
@@ -0,0 +1,19 @@
1
+ import { LLM_SUBSCRIPTION_QUERY_KEYS as e } from "./query-keys.js";
2
+ import { useQuery as t } from "../../node_modules/@tanstack/react-query/build/modern/useQuery.js";
3
+ import n from "../../api/llm-subscription-service.js";
4
+ //#region src/hooks/query/use-llm-subscription-status.ts
5
+ function r({ enabled: r = !0 } = {}) {
6
+ return t({
7
+ queryKey: e.openaiStatus,
8
+ queryFn: n.getOpenAIStatus,
9
+ enabled: r,
10
+ retry: !1,
11
+ refetchOnWindowFocus: !1,
12
+ staleTime: 1e3 * 60 * 5,
13
+ meta: { disableToast: !0 }
14
+ });
15
+ }
16
+ //#endregion
17
+ export { r as useOpenAISubscriptionStatus };
18
+
19
+ //# sourceMappingURL=use-llm-subscription-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-llm-subscription-status.js","names":[],"sources":["../../../src/hooks/query/use-llm-subscription-status.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport LLMSubscriptionService from \"#/api/llm-subscription-service\";\nimport { LLM_SUBSCRIPTION_QUERY_KEYS } from \"#/hooks/query/query-keys\";\n\nexport function useOpenAISubscriptionStatus({\n enabled = true,\n}: { enabled?: boolean } = {}) {\n return useQuery({\n queryKey: LLM_SUBSCRIPTION_QUERY_KEYS.openaiStatus,\n queryFn: LLMSubscriptionService.getOpenAIStatus,\n enabled,\n retry: false,\n refetchOnWindowFocus: false,\n staleTime: 1000 * 60 * 5,\n meta: {\n disableToast: true,\n },\n });\n}\n"],"mappings":";;;;AAIA,SAAgB,EAA4B,EAC1C,aAAU,OACe,EAAE,EAAE;AAC7B,QAAO,EAAS;EACd,UAAU,EAA4B;EACtC,SAAS,EAAuB;EAChC;EACA,OAAO;EACP,sBAAsB;EACtB,WAAW,MAAO,KAAK;EACvB,MAAM,EACJ,cAAc,IACf;EACF,CAAC"}
@@ -1,2 +1,2 @@
1
- const e=require(`../_virtual/_rolldown/runtime.cjs`);let t=require(`react`);t=e.__toESM(t,1);var n=(e,n)=>{let[r,i]=t.default.useState(!1),[a,o]=t.default.useState(null),[s,c]=t.default.useState([]),[l,u]=t.default.useState(null),[d,f]=t.default.useState(!1),p=t.default.useRef(null),m=t.default.useRef(0),h=t.default.useRef(null),g=t.default.useRef(!0),_=t.default.useRef(new WeakSet),v=t.default.useRef(n);t.default.useEffect(()=>{v.current=n},[n]);let y=t.default.useCallback(()=>{let t=e;if(v.current?.queryParams){let n=Object.entries(v.current.queryParams).reduce((e,[t,n])=>(e[t]=String(n),e),{});t=`${e}?${new URLSearchParams(n).toString()}`}let n=new WebSocket(t);p.current=n,_.current.add(n),n.onopen=e=>{i(!0),u(null),f(!1),m.current=0,v.current?.onOpen?.(e)},n.onmessage=e=>{o(e.data),c(t=>[...t,e.data]),v.current?.onMessage?.(e)},n.onclose=e=>{let t=_.current.has(n);i(!1),e.code!==1e3&&(u(Error(`WebSocket closed with code ${e.code}: ${e.reason||`Connection closed unexpectedly`}`)),t&&v.current?.onError?.(e)),v.current?.onClose?.(e);let r=v.current?.reconnect?.enabled??!1,a=v.current?.reconnect?.maxAttempts??1/0;r&&t&&g.current&&m.current<a?(f(!0),m.current+=1,h.current=setTimeout(()=>{y()},3e3)):f(!1)},n.onerror=e=>{i(!1),v.current?.onError?.(e)}},[e]);t.default.useEffect(()=>(g.current=!0,m.current=0,e&&e.trim()!==``&&y(),()=>{if(g.current=!1,h.current&&=(clearTimeout(h.current),null),p.current){let{readyState:e}=p.current;_.current.delete(p.current),(e===WebSocket.CONNECTING||e===WebSocket.OPEN)&&p.current.close(),p.current=null}}),[e,y]);let b=t.default.useCallback(e=>{p.current?.readyState===WebSocket.OPEN&&p.current.send(e)},[]),x=t.default.useCallback(()=>{g.current=!1,f(!1),h.current&&=(clearTimeout(h.current),null),p.current&&(_.current.delete(p.current),p.current.close())},[]),S=t.default.useCallback(()=>{if(g.current=!0,m.current=0,f(!0),u(null),h.current&&=(clearTimeout(h.current),null),p.current){let e=p.current;_.current.delete(e),p.current=null,(e.readyState===WebSocket.CONNECTING||e.readyState===WebSocket.OPEN)&&e.close()}y()},[y]);return{isConnected:r,lastMessage:a,messages:s,error:l,socket:p.current,sendMessage:b,isReconnecting:d,attemptCount:m.current,disconnect:x,reconnect:S}};exports.useWebSocket=n;
1
+ const e=require(`../_virtual/_rolldown/runtime.cjs`);let t=require(`react`);t=e.__toESM(t,1);var n=(e,n)=>{let[r,i]=t.default.useState(!1),[a,o]=t.default.useState(null),[s,c]=t.default.useState(null),[l,u]=t.default.useState(!1),d=t.default.useRef(null),f=t.default.useRef(0),p=t.default.useRef(null),m=t.default.useRef(!0),h=t.default.useRef(new WeakSet),g=t.default.useRef(n);t.default.useEffect(()=>{g.current=n},[n]);let _=t.default.useCallback(()=>{let t=e;if(g.current?.queryParams){let n=Object.entries(g.current.queryParams).reduce((e,[t,n])=>(e[t]=String(n),e),{});t=`${e}?${new URLSearchParams(n).toString()}`}let n=new WebSocket(t);d.current=n,h.current.add(n),n.onopen=e=>{i(!0),c(null),u(!1),f.current=0,g.current?.onOpen?.(e)},n.onmessage=e=>{o(e.data),g.current?.onMessage?.(e)},n.onclose=e=>{let t=h.current.has(n);i(!1),e.code!==1e3&&(c(Error(`WebSocket closed with code ${e.code}: ${e.reason||`Connection closed unexpectedly`}`)),t&&g.current?.onError?.(e)),g.current?.onClose?.(e);let r=g.current?.reconnect?.enabled??!1,a=g.current?.reconnect?.maxAttempts??1/0;r&&t&&m.current&&f.current<a?(u(!0),f.current+=1,p.current=setTimeout(()=>{_()},3e3)):u(!1)},n.onerror=e=>{i(!1),g.current?.onError?.(e)}},[e]);t.default.useEffect(()=>(m.current=!0,f.current=0,e&&e.trim()!==``&&_(),()=>{if(m.current=!1,p.current&&=(clearTimeout(p.current),null),d.current){let{readyState:e}=d.current;h.current.delete(d.current),(e===WebSocket.CONNECTING||e===WebSocket.OPEN)&&d.current.close(),d.current=null}}),[e,_]);let v=t.default.useCallback(e=>{d.current?.readyState===WebSocket.OPEN&&d.current.send(e)},[]),y=t.default.useCallback(()=>{m.current=!1,u(!1),p.current&&=(clearTimeout(p.current),null),d.current&&(h.current.delete(d.current),d.current.close())},[]),b=t.default.useCallback(()=>{if(m.current=!0,f.current=0,u(!0),c(null),p.current&&=(clearTimeout(p.current),null),d.current){let e=d.current;h.current.delete(e),d.current=null,(e.readyState===WebSocket.CONNECTING||e.readyState===WebSocket.OPEN)&&e.close()}_()},[_]);return{isConnected:r,lastMessage:a,error:s,socket:d.current,sendMessage:v,isReconnecting:l,attemptCount:f.current,disconnect:y,reconnect:b}};exports.useWebSocket=n;
2
2
  //# sourceMappingURL=use-websocket.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-websocket.cjs","names":[],"sources":["../../src/hooks/use-websocket.ts"],"sourcesContent":["import React from \"react\";\n\nexport interface WebSocketHookOptions {\n queryParams?: Record<string, string | boolean>;\n onOpen?: (event: Event) => void;\n onClose?: (event: CloseEvent) => void;\n onMessage?: (event: MessageEvent) => void;\n onError?: (event: Event) => void;\n reconnect?: {\n enabled?: boolean;\n maxAttempts?: number;\n };\n}\n\nexport const useWebSocket = <T = string>(\n url: string,\n options?: WebSocketHookOptions,\n) => {\n const [isConnected, setIsConnected] = React.useState(false);\n const [lastMessage, setLastMessage] = React.useState<T | null>(null);\n const [messages, setMessages] = React.useState<T[]>([]);\n const [error, setError] = React.useState<Error | null>(null);\n const [isReconnecting, setIsReconnecting] = React.useState(false);\n const wsRef = React.useRef<WebSocket | null>(null);\n const attemptCountRef = React.useRef(0);\n const reconnectTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);\n const shouldReconnectRef = React.useRef(true); // Only set to false by disconnect()\n // Track which WebSocket instances are allowed to reconnect using a WeakSet\n const allowedToReconnectRef = React.useRef<WeakSet<WebSocket>>(new WeakSet());\n\n // Store options in a ref to avoid reconnecting when callbacks change\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const connectWebSocket = React.useCallback(() => {\n // Build URL with query parameters if provided\n let wsUrl = url;\n if (optionsRef.current?.queryParams) {\n const stringParams = Object.entries(\n optionsRef.current.queryParams,\n ).reduce(\n (acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n },\n {} as Record<string, string>,\n );\n const params = new URLSearchParams(stringParams);\n wsUrl = `${url}?${params.toString()}`;\n }\n\n const ws = new WebSocket(wsUrl);\n wsRef.current = ws;\n // Mark this WebSocket instance as allowed to reconnect\n allowedToReconnectRef.current.add(ws);\n\n ws.onopen = (event) => {\n setIsConnected(true);\n setError(null); // Clear any previous errors\n setIsReconnecting(false);\n attemptCountRef.current = 0; // Reset attempt count on successful connection\n optionsRef.current?.onOpen?.(event);\n };\n\n ws.onmessage = (event) => {\n setLastMessage(event.data);\n setMessages((prev) => [...prev, event.data]);\n optionsRef.current?.onMessage?.(event);\n };\n\n ws.onclose = (event) => {\n // Check if this specific WebSocket instance is allowed to reconnect\n const canReconnect = allowedToReconnectRef.current.has(ws);\n setIsConnected(false);\n // If the connection closes with an error code, treat it as an error\n if (event.code !== 1000) {\n // 1000 is normal closure\n setError(\n new Error(\n `WebSocket closed with code ${event.code}: ${event.reason || \"Connection closed unexpectedly\"}`,\n ),\n );\n // Also call onError handler for error closures (only if allowed to reconnect)\n if (canReconnect) {\n optionsRef.current?.onError?.(event);\n }\n }\n optionsRef.current?.onClose?.(event);\n\n // Attempt reconnection if enabled and allowed\n // IMPORTANT: Only reconnect if this specific instance is allowed to reconnect\n const reconnectEnabled = optionsRef.current?.reconnect?.enabled ?? false;\n const maxAttempts =\n optionsRef.current?.reconnect?.maxAttempts ?? Infinity;\n\n if (\n reconnectEnabled &&\n canReconnect &&\n shouldReconnectRef.current &&\n attemptCountRef.current < maxAttempts\n ) {\n setIsReconnecting(true);\n attemptCountRef.current += 1;\n\n reconnectTimeoutRef.current = setTimeout(() => {\n connectWebSocket();\n }, 3000); // 3 second delay\n } else {\n setIsReconnecting(false);\n }\n };\n\n ws.onerror = (event) => {\n setIsConnected(false);\n optionsRef.current?.onError?.(event);\n };\n }, [url]);\n\n React.useEffect(() => {\n // Reset shouldReconnect flag and attempt count when creating a new connection\n shouldReconnectRef.current = true;\n attemptCountRef.current = 0;\n\n // Only attempt connection if we have a valid URL\n if (url && url.trim() !== \"\") {\n connectWebSocket();\n }\n\n return () => {\n // Disable reconnection on unmount to prevent reconnection attempts\n // This must be set BEFORE closing the socket, so the onclose handler sees it\n shouldReconnectRef.current = false;\n // Clear any pending reconnection timeouts\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n // Close the WebSocket connection\n if (wsRef.current) {\n const { readyState } = wsRef.current;\n // Remove this WebSocket from the allowed list BEFORE closing\n // so its onclose handler won't try to reconnect\n allowedToReconnectRef.current.delete(wsRef.current);\n // Only close if not already closed/closing\n if (\n readyState === WebSocket.CONNECTING ||\n readyState === WebSocket.OPEN\n ) {\n wsRef.current.close();\n }\n wsRef.current = null;\n }\n };\n }, [url, connectWebSocket]);\n\n const sendMessage = React.useCallback(\n (data: string | Blob | BufferSource) => {\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n wsRef.current.send(data);\n }\n },\n [],\n );\n\n const disconnect = React.useCallback(() => {\n shouldReconnectRef.current = false;\n setIsReconnecting(false);\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n if (wsRef.current) {\n // Remove from allowed list before closing\n allowedToReconnectRef.current.delete(wsRef.current);\n wsRef.current.close();\n }\n }, []);\n\n const reconnect = React.useCallback(() => {\n shouldReconnectRef.current = true;\n attemptCountRef.current = 0;\n setIsReconnecting(true);\n setError(null);\n\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n\n if (wsRef.current) {\n const ws = wsRef.current;\n allowedToReconnectRef.current.delete(ws);\n wsRef.current = null;\n if (\n ws.readyState === WebSocket.CONNECTING ||\n ws.readyState === WebSocket.OPEN\n ) {\n ws.close();\n }\n }\n\n connectWebSocket();\n }, [connectWebSocket]);\n\n return {\n isConnected,\n lastMessage,\n messages,\n error,\n socket: wsRef.current,\n sendMessage,\n isReconnecting,\n attemptCount: attemptCountRef.current,\n disconnect,\n reconnect,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-websocket.cjs","names":[],"sources":["../../src/hooks/use-websocket.ts"],"sourcesContent":["import React from \"react\";\n\nexport interface WebSocketHookOptions {\n queryParams?: Record<string, string | boolean>;\n onOpen?: (event: Event) => void;\n onClose?: (event: CloseEvent) => void;\n onMessage?: (event: MessageEvent) => void;\n onError?: (event: Event) => void;\n reconnect?: {\n enabled?: boolean;\n maxAttempts?: number;\n };\n}\n\nexport const useWebSocket = <T = string>(\n url: string,\n options?: WebSocketHookOptions,\n) => {\n const [isConnected, setIsConnected] = React.useState(false);\n const [lastMessage, setLastMessage] = React.useState<T | null>(null);\n const [error, setError] = React.useState<Error | null>(null);\n const [isReconnecting, setIsReconnecting] = React.useState(false);\n const wsRef = React.useRef<WebSocket | null>(null);\n const attemptCountRef = React.useRef(0);\n const reconnectTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);\n const shouldReconnectRef = React.useRef(true); // Only set to false by disconnect()\n // Track which WebSocket instances are allowed to reconnect using a WeakSet\n const allowedToReconnectRef = React.useRef<WeakSet<WebSocket>>(new WeakSet());\n\n // Store options in a ref to avoid reconnecting when callbacks change\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const connectWebSocket = React.useCallback(() => {\n // Build URL with query parameters if provided\n let wsUrl = url;\n if (optionsRef.current?.queryParams) {\n const stringParams = Object.entries(\n optionsRef.current.queryParams,\n ).reduce(\n (acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n },\n {} as Record<string, string>,\n );\n const params = new URLSearchParams(stringParams);\n wsUrl = `${url}?${params.toString()}`;\n }\n\n const ws = new WebSocket(wsUrl);\n wsRef.current = ws;\n // Mark this WebSocket instance as allowed to reconnect\n allowedToReconnectRef.current.add(ws);\n\n ws.onopen = (event) => {\n setIsConnected(true);\n setError(null); // Clear any previous errors\n setIsReconnecting(false);\n attemptCountRef.current = 0; // Reset attempt count on successful connection\n optionsRef.current?.onOpen?.(event);\n };\n\n ws.onmessage = (event) => {\n setLastMessage(event.data);\n optionsRef.current?.onMessage?.(event);\n };\n\n ws.onclose = (event) => {\n // Check if this specific WebSocket instance is allowed to reconnect\n const canReconnect = allowedToReconnectRef.current.has(ws);\n setIsConnected(false);\n // If the connection closes with an error code, treat it as an error\n if (event.code !== 1000) {\n // 1000 is normal closure\n setError(\n new Error(\n `WebSocket closed with code ${event.code}: ${event.reason || \"Connection closed unexpectedly\"}`,\n ),\n );\n // Also call onError handler for error closures (only if allowed to reconnect)\n if (canReconnect) {\n optionsRef.current?.onError?.(event);\n }\n }\n optionsRef.current?.onClose?.(event);\n\n // Attempt reconnection if enabled and allowed\n // IMPORTANT: Only reconnect if this specific instance is allowed to reconnect\n const reconnectEnabled = optionsRef.current?.reconnect?.enabled ?? false;\n const maxAttempts =\n optionsRef.current?.reconnect?.maxAttempts ?? Infinity;\n\n if (\n reconnectEnabled &&\n canReconnect &&\n shouldReconnectRef.current &&\n attemptCountRef.current < maxAttempts\n ) {\n setIsReconnecting(true);\n attemptCountRef.current += 1;\n\n reconnectTimeoutRef.current = setTimeout(() => {\n connectWebSocket();\n }, 3000); // 3 second delay\n } else {\n setIsReconnecting(false);\n }\n };\n\n ws.onerror = (event) => {\n setIsConnected(false);\n optionsRef.current?.onError?.(event);\n };\n }, [url]);\n\n React.useEffect(() => {\n // Reset shouldReconnect flag and attempt count when creating a new connection\n shouldReconnectRef.current = true;\n attemptCountRef.current = 0;\n\n // Only attempt connection if we have a valid URL\n if (url && url.trim() !== \"\") {\n connectWebSocket();\n }\n\n return () => {\n // Disable reconnection on unmount to prevent reconnection attempts\n // This must be set BEFORE closing the socket, so the onclose handler sees it\n shouldReconnectRef.current = false;\n // Clear any pending reconnection timeouts\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n // Close the WebSocket connection\n if (wsRef.current) {\n const { readyState } = wsRef.current;\n // Remove this WebSocket from the allowed list BEFORE closing\n // so its onclose handler won't try to reconnect\n allowedToReconnectRef.current.delete(wsRef.current);\n // Only close if not already closed/closing\n if (\n readyState === WebSocket.CONNECTING ||\n readyState === WebSocket.OPEN\n ) {\n wsRef.current.close();\n }\n wsRef.current = null;\n }\n };\n }, [url, connectWebSocket]);\n\n const sendMessage = React.useCallback(\n (data: string | Blob | BufferSource) => {\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n wsRef.current.send(data);\n }\n },\n [],\n );\n\n const disconnect = React.useCallback(() => {\n shouldReconnectRef.current = false;\n setIsReconnecting(false);\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n if (wsRef.current) {\n // Remove from allowed list before closing\n allowedToReconnectRef.current.delete(wsRef.current);\n wsRef.current.close();\n }\n }, []);\n\n const reconnect = React.useCallback(() => {\n shouldReconnectRef.current = true;\n attemptCountRef.current = 0;\n setIsReconnecting(true);\n setError(null);\n\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n\n if (wsRef.current) {\n const ws = wsRef.current;\n allowedToReconnectRef.current.delete(ws);\n wsRef.current = null;\n if (\n ws.readyState === WebSocket.CONNECTING ||\n ws.readyState === WebSocket.OPEN\n ) {\n ws.close();\n }\n }\n\n connectWebSocket();\n }, [connectWebSocket]);\n\n return {\n isConnected,\n lastMessage,\n error,\n socket: wsRef.current,\n sendMessage,\n isReconnecting,\n attemptCount: attemptCountRef.current,\n disconnect,\n reconnect,\n };\n};\n"],"mappings":"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"}
@@ -12,7 +12,6 @@ export interface WebSocketHookOptions {
12
12
  export declare const useWebSocket: <T = string>(url: string, options?: WebSocketHookOptions) => {
13
13
  isConnected: boolean;
14
14
  lastMessage: T | null;
15
- messages: T[];
16
15
  error: Error | null;
17
16
  socket: WebSocket | null;
18
17
  sendMessage: (data: string | Blob | BufferSource) => void;
@@ -1,60 +1,59 @@
1
1
  import e from "react";
2
2
  //#region src/hooks/use-websocket.ts
3
3
  var t = (t, n) => {
4
- let [r, i] = e.useState(!1), [a, o] = e.useState(null), [s, c] = e.useState([]), [l, u] = e.useState(null), [d, f] = e.useState(!1), p = e.useRef(null), m = e.useRef(0), h = e.useRef(null), g = e.useRef(!0), _ = e.useRef(/* @__PURE__ */ new WeakSet()), v = e.useRef(n);
4
+ let [r, i] = e.useState(!1), [a, o] = e.useState(null), [s, c] = e.useState(null), [l, u] = e.useState(!1), d = e.useRef(null), f = e.useRef(0), p = e.useRef(null), m = e.useRef(!0), h = e.useRef(/* @__PURE__ */ new WeakSet()), g = e.useRef(n);
5
5
  e.useEffect(() => {
6
- v.current = n;
6
+ g.current = n;
7
7
  }, [n]);
8
- let y = e.useCallback(() => {
8
+ let _ = e.useCallback(() => {
9
9
  let e = t;
10
- if (v.current?.queryParams) {
11
- let n = Object.entries(v.current.queryParams).reduce((e, [t, n]) => (e[t] = String(n), e), {});
10
+ if (g.current?.queryParams) {
11
+ let n = Object.entries(g.current.queryParams).reduce((e, [t, n]) => (e[t] = String(n), e), {});
12
12
  e = `${t}?${new URLSearchParams(n).toString()}`;
13
13
  }
14
14
  let n = new WebSocket(e);
15
- p.current = n, _.current.add(n), n.onopen = (e) => {
16
- i(!0), u(null), f(!1), m.current = 0, v.current?.onOpen?.(e);
15
+ d.current = n, h.current.add(n), n.onopen = (e) => {
16
+ i(!0), c(null), u(!1), f.current = 0, g.current?.onOpen?.(e);
17
17
  }, n.onmessage = (e) => {
18
- o(e.data), c((t) => [...t, e.data]), v.current?.onMessage?.(e);
18
+ o(e.data), g.current?.onMessage?.(e);
19
19
  }, n.onclose = (e) => {
20
- let t = _.current.has(n);
21
- i(!1), e.code !== 1e3 && (u(/* @__PURE__ */ Error(`WebSocket closed with code ${e.code}: ${e.reason || "Connection closed unexpectedly"}`)), t && v.current?.onError?.(e)), v.current?.onClose?.(e);
22
- let r = v.current?.reconnect?.enabled ?? !1, a = v.current?.reconnect?.maxAttempts ?? Infinity;
23
- r && t && g.current && m.current < a ? (f(!0), m.current += 1, h.current = setTimeout(() => {
24
- y();
25
- }, 3e3)) : f(!1);
20
+ let t = h.current.has(n);
21
+ i(!1), e.code !== 1e3 && (c(/* @__PURE__ */ Error(`WebSocket closed with code ${e.code}: ${e.reason || "Connection closed unexpectedly"}`)), t && g.current?.onError?.(e)), g.current?.onClose?.(e);
22
+ let r = g.current?.reconnect?.enabled ?? !1, a = g.current?.reconnect?.maxAttempts ?? Infinity;
23
+ r && t && m.current && f.current < a ? (u(!0), f.current += 1, p.current = setTimeout(() => {
24
+ _();
25
+ }, 3e3)) : u(!1);
26
26
  }, n.onerror = (e) => {
27
- i(!1), v.current?.onError?.(e);
27
+ i(!1), g.current?.onError?.(e);
28
28
  };
29
29
  }, [t]);
30
- e.useEffect(() => (g.current = !0, m.current = 0, t && t.trim() !== "" && y(), () => {
31
- if (g.current = !1, h.current &&= (clearTimeout(h.current), null), p.current) {
32
- let { readyState: e } = p.current;
33
- _.current.delete(p.current), (e === WebSocket.CONNECTING || e === WebSocket.OPEN) && p.current.close(), p.current = null;
30
+ e.useEffect(() => (m.current = !0, f.current = 0, t && t.trim() !== "" && _(), () => {
31
+ if (m.current = !1, p.current &&= (clearTimeout(p.current), null), d.current) {
32
+ let { readyState: e } = d.current;
33
+ h.current.delete(d.current), (e === WebSocket.CONNECTING || e === WebSocket.OPEN) && d.current.close(), d.current = null;
34
34
  }
35
- }), [t, y]);
36
- let b = e.useCallback((e) => {
37
- p.current?.readyState === WebSocket.OPEN && p.current.send(e);
38
- }, []), x = e.useCallback(() => {
39
- g.current = !1, f(!1), h.current &&= (clearTimeout(h.current), null), p.current && (_.current.delete(p.current), p.current.close());
40
- }, []), S = e.useCallback(() => {
41
- if (g.current = !0, m.current = 0, f(!0), u(null), h.current &&= (clearTimeout(h.current), null), p.current) {
42
- let e = p.current;
43
- _.current.delete(e), p.current = null, (e.readyState === WebSocket.CONNECTING || e.readyState === WebSocket.OPEN) && e.close();
35
+ }), [t, _]);
36
+ let v = e.useCallback((e) => {
37
+ d.current?.readyState === WebSocket.OPEN && d.current.send(e);
38
+ }, []), y = e.useCallback(() => {
39
+ m.current = !1, u(!1), p.current &&= (clearTimeout(p.current), null), d.current && (h.current.delete(d.current), d.current.close());
40
+ }, []), b = e.useCallback(() => {
41
+ if (m.current = !0, f.current = 0, u(!0), c(null), p.current &&= (clearTimeout(p.current), null), d.current) {
42
+ let e = d.current;
43
+ h.current.delete(e), d.current = null, (e.readyState === WebSocket.CONNECTING || e.readyState === WebSocket.OPEN) && e.close();
44
44
  }
45
- y();
46
- }, [y]);
45
+ _();
46
+ }, [_]);
47
47
  return {
48
48
  isConnected: r,
49
49
  lastMessage: a,
50
- messages: s,
51
- error: l,
52
- socket: p.current,
53
- sendMessage: b,
54
- isReconnecting: d,
55
- attemptCount: m.current,
56
- disconnect: x,
57
- reconnect: S
50
+ error: s,
51
+ socket: d.current,
52
+ sendMessage: v,
53
+ isReconnecting: l,
54
+ attemptCount: f.current,
55
+ disconnect: y,
56
+ reconnect: b
58
57
  };
59
58
  };
60
59
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"use-websocket.js","names":[],"sources":["../../src/hooks/use-websocket.ts"],"sourcesContent":["import React from \"react\";\n\nexport interface WebSocketHookOptions {\n queryParams?: Record<string, string | boolean>;\n onOpen?: (event: Event) => void;\n onClose?: (event: CloseEvent) => void;\n onMessage?: (event: MessageEvent) => void;\n onError?: (event: Event) => void;\n reconnect?: {\n enabled?: boolean;\n maxAttempts?: number;\n };\n}\n\nexport const useWebSocket = <T = string>(\n url: string,\n options?: WebSocketHookOptions,\n) => {\n const [isConnected, setIsConnected] = React.useState(false);\n const [lastMessage, setLastMessage] = React.useState<T | null>(null);\n const [messages, setMessages] = React.useState<T[]>([]);\n const [error, setError] = React.useState<Error | null>(null);\n const [isReconnecting, setIsReconnecting] = React.useState(false);\n const wsRef = React.useRef<WebSocket | null>(null);\n const attemptCountRef = React.useRef(0);\n const reconnectTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);\n const shouldReconnectRef = React.useRef(true); // Only set to false by disconnect()\n // Track which WebSocket instances are allowed to reconnect using a WeakSet\n const allowedToReconnectRef = React.useRef<WeakSet<WebSocket>>(new WeakSet());\n\n // Store options in a ref to avoid reconnecting when callbacks change\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const connectWebSocket = React.useCallback(() => {\n // Build URL with query parameters if provided\n let wsUrl = url;\n if (optionsRef.current?.queryParams) {\n const stringParams = Object.entries(\n optionsRef.current.queryParams,\n ).reduce(\n (acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n },\n {} as Record<string, string>,\n );\n const params = new URLSearchParams(stringParams);\n wsUrl = `${url}?${params.toString()}`;\n }\n\n const ws = new WebSocket(wsUrl);\n wsRef.current = ws;\n // Mark this WebSocket instance as allowed to reconnect\n allowedToReconnectRef.current.add(ws);\n\n ws.onopen = (event) => {\n setIsConnected(true);\n setError(null); // Clear any previous errors\n setIsReconnecting(false);\n attemptCountRef.current = 0; // Reset attempt count on successful connection\n optionsRef.current?.onOpen?.(event);\n };\n\n ws.onmessage = (event) => {\n setLastMessage(event.data);\n setMessages((prev) => [...prev, event.data]);\n optionsRef.current?.onMessage?.(event);\n };\n\n ws.onclose = (event) => {\n // Check if this specific WebSocket instance is allowed to reconnect\n const canReconnect = allowedToReconnectRef.current.has(ws);\n setIsConnected(false);\n // If the connection closes with an error code, treat it as an error\n if (event.code !== 1000) {\n // 1000 is normal closure\n setError(\n new Error(\n `WebSocket closed with code ${event.code}: ${event.reason || \"Connection closed unexpectedly\"}`,\n ),\n );\n // Also call onError handler for error closures (only if allowed to reconnect)\n if (canReconnect) {\n optionsRef.current?.onError?.(event);\n }\n }\n optionsRef.current?.onClose?.(event);\n\n // Attempt reconnection if enabled and allowed\n // IMPORTANT: Only reconnect if this specific instance is allowed to reconnect\n const reconnectEnabled = optionsRef.current?.reconnect?.enabled ?? false;\n const maxAttempts =\n optionsRef.current?.reconnect?.maxAttempts ?? Infinity;\n\n if (\n reconnectEnabled &&\n canReconnect &&\n shouldReconnectRef.current &&\n attemptCountRef.current < maxAttempts\n ) {\n setIsReconnecting(true);\n attemptCountRef.current += 1;\n\n reconnectTimeoutRef.current = setTimeout(() => {\n connectWebSocket();\n }, 3000); // 3 second delay\n } else {\n setIsReconnecting(false);\n }\n };\n\n ws.onerror = (event) => {\n setIsConnected(false);\n optionsRef.current?.onError?.(event);\n };\n }, [url]);\n\n React.useEffect(() => {\n // Reset shouldReconnect flag and attempt count when creating a new connection\n shouldReconnectRef.current = true;\n attemptCountRef.current = 0;\n\n // Only attempt connection if we have a valid URL\n if (url && url.trim() !== \"\") {\n connectWebSocket();\n }\n\n return () => {\n // Disable reconnection on unmount to prevent reconnection attempts\n // This must be set BEFORE closing the socket, so the onclose handler sees it\n shouldReconnectRef.current = false;\n // Clear any pending reconnection timeouts\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n // Close the WebSocket connection\n if (wsRef.current) {\n const { readyState } = wsRef.current;\n // Remove this WebSocket from the allowed list BEFORE closing\n // so its onclose handler won't try to reconnect\n allowedToReconnectRef.current.delete(wsRef.current);\n // Only close if not already closed/closing\n if (\n readyState === WebSocket.CONNECTING ||\n readyState === WebSocket.OPEN\n ) {\n wsRef.current.close();\n }\n wsRef.current = null;\n }\n };\n }, [url, connectWebSocket]);\n\n const sendMessage = React.useCallback(\n (data: string | Blob | BufferSource) => {\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n wsRef.current.send(data);\n }\n },\n [],\n );\n\n const disconnect = React.useCallback(() => {\n shouldReconnectRef.current = false;\n setIsReconnecting(false);\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n if (wsRef.current) {\n // Remove from allowed list before closing\n allowedToReconnectRef.current.delete(wsRef.current);\n wsRef.current.close();\n }\n }, []);\n\n const reconnect = React.useCallback(() => {\n shouldReconnectRef.current = true;\n attemptCountRef.current = 0;\n setIsReconnecting(true);\n setError(null);\n\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n\n if (wsRef.current) {\n const ws = wsRef.current;\n allowedToReconnectRef.current.delete(ws);\n wsRef.current = null;\n if (\n ws.readyState === WebSocket.CONNECTING ||\n ws.readyState === WebSocket.OPEN\n ) {\n ws.close();\n }\n }\n\n connectWebSocket();\n }, [connectWebSocket]);\n\n return {\n isConnected,\n lastMessage,\n messages,\n error,\n socket: wsRef.current,\n sendMessage,\n isReconnecting,\n attemptCount: attemptCountRef.current,\n disconnect,\n reconnect,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-websocket.js","names":[],"sources":["../../src/hooks/use-websocket.ts"],"sourcesContent":["import React from \"react\";\n\nexport interface WebSocketHookOptions {\n queryParams?: Record<string, string | boolean>;\n onOpen?: (event: Event) => void;\n onClose?: (event: CloseEvent) => void;\n onMessage?: (event: MessageEvent) => void;\n onError?: (event: Event) => void;\n reconnect?: {\n enabled?: boolean;\n maxAttempts?: number;\n };\n}\n\nexport const useWebSocket = <T = string>(\n url: string,\n options?: WebSocketHookOptions,\n) => {\n const [isConnected, setIsConnected] = React.useState(false);\n const [lastMessage, setLastMessage] = React.useState<T | null>(null);\n const [error, setError] = React.useState<Error | null>(null);\n const [isReconnecting, setIsReconnecting] = React.useState(false);\n const wsRef = React.useRef<WebSocket | null>(null);\n const attemptCountRef = React.useRef(0);\n const reconnectTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);\n const shouldReconnectRef = React.useRef(true); // Only set to false by disconnect()\n // Track which WebSocket instances are allowed to reconnect using a WeakSet\n const allowedToReconnectRef = React.useRef<WeakSet<WebSocket>>(new WeakSet());\n\n // Store options in a ref to avoid reconnecting when callbacks change\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const connectWebSocket = React.useCallback(() => {\n // Build URL with query parameters if provided\n let wsUrl = url;\n if (optionsRef.current?.queryParams) {\n const stringParams = Object.entries(\n optionsRef.current.queryParams,\n ).reduce(\n (acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n },\n {} as Record<string, string>,\n );\n const params = new URLSearchParams(stringParams);\n wsUrl = `${url}?${params.toString()}`;\n }\n\n const ws = new WebSocket(wsUrl);\n wsRef.current = ws;\n // Mark this WebSocket instance as allowed to reconnect\n allowedToReconnectRef.current.add(ws);\n\n ws.onopen = (event) => {\n setIsConnected(true);\n setError(null); // Clear any previous errors\n setIsReconnecting(false);\n attemptCountRef.current = 0; // Reset attempt count on successful connection\n optionsRef.current?.onOpen?.(event);\n };\n\n ws.onmessage = (event) => {\n setLastMessage(event.data);\n optionsRef.current?.onMessage?.(event);\n };\n\n ws.onclose = (event) => {\n // Check if this specific WebSocket instance is allowed to reconnect\n const canReconnect = allowedToReconnectRef.current.has(ws);\n setIsConnected(false);\n // If the connection closes with an error code, treat it as an error\n if (event.code !== 1000) {\n // 1000 is normal closure\n setError(\n new Error(\n `WebSocket closed with code ${event.code}: ${event.reason || \"Connection closed unexpectedly\"}`,\n ),\n );\n // Also call onError handler for error closures (only if allowed to reconnect)\n if (canReconnect) {\n optionsRef.current?.onError?.(event);\n }\n }\n optionsRef.current?.onClose?.(event);\n\n // Attempt reconnection if enabled and allowed\n // IMPORTANT: Only reconnect if this specific instance is allowed to reconnect\n const reconnectEnabled = optionsRef.current?.reconnect?.enabled ?? false;\n const maxAttempts =\n optionsRef.current?.reconnect?.maxAttempts ?? Infinity;\n\n if (\n reconnectEnabled &&\n canReconnect &&\n shouldReconnectRef.current &&\n attemptCountRef.current < maxAttempts\n ) {\n setIsReconnecting(true);\n attemptCountRef.current += 1;\n\n reconnectTimeoutRef.current = setTimeout(() => {\n connectWebSocket();\n }, 3000); // 3 second delay\n } else {\n setIsReconnecting(false);\n }\n };\n\n ws.onerror = (event) => {\n setIsConnected(false);\n optionsRef.current?.onError?.(event);\n };\n }, [url]);\n\n React.useEffect(() => {\n // Reset shouldReconnect flag and attempt count when creating a new connection\n shouldReconnectRef.current = true;\n attemptCountRef.current = 0;\n\n // Only attempt connection if we have a valid URL\n if (url && url.trim() !== \"\") {\n connectWebSocket();\n }\n\n return () => {\n // Disable reconnection on unmount to prevent reconnection attempts\n // This must be set BEFORE closing the socket, so the onclose handler sees it\n shouldReconnectRef.current = false;\n // Clear any pending reconnection timeouts\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n // Close the WebSocket connection\n if (wsRef.current) {\n const { readyState } = wsRef.current;\n // Remove this WebSocket from the allowed list BEFORE closing\n // so its onclose handler won't try to reconnect\n allowedToReconnectRef.current.delete(wsRef.current);\n // Only close if not already closed/closing\n if (\n readyState === WebSocket.CONNECTING ||\n readyState === WebSocket.OPEN\n ) {\n wsRef.current.close();\n }\n wsRef.current = null;\n }\n };\n }, [url, connectWebSocket]);\n\n const sendMessage = React.useCallback(\n (data: string | Blob | BufferSource) => {\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n wsRef.current.send(data);\n }\n },\n [],\n );\n\n const disconnect = React.useCallback(() => {\n shouldReconnectRef.current = false;\n setIsReconnecting(false);\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n if (wsRef.current) {\n // Remove from allowed list before closing\n allowedToReconnectRef.current.delete(wsRef.current);\n wsRef.current.close();\n }\n }, []);\n\n const reconnect = React.useCallback(() => {\n shouldReconnectRef.current = true;\n attemptCountRef.current = 0;\n setIsReconnecting(true);\n setError(null);\n\n if (reconnectTimeoutRef.current) {\n clearTimeout(reconnectTimeoutRef.current);\n reconnectTimeoutRef.current = null;\n }\n\n if (wsRef.current) {\n const ws = wsRef.current;\n allowedToReconnectRef.current.delete(ws);\n wsRef.current = null;\n if (\n ws.readyState === WebSocket.CONNECTING ||\n ws.readyState === WebSocket.OPEN\n ) {\n ws.close();\n }\n }\n\n connectWebSocket();\n }, [connectWebSocket]);\n\n return {\n isConnected,\n lastMessage,\n error,\n socket: wsRef.current,\n sendMessage,\n isReconnecting,\n attemptCount: attemptCountRef.current,\n disconnect,\n reconnect,\n };\n};\n"],"mappings":"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"}