@openhands/agent-canvas 1.4.0 → 1.5.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 (674) hide show
  1. package/README.md +11 -12
  2. package/README.windows.md +4 -2
  3. package/build/assets/{Trans-Ch-OVWEA.js → Trans-DqF_OEZB.js} +1 -1
  4. package/build/assets/{acp-providers-Sy2LKyMa.js → acp-providers-BQwoUBpe.js} +1 -1
  5. package/build/assets/active-backend-context-BCXRv5pm.js +1 -0
  6. package/build/assets/{add-backend-modal-Bgy8anoR.js → add-backend-modal-BKHzq5no.js} +1 -1
  7. package/build/assets/{agent-profiles-settings-3mmXljZX.js → agent-profiles-settings-CEp5CIz8.js} +1 -1
  8. package/build/assets/agent-server-compatibility-D6DSanlx.js +1 -0
  9. package/build/assets/agent-server-conversation-service.api-cylugfDz.js +41 -0
  10. package/build/assets/{agent-settings-B-W10B4E.js → agent-settings-C-tann3x.js} +2 -2
  11. package/build/assets/agent-settings-DGUKNSnU.js +1 -0
  12. package/build/assets/{alert-banner-Bpf2NQJq.js → alert-banner-Dd4woGbm.js} +1 -1
  13. package/build/assets/api-key-entry-screen-Bll_CkuQ.js +1 -0
  14. package/build/assets/app-settings-BpViWwpC.js +1 -0
  15. package/build/assets/automation-detail-Z9dN2f5l.js +2 -0
  16. package/build/assets/automation-export-D5uL0Tf8.js +2 -0
  17. package/build/assets/automations-list-BMOWcqzZ.js +1 -0
  18. package/build/assets/{back-nav-button-CUS2mi-U.js → back-nav-button-ZUDTMOTB.js} +1 -1
  19. package/build/assets/backend-form-modal-DhBT8P4g.js +1 -0
  20. package/build/assets/{backend-synced-settings-badge-D9hZx05w.js → backend-synced-settings-badge-Db_REKVw.js} +1 -1
  21. package/build/assets/{base-modal-BuF2f0YL.js → base-modal-Cb0C6m0Q.js} +1 -1
  22. package/build/assets/{bot-D7eUAuSB.js → bot-C-EA6685.js} +1 -1
  23. package/build/assets/{brand-button-vMVuyDXu.js → brand-button-2Tp3aXk1.js} +1 -1
  24. package/build/assets/{browser-BifzWIBd.js → browser-QYLVr1kS.js} +2 -2
  25. package/build/assets/browser-store-BGXOGo-R.js +1 -0
  26. package/build/assets/{browser-tab-CkkqJnqw.js → browser-tab-BTTP1OE2.js} +1 -1
  27. package/build/assets/canvas-ui-BhxjoA1f.js +1 -0
  28. package/build/assets/{chat-send-button-B_jc5z5C.js → chat-send-button-BZHG2hAW.js} +1 -1
  29. package/build/assets/check-TbOEtHKj.js +1 -0
  30. package/build/assets/{chevron-down-CI8A6tet.js → chevron-down-D7IvFMBM.js} +1 -1
  31. package/build/assets/{circle-plus-check-toggle-C5YxjxvI.js → circle-plus-check-toggle-QQJKUnF6.js} +1 -1
  32. package/build/assets/client-source-Biqarhgr.js +1 -0
  33. package/build/assets/cloud-funnel-analytics-D-27T5Ao.js +1 -0
  34. package/build/assets/{combobox-caret-DoObzqf4.js → combobox-caret-A5jMVHgE.js} +1 -1
  35. package/build/assets/{command-menu-D-JV3Wqm.js → command-menu-BMVEme4H.js} +1 -1
  36. package/build/assets/{command-store-D8J5LMW1.js → command-store-jrWX_CcW.js} +1 -1
  37. package/build/assets/condenser-settings-CIa6eyB4.js +1 -0
  38. package/build/assets/{confirmation-modal-BGLu6XJN.js → confirmation-modal-4n_944zq.js} +1 -1
  39. package/build/assets/{context-menu-list-item-BD5_EaKN.js → context-menu-list-item-DvfFic5I.js} +1 -1
  40. package/build/assets/conversation-CHnEXGRM.js +1 -0
  41. package/build/assets/conversation-D4o4dG2t.js +89 -0
  42. package/build/assets/conversation-panel-11FjLCId.js +1 -0
  43. package/build/assets/conversation-service.api-0JG_fF9d.js +1 -0
  44. package/build/assets/conversation-state-store-JiUQp02U.js +1 -0
  45. package/build/assets/{conversation-store-LptbOvKa.js → conversation-store-BCSpLuEf.js} +1 -1
  46. package/build/assets/{conversation-tab-empty-state-B2dDYEwf.js → conversation-tab-empty-state-BUhKVwoi.js} +1 -1
  47. package/build/assets/{copy-3JCw_XFB.js → copy-D1YGjP-C.js} +1 -1
  48. package/build/assets/{copy-to-clipboard-button-CaR52OnA.js → copy-to-clipboard-button-Dp4Q_hXi.js} +1 -1
  49. package/build/assets/declaration-BGsHjPEW.js +1 -0
  50. package/build/assets/{device-verify-Dj6I4Iqe.js → device-verify-CM086oKs.js} +1 -1
  51. package/build/assets/{dropdown-classes-DJSXeLqc.js → dropdown-classes-DZq0BQjb.js} +1 -1
  52. package/build/assets/{ellipsis-button-C5Z-tPFp.js → ellipsis-button-TPJdyxnK.js} +1 -1
  53. package/build/assets/{entry.client-2_3ePAm0.js → entry.client-DypYHsYm.js} +2 -2
  54. package/build/assets/{enum-filter-dropdown-DXbVwiNZ.js → enum-filter-dropdown-CW_mBg0e.js} +1 -1
  55. package/build/assets/{environment-switch-overlay-DZQfRGTx.js → environment-switch-overlay-6laJch-9.js} +1 -1
  56. package/build/assets/{extensions-hub-CLjmOGqS.js → extensions-hub-BJFbFfKS.js} +1 -1
  57. package/build/assets/{extensions-navigation-B5MeUiMq.js → extensions-navigation-DqSl7CJw.js} +1 -1
  58. package/build/assets/{external-link-D2zJjE9G.js → external-link-lhdITPqq.js} +1 -1
  59. package/build/assets/files-tab-c316dKAq.js +1 -0
  60. package/build/assets/{files-tab-store-DJsPxYdg.js → files-tab-store-tfCav6bB.js} +1 -1
  61. package/build/assets/{form-control-classes-ChpNBe9n.js → form-control-classes-CvUaw8_L.js} +1 -1
  62. package/build/assets/format-time-delta-DhDbkD7V.js +1 -0
  63. package/build/assets/git-provider-icon-DUb2pEos.js +1 -0
  64. package/build/assets/home-BQ-DC06B.js +1 -0
  65. package/build/assets/home-D7ds4Jyw.js +1 -0
  66. package/build/assets/index-home-BRFDWs_4.js +1 -0
  67. package/build/assets/install-server-modal-CvHvzZj3.js +1 -0
  68. package/build/assets/{launch-V_3Rp7WS.js → launch-A47TK2Me.js} +1 -1
  69. package/build/assets/lesson-plan-DSf95kQ2.js +4 -0
  70. package/build/assets/{list-todo-BfH6yYUK.js → list-todo-CAtvkzcy.js} +1 -1
  71. package/build/assets/llm-not-configured-banner-B7DR8SPs.js +27 -0
  72. package/build/assets/llm-settings-Ch4ZbW3c.js +1 -0
  73. package/build/assets/llm-settings-Cvi7q4ox.js +1 -0
  74. package/build/assets/llm-subscription-service-Cz1yBBvE.js +1 -0
  75. package/build/assets/loader-circle-CsAsfLIs.js +1 -0
  76. package/build/assets/{loading-spinner-BLdswi6q.js → loading-spinner-Txxx1I1p.js} +1 -1
  77. package/build/assets/manage-backends-modal-DAVhNUCe.js +1 -0
  78. package/build/assets/manifest-958c741a.js +1 -0
  79. package/build/assets/{markdown-renderer-DMT7Zkpd.js → markdown-renderer-B6cJJ_MN.js} +1 -1
  80. package/build/assets/{mcp-BQufNPZ6.js → mcp-BwssgMpg.js} +2 -2
  81. package/build/assets/{messages-BnJCqnFf.js → messages-D8MeG2ZQ.js} +26 -26
  82. package/build/assets/{modal-body-DouOE5KH.js → modal-body-CDZdZ-eT.js} +1 -1
  83. package/build/assets/{modal-classes-Cf-4tbHz.js → modal-classes-B4oit_4c.js} +1 -1
  84. package/build/assets/{modal-close-button-B6CfxxJj.js → modal-close-button-DQElFMPo.js} +1 -1
  85. package/build/assets/model-selector-BxTbbKub.js +1 -0
  86. package/build/assets/{navigation-link-D8hfwvxI.js → navigation-link-imH6S7h2.js} +1 -1
  87. package/build/assets/onboarding-CyicadGt.js +1 -0
  88. package/build/assets/onboarding-modal-DQwihcTz.js +1 -0
  89. package/build/assets/option-service.api-DAkg_F7p.js +1 -0
  90. package/build/assets/organization-service.api-QknzKDh3.js +1 -0
  91. package/build/assets/package-Dk_VtWJ4.js +1 -0
  92. package/build/assets/path-utils-BECGet7G.js +1 -0
  93. package/build/assets/{pencil-Dj2XjKmI.js → pencil-CWhApFIT.js} +1 -1
  94. package/build/assets/{plan-components-CcCBmCzW.js → plan-components-CgPHWz3E.js} +1 -1
  95. package/build/assets/{planner-tab-Dhyqcqmb.js → planner-tab-DPKHmMAX.js} +1 -1
  96. package/build/assets/plugins-management-service-DuTTxquo.js +1 -0
  97. package/build/assets/profiles-service.api-D6jvZOWu.js +1 -0
  98. package/build/assets/providers-B8pxQK8x.js +1 -0
  99. package/build/assets/proxy-CuhhyjdX.js +1 -0
  100. package/build/assets/{recommended-automations-launcher-CTcGTl4d.js → recommended-automations-launcher-DA6DWS7V.js} +1 -1
  101. package/build/assets/{refresh-cw-DM82EIyx.js → refresh-cw-B9B7-vsy.js} +1 -1
  102. package/build/assets/root-BhP6bTBB.js +2 -0
  103. package/build/assets/root-CYWeTrWE.css +1 -0
  104. package/build/assets/root-layout-oBaHHW4s.js +2 -0
  105. package/build/assets/{runtime-waiting-state-BsXLElvN.js → runtime-waiting-state-DeD9G7yJ.js} +1 -1
  106. package/build/assets/schema-field-BOLdL_U7.js +1 -0
  107. package/build/assets/sdk-section-page-ZIltERxf.js +1 -0
  108. package/build/assets/{sdk-settings-schema-Cv7M9eSe.js → sdk-settings-schema-Ba_Pa9uf.js} +1 -1
  109. package/build/assets/search-dIQ09UY2.js +1 -0
  110. package/build/assets/secrets-service-50DElFny.js +1 -0
  111. package/build/assets/{secrets-settings-Cx6yJlyb.js → secrets-settings-Digl7JLe.js} +1 -1
  112. package/build/assets/{segmented-toggle-DyQqPhoD.js → segmented-toggle-C1-ZVyr5.js} +1 -1
  113. package/build/assets/settings-BfgLHsgd.js +1 -0
  114. package/build/assets/{settings-dropdown-input-BjunGXJb.js → settings-dropdown-input-BD5C9fBn.js} +1 -1
  115. package/build/assets/{settings-index-D6sxWSRk.js → settings-index-CODBijya.js} +1 -1
  116. package/build/assets/{settings-input-bD6npsDm.js → settings-input-CzNOq-Pj.js} +1 -1
  117. package/build/assets/{settings-list-classes-Dp_OgzN0.js → settings-list-classes-CuxH9ZoR.js} +1 -1
  118. package/build/assets/settings-modal-BcSWpKI0.js +1 -0
  119. package/build/assets/settings-service.api-Bsu1PvYO.js +1 -0
  120. package/build/assets/{settings-switch-5cuxl8Dc.js → settings-switch-DqmHAfWJ.js} +1 -1
  121. package/build/assets/{settings-utils-BMdG65By.js → settings-utils-B1lBErvV.js} +1 -1
  122. package/build/assets/shared-conversation-CDskdmrb.js +1 -0
  123. package/build/assets/{sidebar-layout-DuzJMMw-.js → sidebar-layout-Cn6eQaEI.js} +1 -1
  124. package/build/assets/{sidebar-mobile-menu-toggle-VvvX32iF.js → sidebar-mobile-menu-toggle-DmCmZgq1.js} +1 -1
  125. package/build/assets/{sidebar-nav-link-5AlTT5-j.js → sidebar-nav-link-DMc8AIe7.js} +1 -1
  126. package/build/assets/{sidebar-store-CZuE8AH5.js → sidebar-store-DXWSCFpa.js} +1 -1
  127. package/build/assets/{skill-card-pill-row-eTIkE4nz.js → skill-card-pill-row-DOIWjiMF.js} +1 -1
  128. package/build/assets/skills-plugins-DLgINYuY.js +1 -0
  129. package/build/assets/{skills-settings-BUv04tSc.js → skills-settings-DPiCWalR.js} +2 -2
  130. package/build/assets/{square-chevron-right-Czw3FFWc.js → square-chevron-right-DE-JN-2Z.js} +1 -1
  131. package/build/assets/{styled-tooltip-CgX2oRaK.js → styled-tooltip-CvsjHuIf.js} +1 -1
  132. package/build/assets/{suspense-BO1Tl_Vf.js → suspense-DmZ3MSSh.js} +1 -1
  133. package/build/assets/{task-list-tab-0k9PhuqO.js → task-list-tab-CuhcDycu.js} +1 -1
  134. package/build/assets/telemetry-DHiTRl5_.js +2 -0
  135. package/build/assets/{terminal-B61WrJdS.js → terminal-CVqF-tCD.js} +1 -1
  136. package/build/assets/{toggle-switch-2j5wwR85.js → toggle-switch-CS0ClOy2.js} +1 -1
  137. package/build/assets/{trash-2-Be_7y3Mn.js → trash-2-BU6BlW4i.js} +1 -1
  138. package/build/assets/{typography-D8mHDeNG.js → typography-CQn9wyEX.js} +1 -1
  139. package/build/assets/use-acp-credential-form-LlfZO_DD.js +1 -0
  140. package/build/assets/{use-activate-agent-profile-20v1xU0H.js → use-activate-agent-profile-DpQjsXM0.js} +1 -1
  141. package/build/assets/{use-active-agent-profile-BkJN7Xsy.js → use-active-agent-profile-Gw_uJujV.js} +1 -1
  142. package/build/assets/{use-active-conversation-DOZVSVtU.js → use-active-conversation-B0sttISF.js} +1 -1
  143. package/build/assets/use-agent-profiles-LHZEK0Gg.js +1 -0
  144. package/build/assets/{use-agent-state-DHZO1obC.js → use-agent-state-C5uA0bRj.js} +1 -1
  145. package/build/assets/use-backends-health-DKt1SmiU.js +1 -0
  146. package/build/assets/{use-can-manage-org-profiles-C-OhWVBS.js → use-can-manage-org-profiles-BAtqsbCi.js} +1 -1
  147. package/build/assets/use-cloud-current-user-id-C8m1oBy4.js +1 -0
  148. package/build/assets/use-config-CXy4vD0H.js +1 -0
  149. package/build/assets/use-create-conversation-CWQGNQ9J.js +1 -0
  150. package/build/assets/{use-create-secret-D0HCOZFu.js → use-create-secret-BIrsX3OB.js} +1 -1
  151. package/build/assets/{use-event-store-DjgU4wfO.js → use-event-store-CsD17BKu.js} +1 -1
  152. package/build/assets/{use-handle-plan-click-DyRASKMC.js → use-handle-plan-click-DEuO7Byo.js} +1 -1
  153. package/build/assets/use-is-authed-Dvbpiu3B.js +1 -0
  154. package/build/assets/{use-launch-skill-in-chat-BnoDgvAy.js → use-launch-skill-in-chat-CdHOoQGM.js} +1 -1
  155. package/build/assets/use-llm-profiles-DQpQxVOD.js +1 -0
  156. package/build/assets/use-plugins-marketplace-Bkk07W_M.js +1 -0
  157. package/build/assets/use-runtime-is-ready-D4pkGT6E.js +1 -0
  158. package/build/assets/use-save-agent-profile-BxEsjnW0.js +1 -0
  159. package/build/assets/{use-save-settings-BGlSfuYn.js → use-save-settings-ioSUMqbE.js} +1 -1
  160. package/build/assets/use-settings-DNJC4gqu.js +1 -0
  161. package/build/assets/use-settings-nav-items-BUsrDbVR.js +16 -0
  162. package/build/assets/use-skills-BsmKm23M.js +1 -0
  163. package/build/assets/{use-task-list-AVBLbbcR.js → use-task-list-rs7Xbzl8.js} +1 -1
  164. package/build/assets/{use-tracking-BIXzagSI.js → use-tracking-poxQHMwR.js} +1 -1
  165. package/build/assets/{use-user-conversation-zpx7I18x.js → use-user-conversation-B337kUKB.js} +1 -1
  166. package/build/assets/{useMutation-DrCAVg5w.js → useMutation-u8UgRgDK.js} +1 -1
  167. package/build/assets/{useMutationState-D8QN0ZCy.js → useMutationState-OnAsCLA_.js} +1 -1
  168. package/build/assets/{useQueries-DClgUTc7.js → useQueries-BpIlT8Mt.js} +1 -1
  169. package/build/assets/{useQuery-CRBP8vIS.js → useQuery-D5pEZbY0.js} +1 -1
  170. package/build/assets/{useTranslation-CDRCKC9-.js → useTranslation-Cbf5AGg_.js} +1 -1
  171. package/build/assets/{utils-D3U3rlqr.js → utils-DB9LsAzA.js} +1 -1
  172. package/build/assets/{vendor~browser-Cvg7jXCP.js → vendor~browser-5Njjw1Cl.js} +1 -1
  173. package/build/assets/{vendor~browser-tab-Di0kh5Zw.js → vendor~browser-tab-Xn94EtUo.js} +1 -1
  174. package/build/assets/{vendor~entry.client-9tWQ_Ugw.js → vendor~entry.client-DDuH4--y.js} +1 -1
  175. package/build/assets/{vendor~entry.client-D6BtSOtn.js → vendor~entry.client-DQGrNspu.js} +1 -1
  176. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~dhilvkxv-BIieS_OD.js +2 -0
  177. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~iztiaf2o-rqo6vRFz.js +185 -0
  178. package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~jewnulst-C57fm7C-.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ljq5gbus-B2XD4bi2.js} +1 -1
  179. package/build/assets/{vendor~files-tab-B02k3xvx.js → vendor~files-tab-CiC3-lGh.js} +1 -1
  180. package/build/assets/{vendor~home~conversation-panel~conversation-CYMF9JYB.js → vendor~index-home~home~conversation-panel~conversation-ChmYLVgz.js} +1 -1
  181. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-9UMeuPV6.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-B8XNNEQZ.js} +1 -1
  182. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-d8aETPbZ.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CnJRLn5s.js} +1 -1
  183. package/build/assets/vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DWteoTyr.js +2 -0
  184. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-D7EUmQ8Y.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dc5nKwLO.js} +1 -1
  185. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BD0GaRGa.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-tlf6AnDe.js} +1 -1
  186. package/build/assets/{vendor~launch-CHXA9cJu.js → vendor~launch-28DK5Rsl.js} +1 -1
  187. package/build/assets/{vendor~root-layout~conversation-panel~conversation-CpiTHpHl.js → vendor~root-layout~conversation-panel~conversation-8tIsyrPj.js} +1 -1
  188. package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-_TdZcNMD.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-lYNG9C1U.js} +1 -1
  189. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation-Cn2jwSga.js → vendor~root-layout~index-home~home~conversation-panel~conversation-Zhq6xAxR.js} +1 -1
  190. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-CZM4hwUT.js → vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation~aler~gfu7qdtz-B81b08bw.js} +1 -1
  191. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~enjl5xdm-B0EIDu4M.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~ftka0fnu-jpyLM1Nn.js} +2 -2
  192. package/build/assets/{verification-settings-D576N2xa.js → verification-settings-HRgEM-_3.js} +1 -1
  193. package/build/assets/x-ZE4iplpF.js +1 -0
  194. package/build/index.html +4 -4
  195. package/build/locales/ar/openhands.json +9 -0
  196. package/build/locales/ca/openhands.json +9 -0
  197. package/build/locales/de/openhands.json +9 -0
  198. package/build/locales/en/openhands.json +9 -0
  199. package/build/locales/es/openhands.json +9 -0
  200. package/build/locales/fr/openhands.json +9 -0
  201. package/build/locales/it/openhands.json +9 -0
  202. package/build/locales/ja/openhands.json +9 -0
  203. package/build/locales/ko-KR/openhands.json +9 -0
  204. package/build/locales/no/openhands.json +9 -0
  205. package/build/locales/pt/openhands.json +9 -0
  206. package/build/locales/tr/openhands.json +9 -0
  207. package/build/locales/uk/openhands.json +9 -0
  208. package/build/locales/zh-CN/openhands.json +9 -0
  209. package/build/locales/zh-TW/openhands.json +9 -0
  210. package/config/defaults.json +5 -4
  211. package/dist/api/agent-server-adapter.cjs +3 -3
  212. package/dist/api/agent-server-adapter.cjs.map +1 -1
  213. package/dist/api/agent-server-adapter.d.ts +9 -1
  214. package/dist/api/agent-server-adapter.js +105 -102
  215. package/dist/api/agent-server-adapter.js.map +1 -1
  216. package/dist/api/agent-server-config.cjs +1 -1
  217. package/dist/api/agent-server-config.cjs.map +1 -1
  218. package/dist/api/agent-server-config.d.ts +3 -0
  219. package/dist/api/agent-server-config.js +45 -23
  220. package/dist/api/agent-server-config.js.map +1 -1
  221. package/dist/api/backend-registry/auth.cjs +1 -1
  222. package/dist/api/backend-registry/auth.cjs.map +1 -1
  223. package/dist/api/backend-registry/auth.js +1 -1
  224. package/dist/api/backend-registry/auth.js.map +1 -1
  225. package/dist/api/backend-registry/default-backend.cjs +1 -1
  226. package/dist/api/backend-registry/default-backend.cjs.map +1 -1
  227. package/dist/api/backend-registry/default-backend.d.ts +3 -0
  228. package/dist/api/backend-registry/default-backend.js +22 -10
  229. package/dist/api/backend-registry/default-backend.js.map +1 -1
  230. package/dist/api/backend-registry/storage.cjs +1 -1
  231. package/dist/api/backend-registry/storage.cjs.map +1 -1
  232. package/dist/api/backend-registry/storage.js +38 -33
  233. package/dist/api/backend-registry/storage.js.map +1 -1
  234. package/dist/api/backend-registry/types.d.ts +2 -0
  235. package/dist/api/canvas-ui-client-tool.cjs +38 -0
  236. package/dist/api/canvas-ui-client-tool.cjs.map +1 -0
  237. package/dist/api/canvas-ui-client-tool.d.ts +14 -0
  238. package/dist/api/canvas-ui-client-tool.js +47 -0
  239. package/dist/api/canvas-ui-client-tool.js.map +1 -0
  240. package/dist/api/client-source.cjs +2 -0
  241. package/dist/api/client-source.cjs.map +1 -0
  242. package/dist/api/client-source.d.ts +9 -0
  243. package/dist/api/client-source.js +10 -0
  244. package/dist/api/client-source.js.map +1 -0
  245. package/dist/api/cloud/client.cjs +2 -0
  246. package/dist/api/cloud/client.cjs.map +1 -0
  247. package/dist/api/cloud/client.d.ts +4 -0
  248. package/dist/api/cloud/client.js +38 -0
  249. package/dist/api/cloud/client.js.map +1 -0
  250. package/dist/api/cloud/conversation-service.api.cjs +1 -1
  251. package/dist/api/cloud/conversation-service.api.cjs.map +1 -1
  252. package/dist/api/cloud/conversation-service.api.js +33 -31
  253. package/dist/api/cloud/conversation-service.api.js.map +1 -1
  254. package/dist/api/cloud/organization-service.api.cjs +1 -1
  255. package/dist/api/cloud/organization-service.api.cjs.map +1 -1
  256. package/dist/api/cloud/organization-service.api.js +11 -10
  257. package/dist/api/cloud/organization-service.api.js.map +1 -1
  258. package/dist/api/cloud/proxy.cjs +1 -1
  259. package/dist/api/cloud/proxy.cjs.map +1 -1
  260. package/dist/api/cloud/proxy.d.ts +2 -40
  261. package/dist/api/cloud/proxy.js +14 -39
  262. package/dist/api/cloud/proxy.js.map +1 -1
  263. package/dist/api/conversation-metadata-store.cjs +1 -1
  264. package/dist/api/conversation-metadata-store.cjs.map +1 -1
  265. package/dist/api/conversation-metadata-store.d.ts +2 -9
  266. package/dist/api/conversation-metadata-store.js +13 -9
  267. package/dist/api/conversation-metadata-store.js.map +1 -1
  268. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  269. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  270. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +2 -1
  271. package/dist/api/conversation-service/agent-server-conversation-service.api.js +98 -88
  272. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  273. package/dist/api/conversation-service/agent-server-conversation-service.types.d.ts +5 -0
  274. package/dist/api/device-flow-client.cjs +1 -1
  275. package/dist/api/device-flow-client.cjs.map +1 -1
  276. package/dist/api/device-flow-client.d.ts +4 -58
  277. package/dist/api/device-flow-client.js +8 -98
  278. package/dist/api/device-flow-client.js.map +1 -1
  279. package/dist/api/git-service/agent-server-git-service.api.cjs +1 -1
  280. package/dist/api/git-service/agent-server-git-service.api.cjs.map +1 -1
  281. package/dist/api/git-service/agent-server-git-service.api.d.ts +10 -2
  282. package/dist/api/git-service/agent-server-git-service.api.js +107 -40
  283. package/dist/api/git-service/agent-server-git-service.api.js.map +1 -1
  284. package/dist/api/open-hands.types.d.ts +11 -0
  285. package/dist/api/secrets-service.cjs +1 -1
  286. package/dist/api/secrets-service.cjs.map +1 -1
  287. package/dist/api/secrets-service.js +15 -14
  288. package/dist/api/secrets-service.js.map +1 -1
  289. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs +1 -1
  290. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs.map +1 -1
  291. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js +73 -64
  292. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js.map +1 -1
  293. package/dist/components/conversation-events/chat/event-content-helpers/get-observation-content.cjs +2 -2
  294. package/dist/components/conversation-events/chat/event-content-helpers/get-observation-content.cjs.map +1 -1
  295. package/dist/components/conversation-events/chat/event-content-helpers/get-observation-content.js +2 -0
  296. package/dist/components/conversation-events/chat/event-content-helpers/get-observation-content.js.map +1 -1
  297. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  298. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  299. package/dist/components/features/backends/backend-form-modal.d.ts +4 -2
  300. package/dist/components/features/backends/backend-form-modal.js +202 -188
  301. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  302. package/dist/components/features/backends/backend-row.cjs +1 -1
  303. package/dist/components/features/backends/backend-row.cjs.map +1 -1
  304. package/dist/components/features/backends/backend-row.js +2 -1
  305. package/dist/components/features/backends/backend-row.js.map +1 -1
  306. package/dist/components/features/backends/backend-selector.cjs +1 -1
  307. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  308. package/dist/components/features/backends/backend-selector.js +51 -51
  309. package/dist/components/features/backends/backend-selector.js.map +1 -1
  310. package/dist/components/features/backends/device-flow-auth.cjs +1 -1
  311. package/dist/components/features/backends/device-flow-auth.cjs.map +1 -1
  312. package/dist/components/features/backends/device-flow-auth.d.ts +4 -1
  313. package/dist/components/features/backends/device-flow-auth.js +38 -38
  314. package/dist/components/features/backends/device-flow-auth.js.map +1 -1
  315. package/dist/components/features/chat/chat-interface.cjs +2 -2
  316. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  317. package/dist/components/features/chat/chat-interface.js +173 -165
  318. package/dist/components/features/chat/chat-interface.js.map +1 -1
  319. package/dist/components/features/chat/components/chat-input-actions.cjs +1 -1
  320. package/dist/components/features/chat/components/chat-input-actions.cjs.map +1 -1
  321. package/dist/components/features/chat/components/chat-input-actions.d.ts +2 -1
  322. package/dist/components/features/chat/components/chat-input-actions.js +44 -43
  323. package/dist/components/features/chat/components/chat-input-actions.js.map +1 -1
  324. package/dist/components/features/chat/components/chat-input-container.cjs +1 -1
  325. package/dist/components/features/chat/components/chat-input-container.cjs.map +1 -1
  326. package/dist/components/features/chat/components/chat-input-container.d.ts +2 -1
  327. package/dist/components/features/chat/components/chat-input-container.js +20 -19
  328. package/dist/components/features/chat/components/chat-input-container.js.map +1 -1
  329. package/dist/components/features/chat/components/chat-input-profile-picker.cjs +1 -1
  330. package/dist/components/features/chat/components/chat-input-profile-picker.cjs.map +1 -1
  331. package/dist/components/features/chat/components/chat-input-profile-picker.js +44 -32
  332. package/dist/components/features/chat/components/chat-input-profile-picker.js.map +1 -1
  333. package/dist/components/features/chat/components/resolve-picker-kind.cjs +1 -1
  334. package/dist/components/features/chat/components/resolve-picker-kind.cjs.map +1 -1
  335. package/dist/components/features/chat/components/resolve-picker-kind.d.ts +10 -22
  336. package/dist/components/features/chat/components/resolve-picker-kind.js +6 -3
  337. package/dist/components/features/chat/components/resolve-picker-kind.js.map +1 -1
  338. package/dist/components/features/chat/custom-chat-input.cjs +1 -1
  339. package/dist/components/features/chat/custom-chat-input.cjs.map +1 -1
  340. package/dist/components/features/chat/custom-chat-input.d.ts +2 -1
  341. package/dist/components/features/chat/custom-chat-input.js +52 -51
  342. package/dist/components/features/chat/custom-chat-input.js.map +1 -1
  343. package/dist/components/features/chat/interactive-chat-box.cjs +1 -1
  344. package/dist/components/features/chat/interactive-chat-box.cjs.map +1 -1
  345. package/dist/components/features/chat/interactive-chat-box.d.ts +2 -1
  346. package/dist/components/features/chat/interactive-chat-box.js +9 -8
  347. package/dist/components/features/chat/interactive-chat-box.js.map +1 -1
  348. package/dist/components/features/diff-viewer/commit-list.cjs +2 -0
  349. package/dist/components/features/diff-viewer/commit-list.cjs.map +1 -0
  350. package/dist/components/features/diff-viewer/commit-list.d.ts +13 -0
  351. package/dist/components/features/diff-viewer/commit-list.js +27 -0
  352. package/dist/components/features/diff-viewer/commit-list.js.map +1 -0
  353. package/dist/components/features/diff-viewer/commit-row.cjs +2 -0
  354. package/dist/components/features/diff-viewer/commit-row.cjs.map +1 -0
  355. package/dist/components/features/diff-viewer/commit-row.d.ts +14 -0
  356. package/dist/components/features/diff-viewer/commit-row.js +59 -0
  357. package/dist/components/features/diff-viewer/commit-row.js.map +1 -0
  358. package/dist/components/features/diff-viewer/file-diff-viewer.cjs +1 -1
  359. package/dist/components/features/diff-viewer/file-diff-viewer.cjs.map +1 -1
  360. package/dist/components/features/diff-viewer/file-diff-viewer.d.ts +7 -1
  361. package/dist/components/features/diff-viewer/file-diff-viewer.js +40 -39
  362. package/dist/components/features/diff-viewer/file-diff-viewer.js.map +1 -1
  363. package/dist/components/features/plugins/plugin-detail-modal.d.ts +2 -1
  364. package/dist/components/shared/modals/settings/settings-modal.cjs +1 -1
  365. package/dist/components/shared/modals/settings/settings-modal.cjs.map +1 -1
  366. package/dist/components/shared/modals/settings/settings-modal.js +23 -22
  367. package/dist/components/shared/modals/settings/settings-modal.js.map +1 -1
  368. package/dist/config/defaults.cjs +1 -1
  369. package/dist/config/defaults.cjs.map +1 -1
  370. package/dist/config/defaults.js +5 -4
  371. package/dist/config/defaults.js.map +1 -1
  372. package/dist/constants/acp-providers.cjs.map +1 -1
  373. package/dist/constants/acp-providers.d.ts +1 -1
  374. package/dist/constants/acp-providers.js.map +1 -1
  375. package/dist/constants/canvas-ui.cjs +2 -0
  376. package/dist/constants/canvas-ui.cjs.map +1 -0
  377. package/dist/constants/canvas-ui.d.ts +8 -0
  378. package/dist/constants/canvas-ui.js +6 -0
  379. package/dist/constants/canvas-ui.js.map +1 -0
  380. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  381. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  382. package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
  383. package/dist/hooks/mutation/use-create-conversation.cjs.map +1 -1
  384. package/dist/hooks/mutation/use-create-conversation.d.ts +2 -1
  385. package/dist/hooks/mutation/use-create-conversation.js +52 -52
  386. package/dist/hooks/mutation/use-create-conversation.js.map +1 -1
  387. package/dist/hooks/query/query-keys.cjs +1 -1
  388. package/dist/hooks/query/query-keys.cjs.map +1 -1
  389. package/dist/hooks/query/query-keys.d.ts +2 -0
  390. package/dist/hooks/query/query-keys.js +4 -1
  391. package/dist/hooks/query/query-keys.js.map +1 -1
  392. package/dist/hooks/query/use-backends-health.cjs +1 -1
  393. package/dist/hooks/query/use-backends-health.cjs.map +1 -1
  394. package/dist/hooks/query/use-backends-health.js +50 -48
  395. package/dist/hooks/query/use-backends-health.js.map +1 -1
  396. package/dist/hooks/query/use-cloud-organizations.cjs +1 -1
  397. package/dist/hooks/query/use-cloud-organizations.cjs.map +1 -1
  398. package/dist/hooks/query/use-cloud-organizations.d.ts +1 -0
  399. package/dist/hooks/query/use-cloud-organizations.js +5 -2
  400. package/dist/hooks/query/use-cloud-organizations.js.map +1 -1
  401. package/dist/hooks/query/use-commit-changes.cjs +2 -0
  402. package/dist/hooks/query/use-commit-changes.cjs.map +1 -0
  403. package/dist/hooks/query/use-commit-changes.d.ts +5 -0
  404. package/dist/hooks/query/use-commit-changes.js +34 -0
  405. package/dist/hooks/query/use-commit-changes.js.map +1 -0
  406. package/dist/hooks/query/use-task-polling.cjs +1 -1
  407. package/dist/hooks/query/use-task-polling.cjs.map +1 -1
  408. package/dist/hooks/query/use-task-polling.d.ts +3 -2
  409. package/dist/hooks/query/use-task-polling.js +53 -40
  410. package/dist/hooks/query/use-task-polling.js.map +1 -1
  411. package/dist/hooks/query/use-unified-git-commits.cjs +2 -0
  412. package/dist/hooks/query/use-unified-git-commits.cjs.map +1 -0
  413. package/dist/hooks/query/use-unified-git-commits.d.ts +10 -0
  414. package/dist/hooks/query/use-unified-git-commits.js +41 -0
  415. package/dist/hooks/query/use-unified-git-commits.js.map +1 -0
  416. package/dist/hooks/query/use-unified-git-diff.cjs +1 -1
  417. package/dist/hooks/query/use-unified-git-diff.cjs.map +1 -1
  418. package/dist/hooks/query/use-unified-git-diff.d.ts +5 -0
  419. package/dist/hooks/query/use-unified-git-diff.js +11 -4
  420. package/dist/hooks/query/use-unified-git-diff.js.map +1 -1
  421. package/dist/hooks/use-acp-model-context.cjs +1 -1
  422. package/dist/hooks/use-acp-model-context.cjs.map +1 -1
  423. package/dist/hooks/use-acp-model-context.d.ts +4 -1
  424. package/dist/hooks/use-acp-model-context.js +9 -8
  425. package/dist/hooks/use-acp-model-context.js.map +1 -1
  426. package/dist/hooks/use-auto-refresh-files-on-edit.cjs +1 -1
  427. package/dist/hooks/use-auto-refresh-files-on-edit.cjs.map +1 -1
  428. package/dist/hooks/use-auto-refresh-files-on-edit.js +1 -1
  429. package/dist/hooks/use-auto-refresh-files-on-edit.js.map +1 -1
  430. package/dist/hooks/use-chat-input-model-state.cjs.map +1 -1
  431. package/dist/hooks/use-chat-input-model-state.d.ts +2 -1
  432. package/dist/hooks/use-chat-input-model-state.js.map +1 -1
  433. package/dist/hooks/use-chat-input-profile-state.cjs +1 -1
  434. package/dist/hooks/use-chat-input-profile-state.cjs.map +1 -1
  435. package/dist/hooks/use-chat-input-profile-state.d.ts +1 -10
  436. package/dist/hooks/use-chat-input-profile-state.js +42 -15
  437. package/dist/hooks/use-chat-input-profile-state.js.map +1 -1
  438. package/dist/hooks/use-device-flow.cjs +1 -1
  439. package/dist/hooks/use-device-flow.cjs.map +1 -1
  440. package/dist/hooks/use-device-flow.d.ts +4 -1
  441. package/dist/hooks/use-device-flow.js +42 -40
  442. package/dist/hooks/use-device-flow.js.map +1 -1
  443. package/dist/hooks/use-tracking.cjs.map +1 -1
  444. package/dist/hooks/use-tracking.d.ts +2 -1
  445. package/dist/hooks/use-tracking.js.map +1 -1
  446. package/dist/i18n/declaration.cjs +1 -1
  447. package/dist/i18n/declaration.cjs.map +1 -1
  448. package/dist/i18n/declaration.d.ts +9 -0
  449. package/dist/i18n/declaration.js +1 -1
  450. package/dist/i18n/declaration.js.map +1 -1
  451. package/dist/i18n/index.cjs +1 -1
  452. package/dist/i18n/index.cjs.map +1 -1
  453. package/dist/i18n/index.js +19 -18
  454. package/dist/i18n/index.js.map +1 -1
  455. package/dist/i18n/translation.cjs +2 -2
  456. package/dist/i18n/translation.cjs.map +1 -1
  457. package/dist/i18n/translation.js +153 -0
  458. package/dist/i18n/translation.js.map +1 -1
  459. package/dist/index.cjs +1 -1
  460. package/dist/index.js +32 -32
  461. package/dist/lib/index.cjs +1 -1
  462. package/dist/lib/index.js +1 -1
  463. package/dist/locales/ar/openhands.json +9 -0
  464. package/dist/locales/ca/openhands.json +9 -0
  465. package/dist/locales/de/openhands.json +9 -0
  466. package/dist/locales/en/openhands.json +9 -0
  467. package/dist/locales/es/openhands.json +9 -0
  468. package/dist/locales/fr/openhands.json +9 -0
  469. package/dist/locales/it/openhands.json +9 -0
  470. package/dist/locales/ja/openhands.json +9 -0
  471. package/dist/locales/ko-KR/openhands.json +9 -0
  472. package/dist/locales/no/openhands.json +9 -0
  473. package/dist/locales/pt/openhands.json +9 -0
  474. package/dist/locales/tr/openhands.json +9 -0
  475. package/dist/locales/uk/openhands.json +9 -0
  476. package/dist/locales/zh-CN/openhands.json +9 -0
  477. package/dist/locales/zh-TW/openhands.json +9 -0
  478. package/dist/node_modules/@openhands/typescript-client/dist/client/cloud-client.cjs +2 -0
  479. package/dist/node_modules/@openhands/typescript-client/dist/client/cloud-client.cjs.map +1 -0
  480. package/dist/node_modules/@openhands/typescript-client/dist/client/cloud-client.js +351 -0
  481. package/dist/node_modules/@openhands/typescript-client/dist/client/cloud-client.js.map +1 -0
  482. package/dist/node_modules/@openhands/typescript-client/dist/client/device-flow-client.cjs +2 -0
  483. package/dist/node_modules/@openhands/typescript-client/dist/client/device-flow-client.cjs.map +1 -0
  484. package/dist/node_modules/@openhands/typescript-client/dist/client/device-flow-client.js +106 -0
  485. package/dist/node_modules/@openhands/typescript-client/dist/client/device-flow-client.js.map +1 -0
  486. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.cjs +2 -0
  487. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.cjs.map +1 -0
  488. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.js +69 -0
  489. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.js.map +1 -0
  490. package/dist/node_modules/@openhands/typescript-client/dist/clients.cjs +1 -1
  491. package/dist/node_modules/@openhands/typescript-client/dist/clients.js +3 -0
  492. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs +1 -1
  493. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs.map +1 -1
  494. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js +18 -2
  495. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js.map +1 -1
  496. package/dist/node_modules/@openhands/typescript-client/dist/models/acp.cjs.map +1 -1
  497. package/dist/node_modules/@openhands/typescript-client/dist/models/acp.js.map +1 -1
  498. package/dist/node_modules/axios/index.cjs +1 -1
  499. package/dist/node_modules/axios/index.js +1 -1
  500. package/dist/package.cjs +1 -1
  501. package/dist/package.cjs.map +1 -1
  502. package/dist/package.js +2 -2
  503. package/dist/package.js.map +1 -1
  504. package/dist/routes/app-settings.cjs +1 -1
  505. package/dist/routes/app-settings.cjs.map +1 -1
  506. package/dist/routes/app-settings.js +67 -66
  507. package/dist/routes/app-settings.js.map +1 -1
  508. package/dist/routes/commits-tab.cjs +2 -0
  509. package/dist/routes/commits-tab.cjs.map +1 -0
  510. package/dist/routes/commits-tab.d.ts +8 -0
  511. package/dist/routes/commits-tab.js +45 -0
  512. package/dist/routes/commits-tab.js.map +1 -0
  513. package/dist/routes/files-tab.cjs +1 -1
  514. package/dist/routes/files-tab.cjs.map +1 -1
  515. package/dist/routes/files-tab.js +148 -127
  516. package/dist/routes/files-tab.js.map +1 -1
  517. package/dist/services/cloud-funnel-analytics.cjs +2 -0
  518. package/dist/services/cloud-funnel-analytics.cjs.map +1 -0
  519. package/dist/services/cloud-funnel-analytics.d.ts +12 -0
  520. package/dist/services/cloud-funnel-analytics.js +53 -0
  521. package/dist/services/cloud-funnel-analytics.js.map +1 -0
  522. package/dist/services/telemetry.cjs +1 -1
  523. package/dist/services/telemetry.cjs.map +1 -1
  524. package/dist/services/telemetry.js +49 -47
  525. package/dist/services/telemetry.js.map +1 -1
  526. package/dist/types/agent-server/core/base/action.d.ts +5 -4
  527. package/dist/types/agent-server/core/base/base.d.ts +3 -2
  528. package/dist/types/agent-server/core/base/observation.d.ts +1 -1
  529. package/dist/types/agent-server/type-guards.cjs +1 -1
  530. package/dist/types/agent-server/type-guards.cjs.map +1 -1
  531. package/dist/types/agent-server/type-guards.d.ts +3 -5
  532. package/dist/types/agent-server/type-guards.js +2 -1
  533. package/dist/types/agent-server/type-guards.js.map +1 -1
  534. package/dist/types/automation.d.ts +5 -0
  535. package/dist/utils/api-error-message.d.ts +11 -0
  536. package/dist/utils/automation-timeout.d.ts +19 -0
  537. package/dist/utils/base-path.cjs +2 -0
  538. package/dist/utils/base-path.cjs.map +1 -0
  539. package/dist/utils/base-path.d.ts +2 -0
  540. package/dist/utils/base-path.js +23 -0
  541. package/dist/utils/base-path.js.map +1 -0
  542. package/package.json +2 -2
  543. package/scripts/check-sdk-version-sync.mjs +6 -6
  544. package/scripts/dev-safe.mjs +7 -8
  545. package/scripts/dev-static.mjs +3 -0
  546. package/scripts/dev-with-automation.mjs +3 -0
  547. package/scripts/docker-build.mjs +4 -0
  548. package/scripts/static-server.mjs +93 -7
  549. package/tools/canvas_ui_tool.py +14 -8
  550. package/build/assets/active-backend-context-hxq31W25.js +0 -1
  551. package/build/assets/agent-server-client-options-DVLRd9sw.js +0 -1
  552. package/build/assets/agent-server-conversation-service.api-CuSdr4Qb.js +0 -5
  553. package/build/assets/agent-settings-D_yOy8XB.js +0 -1
  554. package/build/assets/api-key-entry-screen-Dl0qVYi4.js +0 -1
  555. package/build/assets/app-settings-yVizGXox.js +0 -1
  556. package/build/assets/automation-detail-dvJfLs5u.js +0 -2
  557. package/build/assets/automation-export-BPcgLqPD.js +0 -2
  558. package/build/assets/automations-list-CUXogTe0.js +0 -1
  559. package/build/assets/backend-form-modal-CQJGmItq.js +0 -1
  560. package/build/assets/browser-store-BI6uklCG.js +0 -1
  561. package/build/assets/check-Bvi71siz.js +0 -1
  562. package/build/assets/condenser-settings-Ca8fYZ_Z.js +0 -1
  563. package/build/assets/conversation-9NBSRoR9.js +0 -1
  564. package/build/assets/conversation-BXg6Oti2.js +0 -89
  565. package/build/assets/conversation-panel-CZri9Rup.js +0 -1
  566. package/build/assets/conversation-service.api-DPBQvTKD.js +0 -1
  567. package/build/assets/conversation-state-store-B2Bp32XI.js +0 -1
  568. package/build/assets/declaration-gq8V5eTU.js +0 -1
  569. package/build/assets/files-tab-UVoray88.js +0 -1
  570. package/build/assets/git-provider-icon-o6hdnJUB.js +0 -1
  571. package/build/assets/home-D1IHvGTN.js +0 -1
  572. package/build/assets/index-redirect-CUUJiNki.js +0 -1
  573. package/build/assets/install-server-modal-BbuQaA2w.js +0 -1
  574. package/build/assets/lesson-plan-jC0HBoe6.js +0 -4
  575. package/build/assets/llm-not-configured-banner-DoARbcdL.js +0 -27
  576. package/build/assets/llm-settings-4JMLV8mq.js +0 -1
  577. package/build/assets/llm-settings-D91gMw3U.js +0 -1
  578. package/build/assets/llm-subscription-service-CKyaV24y.js +0 -1
  579. package/build/assets/loader-circle-BEykUKYG.js +0 -1
  580. package/build/assets/manage-backends-modal-DkWAMSle.js +0 -1
  581. package/build/assets/manifest-aa5c29f6.js +0 -1
  582. package/build/assets/model-selector-BfqpMkSJ.js +0 -1
  583. package/build/assets/mouse-pointer-click-PdEQPL6v.js +0 -1
  584. package/build/assets/onboarding-Bu-HN1ze.js +0 -1
  585. package/build/assets/onboarding-modal-CZgjs5Bx.js +0 -1
  586. package/build/assets/option-service.api-BrZ5q87N.js +0 -1
  587. package/build/assets/organization-service.api-Bk4pCGU1.js +0 -1
  588. package/build/assets/path-utils-CSmuZoix.js +0 -1
  589. package/build/assets/plugins-management-service-BRBLxvUl.js +0 -1
  590. package/build/assets/profiles-service.api-BMXQ9pZC.js +0 -1
  591. package/build/assets/providers-BpO967xS.js +0 -1
  592. package/build/assets/proxy-BgJLtxu7.js +0 -1
  593. package/build/assets/root-BM5WgQ4_.js +0 -2
  594. package/build/assets/root-V8fkplMy.css +0 -1
  595. package/build/assets/root-layout-BdoyoobY.js +0 -2
  596. package/build/assets/schema-field-BSHrUMme.js +0 -1
  597. package/build/assets/sdk-section-page-t82gsOqa.js +0 -1
  598. package/build/assets/search-4DHskuhE.js +0 -1
  599. package/build/assets/secrets-service-B2GqPV0N.js +0 -1
  600. package/build/assets/settings-DmPLkgq0.js +0 -1
  601. package/build/assets/settings-modal-CwrZKrTq.js +0 -1
  602. package/build/assets/settings-service.api-Ce-ub11I.js +0 -1
  603. package/build/assets/shared-conversation-BDREQPnE.js +0 -1
  604. package/build/assets/skills-plugins-2Y44YHbO.js +0 -1
  605. package/build/assets/use-acp-credential-form-MzZM6D7D.js +0 -1
  606. package/build/assets/use-agent-profiles-DwZxnwO5.js +0 -1
  607. package/build/assets/use-backends-health-CRHzuvxO.js +0 -1
  608. package/build/assets/use-cloud-current-user-id-utFreJ0i.js +0 -1
  609. package/build/assets/use-config-mJrtZHP1.js +0 -1
  610. package/build/assets/use-create-conversation-DdqsP78F.js +0 -1
  611. package/build/assets/use-is-authed-B6M0mcGL.js +0 -1
  612. package/build/assets/use-llm-profiles-RC-PkpBl.js +0 -1
  613. package/build/assets/use-plugins-marketplace-FHlbeQ4c.js +0 -1
  614. package/build/assets/use-runtime-is-ready-RKj2WmEh.js +0 -1
  615. package/build/assets/use-save-agent-profile-tvDP15mh.js +0 -1
  616. package/build/assets/use-settings-CH5LzXD8.js +0 -1
  617. package/build/assets/use-settings-nav-items-Dwktv8wo.js +0 -16
  618. package/build/assets/use-skills-CSzSL6HG.js +0 -1
  619. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~lf310vj7-CSVPGId3.js +0 -10
  620. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~nfpll1wp-BCpa95De.js +0 -177
  621. package/build/assets/vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CqXiHwjU.js +0 -2
  622. package/build/assets/x-Cb0WAtak.js +0 -1
  623. /package/build/assets/{__vite-browser-external-4xJsU2-u.js → __vite-browser-external-DU-Cb_sT.js} +0 -0
  624. /package/build/assets/{automation-BU8zu-FP.js → automation-BVEmGghX.js} +0 -0
  625. /package/build/assets/{checkmark-h7udgXMM.js → checkmark-DhcMGwtO.js} +0 -0
  626. /package/build/assets/{close-p3Op5lxI.js → close-DO5Rq_CS.js} +0 -0
  627. /package/build/assets/{code-tag-DYlAhEQY.js → code-tag-BvjtzUsr.js} +0 -0
  628. /package/build/assets/{color-themes-DPBzNtVg.js → color-themes-bVMmJJG6.js} +0 -0
  629. /package/build/assets/{common-Dh7OqABE.js → common-CxbUL3nR.js} +0 -0
  630. /package/build/assets/{conversation-local-storage-F6zVMM--.js → conversation-local-storage-B31voBg7.js} +0 -0
  631. /package/build/assets/{copy-BLmGeJcd.js → copy-DLUw2us-.js} +0 -0
  632. /package/build/assets/{createLucideIcon-CVAsW2Yo.js → createLucideIcon-D4Tak8A1.js} +0 -0
  633. /package/build/assets/{dist-CodhvuAP.js → dist-Dj01FXbZ.js} +0 -0
  634. /package/build/assets/{environment-switch-store-DkjlKWzN.js → environment-switch-store-D9hB9PvY.js} +0 -0
  635. /package/build/assets/{extension-module-card-classes-9V3J9J_b.js → extension-module-card-classes-BlO4w702.js} +0 -0
  636. /package/build/assets/{folder-mMWFvaw0.js → folder-ynxP2EIL.js} +0 -0
  637. /package/build/assets/{get-language-from-path-CPNnuVhI.js → get-language-from-path-rWEQrOGU.js} +0 -0
  638. /package/build/assets/{git-status-mapper-BI8FyUVp.js → git-status-mapper-C3rfzUex.js} +0 -0
  639. /package/build/assets/{handle-capture-consent-CPMXU29K.js → handle-capture-consent-DDnXMC9Y.js} +0 -0
  640. /package/build/assets/{iconBase-DaTRRUjy.js → iconBase-CuvxAQZ5.js} +0 -0
  641. /package/build/assets/{link-external-DfKQroKX.js → link-external-BVDiJfZt.js} +0 -0
  642. /package/build/assets/{llm-subscription-DtsX5Lhg.js → llm-subscription-BkdHwtQe.js} +0 -0
  643. /package/build/assets/{map-provider-BM2SJarp.js → map-provider-XSFHmdDs.js} +0 -0
  644. /package/build/assets/{middleware-DYHYNAnP.js → middleware-CWYDMVuG.js} +0 -0
  645. /package/build/assets/{modal-backdrop-zVwfOJZr.js → modal-backdrop-Dsmzgr5s.js} +0 -0
  646. /package/build/assets/{navigation-context-DPs2G0X8.js → navigation-context-SjzfpygH.js} +0 -0
  647. /package/build/assets/{objectWithoutPropertiesLoose-BgYIAs2y.js → objectWithoutPropertiesLoose-CQwg6tC0.js} +0 -0
  648. /package/build/assets/{openhands-logo-bcNw5bkY.js → openhands-logo-C-gDp1wr.js} +0 -0
  649. /package/build/assets/{react-DPDgL6du.js → react-ao3tWj6K.js} +0 -0
  650. /package/build/assets/{search-BsjCEFNf.js → search-pb4UKTfK.js} +0 -0
  651. /package/build/assets/{settings-mQUckT0g.js → settings-DksoDv5z.js} +0 -0
  652. /package/build/assets/{settings-gear-MXCyZW0x.js → settings-gear-D_HVvE5L.js} +0 -0
  653. /package/build/assets/{settings-like-page-layout-classes-DENKlZpD.js → settings-like-page-layout-classes-DLrsPCaI.js} +0 -0
  654. /package/build/assets/{settings-section-header-context-COMOIaxV.js → settings-section-header-context-C2MUIG88.js} +0 -0
  655. /package/build/assets/{skills-9cDZONKh.js → skills-CH4xgIh-.js} +0 -0
  656. /package/build/assets/{switch-skeleton-DqQwalyu.js → switch-skeleton-BpSOvZ-n.js} +0 -0
  657. /package/build/assets/{terminal-BuLtIrb9.js → terminal-Dokfkxn0.js} +0 -0
  658. /package/build/assets/{u-check-circle-BqoIxNz_.js → u-check-circle-DPWVO-9N.js} +0 -0
  659. /package/build/assets/{u-check-circle-half-LiwZkuFe.js → u-check-circle-half-BGNAAjOW.js} +0 -0
  660. /package/build/assets/{u-edit-DJ90xc8Z.js → u-edit-BIskOoEG.js} +0 -0
  661. /package/build/assets/{use-breakpoint-B-O0lQ84.js → use-breakpoint-OjLm94cF.js} +0 -0
  662. /package/build/assets/{use-click-outside-element-Bf4Omzot.js → use-click-outside-element-Jpt2WWd7.js} +0 -0
  663. /package/build/assets/{use-onboarding-completion-C_Swglzp.js → use-onboarding-completion-DkRpDTsD.js} +0 -0
  664. /package/build/assets/{v4-DbCkTSoU.js → v4-DPgam_jW.js} +0 -0
  665. /package/build/assets/{vendor~browser-Dqd33JyP.js → vendor~browser-BvEKaLBz.js} +0 -0
  666. /package/build/assets/{vendor~conversation-panel~conversation~alert-banner-DHDkknt5.js → vendor~conversation-panel~conversation~alert-banner-CznX01nZ.js} +0 -0
  667. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~epa72zsm-B4rXs7Zd.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~e0djk9ee-Cw6LUe8p.js} +0 -0
  668. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~ivvrgjsa-BlIZPCXX.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~g9hift2g-DmmpG9SI.js} +0 -0
  669. /package/build/assets/{vendor~entry.client~root~root-layout~index-redirect~home~conversation-panel~conversation~la~byh8a1g4-XNPdpuO8.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~jkpzbynd-CkpBIhVe.js} +0 -0
  670. /package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DTzw2x50.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BuL5kjfS.js} +0 -0
  671. /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~skills-settings~automations-~ki5qnp0d-rV6d1Iv_.js → vendor~root-layout~index-home~home~conversation-panel~conversation~launch~skills-settings~a~j2jlu22h-DUYl8gbZ.js} +0 -0
  672. /package/build/assets/{vendor~root-layout~home~mcp~automations-list~onboarding-modal-CXyYYvYh.js → vendor~root-layout~index-home~home~mcp~automations-list~onboarding-modal-pIgQDxTc.js} +0 -0
  673. /package/build/assets/{vendor~terminal-aeP3PnKf.js → vendor~terminal-jAaNQWS9.js} +0 -0
  674. /package/build/assets/{x-mark-Batp6gXa.js → x-mark-C5przfXl.js} +0 -0
@@ -6,14 +6,14 @@ import { cn as i } from "../../../utils/utils.js";
6
6
  import { useConversationStore as a } from "../../../stores/conversation-store.js";
7
7
  import { isNoBackend as o } from "../../../api/backend-registry/active-store.js";
8
8
  import { useActiveBackendContext as s } from "../../../contexts/active-backend-context.js";
9
- import { formControlTransitionClassName as ee } from "../../../utils/form-control-classes.js";
10
- import { StyledTooltip as te } from "../../shared/buttons/styled-tooltip.js";
11
- import { dropdownFooterActionClassName as c, dropdownMenuListClassName as ne, dropdownMenuRowIconWrapperClassName as l } from "../../../utils/dropdown-classes.js";
12
- import { NavigationLink as re } from "../../shared/navigation-link.js";
13
- import { useAllCloudOrganizations as ie } from "../../../hooks/query/use-cloud-organizations.js";
14
- import { useCloudCurrentUserId as ae } from "../../../hooks/query/use-cloud-current-user-id.js";
15
- import { Dropdown as u } from "../../../ui/dropdown/dropdown.js";
16
- import { useBackendsHealth as d } from "../../../hooks/query/use-backends-health.js";
9
+ import { formControlTransitionClassName as c } from "../../../utils/form-control-classes.js";
10
+ import { StyledTooltip as ee } from "../../shared/buttons/styled-tooltip.js";
11
+ import { dropdownFooterActionClassName as l, dropdownMenuListClassName as te, dropdownMenuRowIconWrapperClassName as u } from "../../../utils/dropdown-classes.js";
12
+ import { NavigationLink as ne } from "../../shared/navigation-link.js";
13
+ import { useAllCloudOrganizations as re } from "../../../hooks/query/use-cloud-organizations.js";
14
+ import { useCloudCurrentUserId as d } from "../../../hooks/query/use-cloud-current-user-id.js";
15
+ import { Dropdown as ie } from "../../../ui/dropdown/dropdown.js";
16
+ import { useBackendsHealth as ae } from "../../../hooks/query/use-backends-health.js";
17
17
  import { triggerEnvironmentSwitch as f } from "./environment-switch-store.js";
18
18
  import { BackendStatusDot as p } from "./backend-status-dot.js";
19
19
  import { AddBackendModal as oe } from "./add-backend-modal.js";
@@ -39,7 +39,7 @@ function b(e) {
39
39
  function de() {
40
40
  return /* @__PURE__ */ h(p, { isConnected: "unavailable" });
41
41
  }
42
- function fe(e, t, n, r, i) {
42
+ function x(e, t, n, r, i) {
43
43
  let a = [], o = e.filter((e) => e.kind === "local"), s = e.filter((e) => e.kind === "cloud");
44
44
  for (let e of o) a.push({
45
45
  value: y(e.id, null),
@@ -67,20 +67,20 @@ function fe(e, t, n, r, i) {
67
67
  }
68
68
  return a;
69
69
  }
70
- function x({ openUpward: p = !1, hideTrigger: v = !1, defaultOpen: x = !1, onSelectOption: S, onOpenAddBackend: C, onOpenManageBackends: w, sidebarCollapsed: T = !1 } = {}) {
71
- let { t: E } = e("openhands"), { backends: D, active: O, setActive: k } = s(), A = ie(), j = ae(), M = d(D), N = le(), P = _("/settings"), F = _("/settings/*"), I = _("/conversations/:conversationId"), L = _("/automations/:automationId"), [R, z] = m.useState(!1), [B, V] = m.useState(!1), H = E(t.BACKEND$PERSONAL_WORKSPACE), U = m.useMemo(() => fe(D, H, A, j, M), [
70
+ function S({ openUpward: p = !1, hideTrigger: v = !1, defaultOpen: S = !1, onSelectOption: C, onOpenAddBackend: w, onOpenManageBackends: T, sidebarCollapsed: fe = !1 } = {}) {
71
+ let { t: E } = e("openhands"), { backends: D, active: O, setActive: k } = s(), A = re(), j = d(), M = ae(D), N = le(), P = _("/settings"), F = _("/settings/*"), I = _("/conversations/:conversationId"), L = _("/automations/:automationId"), [R, z] = m.useState(!1), [B, V] = m.useState(!1), H = E(t.BACKEND$PERSONAL_WORKSPACE), U = m.useMemo(() => x(D, H, A, j, M), [
72
72
  D,
73
73
  H,
74
74
  A,
75
75
  j,
76
76
  M
77
- ]), W = o(O.backend), G = E(t.BACKEND$NO_BACKEND_AVAILABLE), K = y(O.backend.id, O.orgId), q = W ? void 0 : U.find((e) => e.value === K), J = !!(P || F), Y = E(t.SIDEBAR$SETTINGS), pe = a((e) => e.isRightPanelShown), me = !T || I && pe ? "top" : "left", he = Object.values(A).some((e) => e.isLoading);
77
+ ]), W = o(O.backend), G = E(t.BACKEND$NO_BACKEND_AVAILABLE), K = y(O.backend.id, O.orgId), q = W ? void 0 : U.find((e) => e.value === K), J = !!(P || F), Y = E(t.SIDEBAR$SETTINGS), X = a((e) => e.isRightPanelShown), pe = !fe || I && X ? "top" : "left", me = Object.values(A).some((e) => e.isLoading);
78
78
  m.useEffect(() => {
79
79
  if (W || O.backend.kind !== "cloud" || O.orgId) return;
80
80
  let { backend: e } = O, t = A[e.id];
81
81
  if (!t || t.orgs.length === 0) return;
82
- let n = j[e.id]?.userId ?? null, r = (n ? t.orgs.find((e) => e.id === n) : void 0) ?? t.orgs[0];
83
- r && k(e.id, r.id);
82
+ let n = t.currentOrgId ? t.orgs.find((e) => e.id === t.currentOrgId) : void 0, r = j[e.id]?.userId ?? null, i = r ? t.orgs.find((e) => e.id === r) : void 0, a = n ?? i ?? t.orgs[0];
83
+ a && k(e.id, a.id);
84
84
  }, [
85
85
  O,
86
86
  A,
@@ -88,40 +88,40 @@ function x({ openUpward: p = !1, hideTrigger: v = !1, defaultOpen: x = !1, onSel
88
88
  k,
89
89
  W
90
90
  ]);
91
- let X = m.useCallback(() => {
92
- if (C) {
93
- C(), S?.();
91
+ let Z = m.useCallback(() => {
92
+ if (w) {
93
+ w(), C?.();
94
94
  return;
95
95
  }
96
96
  z(!0);
97
- }, [C, S]), Z = m.useCallback(() => {
98
- if (w) {
99
- w(), S?.();
97
+ }, [w, C]), Q = m.useCallback(() => {
98
+ if (T) {
99
+ T(), C?.();
100
100
  return;
101
101
  }
102
102
  V(!0);
103
- }, [w, S]), Q = m.useCallback((e) => {
103
+ }, [T, C]), $ = m.useCallback((e) => {
104
104
  e.preventDefault(), e.stopPropagation();
105
- }, []), ge = m.useCallback((e) => {
106
- Q(e), X();
107
- }, [X, Q]), $ = m.useCallback((e) => {
108
- Q(e), X();
109
- }, [X, Q]), _e = m.useCallback((e) => {
110
- Q(e), Z();
111
- }, [Z, Q]), ve = m.useCallback((e) => {
112
- Q(e), Z();
113
- }, [Z, Q]), ye = /* @__PURE__ */ g("div", {
114
- className: ne,
105
+ }, []), he = m.useCallback((e) => {
106
+ $(e), Z();
107
+ }, [Z, $]), ge = m.useCallback((e) => {
108
+ $(e), Z();
109
+ }, [Z, $]), _e = m.useCallback((e) => {
110
+ $(e), Q();
111
+ }, [Q, $]), ve = m.useCallback((e) => {
112
+ $(e), Q();
113
+ }, [Q, $]), ye = /* @__PURE__ */ g("div", {
114
+ className: te,
115
115
  children: [/* @__PURE__ */ g("button", {
116
116
  type: "button",
117
117
  "data-testid": "add-backend-menu-item",
118
- onMouseDown: Q,
119
- onTouchStart: Q,
120
- onTouchEnd: $,
121
- onClick: ge,
122
- className: i(c, "cursor-pointer rounded-md"),
118
+ onMouseDown: $,
119
+ onTouchStart: $,
120
+ onTouchEnd: ge,
121
+ onClick: he,
122
+ className: i(l, "cursor-pointer rounded-md"),
123
123
  children: [/* @__PURE__ */ h("span", {
124
- className: l,
124
+ className: u,
125
125
  "aria-hidden": !0,
126
126
  children: /* @__PURE__ */ h(n, {
127
127
  width: 16,
@@ -131,13 +131,13 @@ function x({ openUpward: p = !1, hideTrigger: v = !1, defaultOpen: x = !1, onSel
131
131
  }), /* @__PURE__ */ g("button", {
132
132
  type: "button",
133
133
  "data-testid": "manage-backends-menu-item",
134
- onMouseDown: Q,
135
- onTouchStart: Q,
134
+ onMouseDown: $,
135
+ onTouchStart: $,
136
136
  onTouchEnd: ve,
137
137
  onClick: _e,
138
- className: i(c, "cursor-pointer rounded-md"),
138
+ className: i(l, "cursor-pointer rounded-md"),
139
139
  children: [/* @__PURE__ */ h("span", {
140
- className: l,
140
+ className: u,
141
141
  "aria-hidden": !0,
142
142
  children: /* @__PURE__ */ h(r, {
143
143
  width: 16,
@@ -150,7 +150,7 @@ function x({ openUpward: p = !1, hideTrigger: v = !1, defaultOpen: x = !1, onSel
150
150
  let { backendId: t, orgId: n } = ue(e), r = D.find((e) => e.id === t);
151
151
  r && (f(U.find((t) => t.value === e)?.label ?? r.name), await new Promise((e) => {
152
152
  setTimeout(e, 400);
153
- }), I ? N("/conversations") : L && N("/automations"), k(r.id, n), S?.());
153
+ }), I ? N("/conversations") : L && N("/automations"), k(r.id, n), C?.());
154
154
  }, [
155
155
  K,
156
156
  D,
@@ -160,14 +160,14 @@ function x({ openUpward: p = !1, hideTrigger: v = !1, defaultOpen: x = !1, onSel
160
160
  U,
161
161
  k,
162
162
  E,
163
- S
163
+ C
164
164
  ]);
165
165
  return /* @__PURE__ */ g(ce, { children: [
166
166
  /* @__PURE__ */ g("div", {
167
167
  className: "flex items-center gap-2 w-full",
168
168
  children: [/* @__PURE__ */ h("div", {
169
169
  className: "flex-1 min-w-0",
170
- children: /* @__PURE__ */ h(u, {
170
+ children: /* @__PURE__ */ h(ie, {
171
171
  testId: "backend-selector",
172
172
  defaultValue: q ?? {
173
173
  value: K,
@@ -177,26 +177,26 @@ function x({ openUpward: p = !1, hideTrigger: v = !1, defaultOpen: x = !1, onSel
177
177
  footer: ye,
178
178
  openUpward: p,
179
179
  hideTrigger: v,
180
- defaultOpen: x,
180
+ defaultOpen: S,
181
181
  openOnHover: !v,
182
182
  onChange: (e) => {
183
183
  e && be(e.value);
184
184
  },
185
185
  placeholder: W ? G : O.backend.name,
186
- loading: he,
186
+ loading: me,
187
187
  options: U,
188
188
  className: "h-10 px-2 py-0 bg-transparent border-transparent hover:bg-[var(--oh-surface-raised)] focus-within:bg-[var(--oh-surface-raised)] focus-within:border-transparent focus-within:ring-0"
189
189
  }, `${K}-${q?.label ?? ""}`)
190
- }), v ? null : /* @__PURE__ */ h(te, {
190
+ }), v ? null : /* @__PURE__ */ h(ee, {
191
191
  content: Y,
192
- placement: me,
192
+ placement: pe,
193
193
  offset: 10,
194
- children: /* @__PURE__ */ h(re, {
194
+ children: /* @__PURE__ */ h(ne, {
195
195
  to: "/settings",
196
196
  "data-testid": "backend-selector-settings-link",
197
197
  "data-active": J,
198
198
  "aria-label": Y,
199
- className: i(J ? "inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-md bg-tertiary text-white font-normal cursor-pointer" : "inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-md text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)] cursor-pointer", ee),
199
+ className: i(J ? "inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-md bg-tertiary text-white font-normal cursor-pointer" : "inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-md text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)] cursor-pointer", c),
200
200
  children: /* @__PURE__ */ h(r, {
201
201
  width: 16,
202
202
  height: 16
@@ -209,6 +209,6 @@ function x({ openUpward: p = !1, hideTrigger: v = !1, defaultOpen: x = !1, onSel
209
209
  ] });
210
210
  }
211
211
  //#endregion
212
- export { x as BackendSelector };
212
+ export { S as BackendSelector };
213
213
 
214
214
  //# sourceMappingURL=backend-selector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"backend-selector.js","names":[],"sources":["../../../../src/components/features/backends/backend-selector.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useMatch, useNavigate } from \"react-router\";\nimport { Plus, Settings } from \"lucide-react\";\nimport { Dropdown } from \"#/ui/dropdown/dropdown\";\nimport { DropdownOption } from \"#/ui/dropdown/types\";\nimport { isNoBackend } from \"#/api/backend-registry/active-store\";\nimport { useActiveBackendContext } from \"#/contexts/active-backend-context\";\nimport { useAllCloudOrganizations } from \"#/hooks/query/use-cloud-organizations\";\nimport { useCloudCurrentUserId } from \"#/hooks/query/use-cloud-current-user-id\";\nimport {\n useBackendsHealth,\n type BackendHealth,\n} from \"#/hooks/query/use-backends-health\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport type { Backend } from \"#/api/backend-registry/types\";\n// Import the trigger helpers from the lightweight store, not the overlay\n// component, so the eagerly-mounted sidebar/backend-selector graph does not\n// pull in the overlay's render code (the overlay is lazy-loaded from\n// `routes/root-layout.tsx`).\nimport {\n ENVIRONMENT_SWITCH_SETACTIVE_DELAY_MS,\n triggerEnvironmentSwitch,\n} from \"#/components/features/backends/environment-switch-store\";\nimport { NavigationLink } from \"#/components/shared/navigation-link\";\nimport { StyledTooltip } from \"#/components/shared/buttons/styled-tooltip\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { AddBackendModal } from \"./add-backend-modal\";\nimport { BackendStatusDot } from \"./backend-status-dot\";\nimport { ManageBackendsModal } from \"./manage-backends-modal\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlTransitionClassName } from \"#/utils/form-control-classes\";\nimport {\n dropdownFooterActionClassName,\n dropdownMenuListClassName,\n dropdownMenuRowIconWrapperClassName,\n} from \"#/utils/dropdown-classes\";\n\nconst VALUE_SEPARATOR = \"::\";\n\nfunction makeOptionValue(backendId: string, orgId: string | null): string {\n return orgId ? `${backendId}${VALUE_SEPARATOR}${orgId}` : backendId;\n}\n\nfunction parseOptionValue(value: string): {\n backendId: string;\n orgId: string | null;\n} {\n const [backendId, orgId] = value.split(VALUE_SEPARATOR);\n return { backendId, orgId: orgId ?? null };\n}\n\nfunction buildStatusPrefix(health: BackendHealth | undefined) {\n return <BackendStatusDot isConnected={health?.isConnected ?? null} />;\n}\n\nfunction buildNoBackendPrefix() {\n return <BackendStatusDot isConnected=\"unavailable\" />;\n}\n\nfunction buildOptions(\n registered: Backend[],\n personalWorkspaceLabel: string,\n cloudOrgs: ReturnType<typeof useAllCloudOrganizations>,\n currentUserIds: ReturnType<typeof useCloudCurrentUserId>,\n healthByBackendId: Record<string, BackendHealth>,\n): DropdownOption[] {\n const options: DropdownOption[] = [];\n\n const locals = registered.filter((b) => b.kind === \"local\");\n const clouds = registered.filter((b) => b.kind === \"cloud\");\n\n for (const b of locals) {\n options.push({\n value: makeOptionValue(b.id, null),\n label: b.name,\n prefix: buildStatusPrefix(healthByBackendId[b.id]),\n });\n }\n\n for (const b of clouds) {\n const entry = cloudOrgs[b.id];\n const prefix = buildStatusPrefix(healthByBackendId[b.id]);\n if (!entry || entry.orgs.length === 0) {\n options.push({\n value: makeOptionValue(b.id, null),\n label: b.name,\n prefix,\n });\n } else {\n // Personal-workspace rule (per the cloud contract): the org whose\n // id matches the calling user's id is the user's personal\n // workspace. We resolve `user_id` once per backend (via /me on any\n // one org) and apply it across all orgs of that backend.\n const userIdForBackend = currentUserIds[b.id]?.userId ?? null;\n\n for (const org of entry.orgs) {\n const isPersonal = !!userIdForBackend && userIdForBackend === org.id;\n const orgLabel = isPersonal ? personalWorkspaceLabel : org.name;\n options.push({\n value: makeOptionValue(b.id, org.id),\n label: `${b.name} – ${orgLabel}`,\n // All org rows for the same cloud backend share that backend's\n // single connectivity verdict — there is no per-org probe.\n prefix,\n });\n }\n }\n }\n\n return options;\n}\n\ninterface BackendSelectorProps {\n /** Render the menu above the trigger (e.g. when pinned to bottom of sidebar). */\n openUpward?: boolean;\n /** Hide the selector input trigger and only render the dropdown menu. */\n hideTrigger?: boolean;\n /** Whether the dropdown menu should start open on mount. */\n defaultOpen?: boolean;\n /** Callback fired after selecting a backend/org option. */\n onSelectOption?: () => void;\n /**\n * Override the internal Add Backend modal handling. When provided,\n * clicking \"Add Backend\" calls this instead of opening BackendSelector's\n * own modal. Useful when the selector is mounted inside an ephemeral\n * container (e.g. the collapsed-sidebar popover) and the modal must\n * survive the parent unmounting.\n */\n onOpenAddBackend?: () => void;\n /** Same as onOpenAddBackend but for the Manage Backends modal. */\n onOpenManageBackends?: () => void;\n /**\n * Whether the surrounding sidebar rail is in its collapsed variant. Passed\n * down from `SidebarRailBody` so the mobile drawer (which always renders\n * the expanded rail) can override the persisted desktop value.\n */\n sidebarCollapsed?: boolean;\n}\n\nexport function BackendSelector({\n openUpward = false,\n hideTrigger = false,\n defaultOpen = false,\n onSelectOption,\n onOpenAddBackend,\n onOpenManageBackends,\n sidebarCollapsed = false,\n}: BackendSelectorProps = {}) {\n const { t } = useTranslation(\"openhands\");\n const { backends, active, setActive } = useActiveBackendContext();\n const cloudOrgs = useAllCloudOrganizations();\n const currentUserIds = useCloudCurrentUserId();\n // Probe each registered backend every 10s.\n const healthByBackendId = useBackendsHealth(backends);\n const navigate = useNavigate();\n const settingsMatch = useMatch(\"/settings\");\n const settingsSubrouteMatch = useMatch(\"/settings/*\");\n const conversationMatch = useMatch(\"/conversations/:conversationId\");\n const automationDetailMatch = useMatch(\"/automations/:automationId\");\n const [addBackendModalOpen, setAddBackendModalOpen] = React.useState(false);\n const [manageBackendsModalOpen, setManageBackendsModalOpen] =\n React.useState(false);\n\n const personalWorkspaceLabel = t(I18nKey.BACKEND$PERSONAL_WORKSPACE);\n\n const options = React.useMemo(\n () =>\n buildOptions(\n backends,\n personalWorkspaceLabel,\n cloudOrgs,\n currentUserIds,\n healthByBackendId,\n ),\n [\n backends,\n personalWorkspaceLabel,\n cloudOrgs,\n currentUserIds,\n healthByBackendId,\n ],\n );\n\n const noBackendSelected = isNoBackend(active.backend);\n const noBackendLabel = t(I18nKey.BACKEND$NO_BACKEND_AVAILABLE);\n const activeValue = makeOptionValue(active.backend.id, active.orgId);\n const activeOption = noBackendSelected\n ? undefined\n : options.find((o) => o.value === activeValue);\n const isSettingsActive = Boolean(settingsMatch || settingsSubrouteMatch);\n const settingsLabel = t(I18nKey.SIDEBAR$SETTINGS);\n const isRightPanelShown = useConversationStore(\n (state) => state.isRightPanelShown,\n );\n // When the sidebar rail is expanded, `placement=\"left\"` hugs the main\n // canvas and reads awkwardly; prefer above the control. When the rail is\n // collapsed, keep left except on active conversation + open right drawer.\n const settingsTooltipPlacement =\n !sidebarCollapsed || (conversationMatch && isRightPanelShown)\n ? \"top\"\n : \"left\";\n\n const someCloudLoading = Object.values(cloudOrgs).some((c) => c.isLoading);\n\n // Self-heal a malformed `(cloudBackendId, null)` selection.\n //\n // Once a cloud backend's orgs resolve, the dropdown only renders\n // per-org rows for it — the `(backendId, null)` row disappears, so\n // selecting that shape would drift from what the dropdown can render\n // (UI says \"Local\", APIs hit cloud). When we detect the drift, snap\n // the selection onto the personal-workspace org (or, lacking a /me\n // result, the first org). The selection is recorded locally only;\n // the cloud request scope follows from the API key's bound org and the\n // X-Org-Id header sent by `callCloudProxy`, so the cloud UI's\n // org choice is never mutated as a side effect.\n React.useEffect(() => {\n if (noBackendSelected || active.backend.kind !== \"cloud\" || active.orgId)\n return;\n const { backend } = active;\n const entry = cloudOrgs[backend.id];\n if (!entry || entry.orgs.length === 0) return;\n\n const userId = currentUserIds[backend.id]?.userId ?? null;\n const personal = userId\n ? entry.orgs.find((o) => o.id === userId)\n : undefined;\n const target = personal ?? entry.orgs[0];\n if (target) {\n setActive(backend.id, target.id);\n }\n }, [active, cloudOrgs, currentUserIds, setActive, noBackendSelected]);\n\n const openAddBackendModal = React.useCallback(() => {\n if (onOpenAddBackend) {\n onOpenAddBackend();\n onSelectOption?.();\n return;\n }\n setAddBackendModalOpen(true);\n }, [onOpenAddBackend, onSelectOption]);\n\n const openManageBackendsModal = React.useCallback(() => {\n if (onOpenManageBackends) {\n onOpenManageBackends();\n onSelectOption?.();\n return;\n }\n setManageBackendsModalOpen(true);\n }, [onOpenManageBackends, onSelectOption]);\n\n const preventDropdownMenuClose = React.useCallback(\n (event: React.SyntheticEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n },\n [],\n );\n\n const handleAddBackendClick = React.useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n preventDropdownMenuClose(event);\n openAddBackendModal();\n },\n [openAddBackendModal, preventDropdownMenuClose],\n );\n\n const handleAddBackendTouchEnd = React.useCallback(\n (event: React.TouchEvent<HTMLButtonElement>) => {\n preventDropdownMenuClose(event);\n openAddBackendModal();\n },\n [openAddBackendModal, preventDropdownMenuClose],\n );\n\n const handleManageBackendsClick = React.useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n preventDropdownMenuClose(event);\n openManageBackendsModal();\n },\n [openManageBackendsModal, preventDropdownMenuClose],\n );\n\n const handleManageBackendsTouchEnd = React.useCallback(\n (event: React.TouchEvent<HTMLButtonElement>) => {\n preventDropdownMenuClose(event);\n openManageBackendsModal();\n },\n [openManageBackendsModal, preventDropdownMenuClose],\n );\n\n const addBackendFooter = (\n <div className={dropdownMenuListClassName}>\n <button\n type=\"button\"\n data-testid=\"add-backend-menu-item\"\n onMouseDown={preventDropdownMenuClose}\n onTouchStart={preventDropdownMenuClose}\n onTouchEnd={handleAddBackendTouchEnd}\n onClick={handleAddBackendClick}\n className={cn(\n dropdownFooterActionClassName,\n \"cursor-pointer rounded-md\",\n )}\n >\n <span className={dropdownMenuRowIconWrapperClassName} aria-hidden>\n <Plus width={16} height={16} />\n </span>\n {t(I18nKey.BACKEND$ADD)}\n </button>\n <button\n type=\"button\"\n data-testid=\"manage-backends-menu-item\"\n onMouseDown={preventDropdownMenuClose}\n onTouchStart={preventDropdownMenuClose}\n onTouchEnd={handleManageBackendsTouchEnd}\n onClick={handleManageBackendsClick}\n className={cn(\n dropdownFooterActionClassName,\n \"cursor-pointer rounded-md\",\n )}\n >\n <span className={dropdownMenuRowIconWrapperClassName} aria-hidden>\n <Settings width={16} height={16} />\n </span>\n {t(I18nKey.BACKEND$MANAGE)}\n </button>\n </div>\n );\n\n const handleSelectBackend = React.useCallback(\n async (value: string) => {\n if (value === activeValue) return;\n\n const { backendId, orgId } = parseOptionValue(value);\n const target = backends.find((b) => b.id === backendId);\n if (!target) return;\n\n triggerEnvironmentSwitch(\n options.find((option) => option.value === value)?.label ?? target.name,\n );\n await new Promise<void>((resolve) => {\n setTimeout(resolve, ENVIRONMENT_SWITCH_SETACTIVE_DELAY_MS);\n });\n\n // @spec BM-002 — Switching backends keeps the user on the same page\n if (conversationMatch) navigate(\"/conversations\");\n else if (automationDetailMatch) navigate(\"/automations\");\n\n setActive(target.id, orgId);\n onSelectOption?.();\n },\n [\n activeValue,\n backends,\n conversationMatch,\n automationDetailMatch,\n navigate,\n options,\n setActive,\n t,\n onSelectOption,\n ],\n );\n\n return (\n <>\n <div className=\"flex items-center gap-2 w-full\">\n <div className=\"flex-1 min-w-0\">\n <Dropdown\n testId=\"backend-selector\"\n key={`${activeValue}-${activeOption?.label ?? \"\"}`}\n defaultValue={\n activeOption ?? {\n value: activeValue,\n label: noBackendSelected ? noBackendLabel : active.backend.name,\n prefix: noBackendSelected\n ? buildNoBackendPrefix()\n : buildStatusPrefix(healthByBackendId[active.backend.id]),\n }\n }\n footer={addBackendFooter}\n openUpward={openUpward}\n hideTrigger={hideTrigger}\n defaultOpen={defaultOpen}\n openOnHover={!hideTrigger}\n onChange={(item) => {\n if (!item) return;\n void handleSelectBackend(item.value);\n }}\n placeholder={\n noBackendSelected ? noBackendLabel : active.backend.name\n }\n loading={someCloudLoading}\n options={options}\n className=\"h-10 px-2 py-0 bg-transparent border-transparent hover:bg-[var(--oh-surface-raised)] focus-within:bg-[var(--oh-surface-raised)] focus-within:border-transparent focus-within:ring-0\"\n />\n </div>\n {!hideTrigger ? (\n <StyledTooltip\n content={settingsLabel}\n placement={settingsTooltipPlacement}\n offset={10}\n >\n <NavigationLink\n to=\"/settings\"\n data-testid=\"backend-selector-settings-link\"\n data-active={isSettingsActive}\n aria-label={settingsLabel}\n className={\n isSettingsActive\n ? cn(\n \"inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-md bg-tertiary text-white font-normal cursor-pointer\",\n formControlTransitionClassName,\n )\n : cn(\n \"inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-md text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)] cursor-pointer\",\n formControlTransitionClassName,\n )\n }\n >\n <Settings width={16} height={16} />\n </NavigationLink>\n </StyledTooltip>\n ) : null}\n </div>\n {addBackendModalOpen ? (\n <AddBackendModal onClose={() => setAddBackendModalOpen(false)} />\n ) : null}\n {manageBackendsModalOpen ? (\n <ManageBackendsModal\n onClose={() => setManageBackendsModalOpen(false)}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"backend-selector.js","names":[],"sources":["../../../../src/components/features/backends/backend-selector.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useMatch, useNavigate } from \"react-router\";\nimport { Plus, Settings } from \"lucide-react\";\nimport { Dropdown } from \"#/ui/dropdown/dropdown\";\nimport { DropdownOption } from \"#/ui/dropdown/types\";\nimport { isNoBackend } from \"#/api/backend-registry/active-store\";\nimport { useActiveBackendContext } from \"#/contexts/active-backend-context\";\nimport { useAllCloudOrganizations } from \"#/hooks/query/use-cloud-organizations\";\nimport { useCloudCurrentUserId } from \"#/hooks/query/use-cloud-current-user-id\";\nimport {\n useBackendsHealth,\n type BackendHealth,\n} from \"#/hooks/query/use-backends-health\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport type { Backend } from \"#/api/backend-registry/types\";\n// Import the trigger helpers from the lightweight store, not the overlay\n// component, so the eagerly-mounted sidebar/backend-selector graph does not\n// pull in the overlay's render code (the overlay is lazy-loaded from\n// `routes/root-layout.tsx`).\nimport {\n ENVIRONMENT_SWITCH_SETACTIVE_DELAY_MS,\n triggerEnvironmentSwitch,\n} from \"#/components/features/backends/environment-switch-store\";\nimport { NavigationLink } from \"#/components/shared/navigation-link\";\nimport { StyledTooltip } from \"#/components/shared/buttons/styled-tooltip\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { AddBackendModal } from \"./add-backend-modal\";\nimport { BackendStatusDot } from \"./backend-status-dot\";\nimport { ManageBackendsModal } from \"./manage-backends-modal\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlTransitionClassName } from \"#/utils/form-control-classes\";\nimport {\n dropdownFooterActionClassName,\n dropdownMenuListClassName,\n dropdownMenuRowIconWrapperClassName,\n} from \"#/utils/dropdown-classes\";\n\nconst VALUE_SEPARATOR = \"::\";\n\nfunction makeOptionValue(backendId: string, orgId: string | null): string {\n return orgId ? `${backendId}${VALUE_SEPARATOR}${orgId}` : backendId;\n}\n\nfunction parseOptionValue(value: string): {\n backendId: string;\n orgId: string | null;\n} {\n const [backendId, orgId] = value.split(VALUE_SEPARATOR);\n return { backendId, orgId: orgId ?? null };\n}\n\nfunction buildStatusPrefix(health: BackendHealth | undefined) {\n return <BackendStatusDot isConnected={health?.isConnected ?? null} />;\n}\n\nfunction buildNoBackendPrefix() {\n return <BackendStatusDot isConnected=\"unavailable\" />;\n}\n\nfunction buildOptions(\n registered: Backend[],\n personalWorkspaceLabel: string,\n cloudOrgs: ReturnType<typeof useAllCloudOrganizations>,\n currentUserIds: ReturnType<typeof useCloudCurrentUserId>,\n healthByBackendId: Record<string, BackendHealth>,\n): DropdownOption[] {\n const options: DropdownOption[] = [];\n\n const locals = registered.filter((b) => b.kind === \"local\");\n const clouds = registered.filter((b) => b.kind === \"cloud\");\n\n for (const b of locals) {\n options.push({\n value: makeOptionValue(b.id, null),\n label: b.name,\n prefix: buildStatusPrefix(healthByBackendId[b.id]),\n });\n }\n\n for (const b of clouds) {\n const entry = cloudOrgs[b.id];\n const prefix = buildStatusPrefix(healthByBackendId[b.id]);\n if (!entry || entry.orgs.length === 0) {\n options.push({\n value: makeOptionValue(b.id, null),\n label: b.name,\n prefix,\n });\n } else {\n // Personal-workspace rule (per the cloud contract): the org whose\n // id matches the calling user's id is the user's personal\n // workspace. We resolve `user_id` once per backend (via /me on any\n // one org) and apply it across all orgs of that backend.\n const userIdForBackend = currentUserIds[b.id]?.userId ?? null;\n\n for (const org of entry.orgs) {\n const isPersonal = !!userIdForBackend && userIdForBackend === org.id;\n const orgLabel = isPersonal ? personalWorkspaceLabel : org.name;\n options.push({\n value: makeOptionValue(b.id, org.id),\n label: `${b.name} – ${orgLabel}`,\n // All org rows for the same cloud backend share that backend's\n // single connectivity verdict — there is no per-org probe.\n prefix,\n });\n }\n }\n }\n\n return options;\n}\n\ninterface BackendSelectorProps {\n /** Render the menu above the trigger (e.g. when pinned to bottom of sidebar). */\n openUpward?: boolean;\n /** Hide the selector input trigger and only render the dropdown menu. */\n hideTrigger?: boolean;\n /** Whether the dropdown menu should start open on mount. */\n defaultOpen?: boolean;\n /** Callback fired after selecting a backend/org option. */\n onSelectOption?: () => void;\n /**\n * Override the internal Add Backend modal handling. When provided,\n * clicking \"Add Backend\" calls this instead of opening BackendSelector's\n * own modal. Useful when the selector is mounted inside an ephemeral\n * container (e.g. the collapsed-sidebar popover) and the modal must\n * survive the parent unmounting.\n */\n onOpenAddBackend?: () => void;\n /** Same as onOpenAddBackend but for the Manage Backends modal. */\n onOpenManageBackends?: () => void;\n /**\n * Whether the surrounding sidebar rail is in its collapsed variant. Passed\n * down from `SidebarRailBody` so the mobile drawer (which always renders\n * the expanded rail) can override the persisted desktop value.\n */\n sidebarCollapsed?: boolean;\n}\n\nexport function BackendSelector({\n openUpward = false,\n hideTrigger = false,\n defaultOpen = false,\n onSelectOption,\n onOpenAddBackend,\n onOpenManageBackends,\n sidebarCollapsed = false,\n}: BackendSelectorProps = {}) {\n const { t } = useTranslation(\"openhands\");\n const { backends, active, setActive } = useActiveBackendContext();\n const cloudOrgs = useAllCloudOrganizations();\n const currentUserIds = useCloudCurrentUserId();\n // Probe each registered backend every 10s.\n const healthByBackendId = useBackendsHealth(backends);\n const navigate = useNavigate();\n const settingsMatch = useMatch(\"/settings\");\n const settingsSubrouteMatch = useMatch(\"/settings/*\");\n const conversationMatch = useMatch(\"/conversations/:conversationId\");\n const automationDetailMatch = useMatch(\"/automations/:automationId\");\n const [addBackendModalOpen, setAddBackendModalOpen] = React.useState(false);\n const [manageBackendsModalOpen, setManageBackendsModalOpen] =\n React.useState(false);\n\n const personalWorkspaceLabel = t(I18nKey.BACKEND$PERSONAL_WORKSPACE);\n\n const options = React.useMemo(\n () =>\n buildOptions(\n backends,\n personalWorkspaceLabel,\n cloudOrgs,\n currentUserIds,\n healthByBackendId,\n ),\n [\n backends,\n personalWorkspaceLabel,\n cloudOrgs,\n currentUserIds,\n healthByBackendId,\n ],\n );\n\n const noBackendSelected = isNoBackend(active.backend);\n const noBackendLabel = t(I18nKey.BACKEND$NO_BACKEND_AVAILABLE);\n const activeValue = makeOptionValue(active.backend.id, active.orgId);\n const activeOption = noBackendSelected\n ? undefined\n : options.find((o) => o.value === activeValue);\n const isSettingsActive = Boolean(settingsMatch || settingsSubrouteMatch);\n const settingsLabel = t(I18nKey.SIDEBAR$SETTINGS);\n const isRightPanelShown = useConversationStore(\n (state) => state.isRightPanelShown,\n );\n // When the sidebar rail is expanded, `placement=\"left\"` hugs the main\n // canvas and reads awkwardly; prefer above the control. When the rail is\n // collapsed, keep left except on active conversation + open right drawer.\n const settingsTooltipPlacement =\n !sidebarCollapsed || (conversationMatch && isRightPanelShown)\n ? \"top\"\n : \"left\";\n\n const someCloudLoading = Object.values(cloudOrgs).some((c) => c.isLoading);\n\n // Self-heal a malformed `(cloudBackendId, null)` selection.\n //\n // Once a cloud backend's orgs resolve, the dropdown only renders\n // per-org rows for it — the `(backendId, null)` row disappears, so\n // selecting that shape would drift from what the dropdown can render\n // (UI says \"Local\", APIs hit cloud). When we detect the drift, snap\n // the selection onto Cloud's current org first, then fall back to the\n // personal workspace (or, lacking a /me result, the first org). The\n // selection is recorded locally only; the cloud request scope follows\n // from the X-Org-Id header sent by `callCloudProxy`, so the cloud UI's\n // org choice is never mutated as a side effect.\n React.useEffect(() => {\n if (noBackendSelected || active.backend.kind !== \"cloud\" || active.orgId)\n return;\n const { backend } = active;\n const entry = cloudOrgs[backend.id];\n if (!entry || entry.orgs.length === 0) return;\n\n const currentOrg = entry.currentOrgId\n ? entry.orgs.find((o) => o.id === entry.currentOrgId)\n : undefined;\n const userId = currentUserIds[backend.id]?.userId ?? null;\n const personal = userId\n ? entry.orgs.find((o) => o.id === userId)\n : undefined;\n const target = currentOrg ?? personal ?? entry.orgs[0];\n if (target) {\n setActive(backend.id, target.id);\n }\n }, [active, cloudOrgs, currentUserIds, setActive, noBackendSelected]);\n\n const openAddBackendModal = React.useCallback(() => {\n if (onOpenAddBackend) {\n onOpenAddBackend();\n onSelectOption?.();\n return;\n }\n setAddBackendModalOpen(true);\n }, [onOpenAddBackend, onSelectOption]);\n\n const openManageBackendsModal = React.useCallback(() => {\n if (onOpenManageBackends) {\n onOpenManageBackends();\n onSelectOption?.();\n return;\n }\n setManageBackendsModalOpen(true);\n }, [onOpenManageBackends, onSelectOption]);\n\n const preventDropdownMenuClose = React.useCallback(\n (event: React.SyntheticEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n },\n [],\n );\n\n const handleAddBackendClick = React.useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n preventDropdownMenuClose(event);\n openAddBackendModal();\n },\n [openAddBackendModal, preventDropdownMenuClose],\n );\n\n const handleAddBackendTouchEnd = React.useCallback(\n (event: React.TouchEvent<HTMLButtonElement>) => {\n preventDropdownMenuClose(event);\n openAddBackendModal();\n },\n [openAddBackendModal, preventDropdownMenuClose],\n );\n\n const handleManageBackendsClick = React.useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n preventDropdownMenuClose(event);\n openManageBackendsModal();\n },\n [openManageBackendsModal, preventDropdownMenuClose],\n );\n\n const handleManageBackendsTouchEnd = React.useCallback(\n (event: React.TouchEvent<HTMLButtonElement>) => {\n preventDropdownMenuClose(event);\n openManageBackendsModal();\n },\n [openManageBackendsModal, preventDropdownMenuClose],\n );\n\n const addBackendFooter = (\n <div className={dropdownMenuListClassName}>\n <button\n type=\"button\"\n data-testid=\"add-backend-menu-item\"\n onMouseDown={preventDropdownMenuClose}\n onTouchStart={preventDropdownMenuClose}\n onTouchEnd={handleAddBackendTouchEnd}\n onClick={handleAddBackendClick}\n className={cn(\n dropdownFooterActionClassName,\n \"cursor-pointer rounded-md\",\n )}\n >\n <span className={dropdownMenuRowIconWrapperClassName} aria-hidden>\n <Plus width={16} height={16} />\n </span>\n {t(I18nKey.BACKEND$ADD)}\n </button>\n <button\n type=\"button\"\n data-testid=\"manage-backends-menu-item\"\n onMouseDown={preventDropdownMenuClose}\n onTouchStart={preventDropdownMenuClose}\n onTouchEnd={handleManageBackendsTouchEnd}\n onClick={handleManageBackendsClick}\n className={cn(\n dropdownFooterActionClassName,\n \"cursor-pointer rounded-md\",\n )}\n >\n <span className={dropdownMenuRowIconWrapperClassName} aria-hidden>\n <Settings width={16} height={16} />\n </span>\n {t(I18nKey.BACKEND$MANAGE)}\n </button>\n </div>\n );\n\n const handleSelectBackend = React.useCallback(\n async (value: string) => {\n if (value === activeValue) return;\n\n const { backendId, orgId } = parseOptionValue(value);\n const target = backends.find((b) => b.id === backendId);\n if (!target) return;\n\n triggerEnvironmentSwitch(\n options.find((option) => option.value === value)?.label ?? target.name,\n );\n await new Promise<void>((resolve) => {\n setTimeout(resolve, ENVIRONMENT_SWITCH_SETACTIVE_DELAY_MS);\n });\n\n // @spec BM-002 — Switching backends keeps the user on the same page\n if (conversationMatch) navigate(\"/conversations\");\n else if (automationDetailMatch) navigate(\"/automations\");\n\n setActive(target.id, orgId);\n onSelectOption?.();\n },\n [\n activeValue,\n backends,\n conversationMatch,\n automationDetailMatch,\n navigate,\n options,\n setActive,\n t,\n onSelectOption,\n ],\n );\n\n return (\n <>\n <div className=\"flex items-center gap-2 w-full\">\n <div className=\"flex-1 min-w-0\">\n <Dropdown\n testId=\"backend-selector\"\n key={`${activeValue}-${activeOption?.label ?? \"\"}`}\n defaultValue={\n activeOption ?? {\n value: activeValue,\n label: noBackendSelected ? noBackendLabel : active.backend.name,\n prefix: noBackendSelected\n ? buildNoBackendPrefix()\n : buildStatusPrefix(healthByBackendId[active.backend.id]),\n }\n }\n footer={addBackendFooter}\n openUpward={openUpward}\n hideTrigger={hideTrigger}\n defaultOpen={defaultOpen}\n openOnHover={!hideTrigger}\n onChange={(item) => {\n if (!item) return;\n void handleSelectBackend(item.value);\n }}\n placeholder={\n noBackendSelected ? noBackendLabel : active.backend.name\n }\n loading={someCloudLoading}\n options={options}\n className=\"h-10 px-2 py-0 bg-transparent border-transparent hover:bg-[var(--oh-surface-raised)] focus-within:bg-[var(--oh-surface-raised)] focus-within:border-transparent focus-within:ring-0\"\n />\n </div>\n {!hideTrigger ? (\n <StyledTooltip\n content={settingsLabel}\n placement={settingsTooltipPlacement}\n offset={10}\n >\n <NavigationLink\n to=\"/settings\"\n data-testid=\"backend-selector-settings-link\"\n data-active={isSettingsActive}\n aria-label={settingsLabel}\n className={\n isSettingsActive\n ? cn(\n \"inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-md bg-tertiary text-white font-normal cursor-pointer\",\n formControlTransitionClassName,\n )\n : cn(\n \"inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-md text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)] cursor-pointer\",\n formControlTransitionClassName,\n )\n }\n >\n <Settings width={16} height={16} />\n </NavigationLink>\n </StyledTooltip>\n ) : null}\n </div>\n {addBackendModalOpen ? (\n <AddBackendModal onClose={() => setAddBackendModalOpen(false)} />\n ) : null}\n {manageBackendsModalOpen ? (\n <ManageBackendsModal\n onClose={() => setManageBackendsModalOpen(false)}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../utils/utils.cjs`),i=require(`../../shared/modals/modal-backdrop.cjs`),a=require(`../../shared/modals/modal-body.cjs`),o=require(`../settings/brand-button.cjs`),s=require(`../../../hooks/use-device-flow.cjs`);let c=require(`react`);c=e.__toESM(c,1);let l=require(`react/jsx-runtime`);function u(e){try{return new URL(e).protocol===`https:`}catch{return!1}}function d({host:e,onSuccess:d,testIdRoot:p,isDisabled:m=!1,idleButtonLabel:h,idleButtonContent:g,className:_,buttonClassName:v,buttonVariant:y=`primary`,statusDisplay:b=`inline`}){let{t:x}=t.useTranslation(`openhands`),S=s.useDeviceFlow(),C=c.default.useRef(null);c.default.useEffect(()=>()=>{C.current?.close()},[]),c.default.useEffect(()=>{if(S.status===`awaiting_authorization`&&S.verificationUrl&&C.current&&!C.current.closed){if(!u(S.verificationUrl)){console.error(`Invalid verification URL protocol`);return}try{C.current.location.href=S.verificationUrl}catch{C.current=window.open(S.verificationUrl,`_blank`,`noopener,noreferrer`)}}},[S.status,S.verificationUrl]),c.default.useEffect(()=>{if(S.status===`success`&&S.apiKey)try{d(S.apiKey)}finally{S.reset(),C.current?.close()}},[S.status,S.apiKey,S.reset,d]);let w=()=>{let t=e.trim().replace(/\/+$/,``),n=/^https?:\/\//i.test(t)?t:`https://${t}`;try{let e=new URL(n);if(e.username||e.password)throw Error(`Invalid URL format`)}catch{return}C.current=window.open(`about:blank`,`_blank`),C.current||console.warn(`Popup blocked - user will need to use manual link`),S.start(n)},T=()=>{S.cancel(),C.current?.close()},E=(0,l.jsx)(f,{status:S.status,error:S.error,verificationUrl:S.verificationUrl,testIdRoot:p,onCancel:T,onRetry:w}),D=b===`modal`&&S.status!==`idle`,O=h??x(n.I18nKey.BACKEND$LOGIN_WITH_OPENHANDS);return(0,l.jsxs)(`div`,{"data-testid":`${p}-device-flow`,className:r.cn(`flex flex-col gap-3`,_),children:[S.status===`idle`&&y===`unstyled`&&(0,l.jsx)(`button`,{type:`button`,onClick:w,"data-testid":`${p}-login-button`,className:v,disabled:m,"aria-label":g?O:void 0,children:g??O}),S.status===`idle`&&y!==`unstyled`&&(0,l.jsx)(o.BrandButton,{type:`button`,variant:y,onClick:w,testId:`${p}-login-button`,className:r.cn(`w-full`,v),isDisabled:m,ariaLabel:g?O:void 0,children:g??O}),b===`inline`?E:null,D?(0,l.jsx)(i.ModalBackdrop,{onClose:T,"aria-label":x(n.I18nKey.BACKEND$LOGIN_WITH_OPENHANDS),closeOnBackdropClick:!1,children:(0,l.jsx)(a.ModalBody,{testID:`${p}-auth-modal`,width:`sm`,className:`items-stretch border border-[var(--oh-border)]`,children:E})}):null]})}function f({status:e,error:r,verificationUrl:i,testIdRoot:a,onCancel:s,onRetry:c}){let{t:d}=t.useTranslation(`openhands`);if(e===`idle`||e===`success`)return null;if(e===`starting`)return(0,l.jsxs)(`div`,{className:`flex items-center gap-2 rounded-lg bg-base-tertiary p-3`,"data-testid":`${a}-auth-starting`,role:`status`,"aria-live":`polite`,children:[(0,l.jsx)(p,{}),(0,l.jsx)(`span`,{className:`text-sm text-[var(--oh-text-tertiary)]`,children:d(n.I18nKey.BACKEND$AUTH_STARTING)})]});if(e===`awaiting_authorization`){let e=i&&u(i)?i:null;return(0,l.jsxs)(`div`,{className:`flex flex-col gap-4 rounded-lg bg-base-tertiary p-4`,"data-testid":`${a}-auth-awaiting`,role:`status`,"aria-live":`polite`,children:[(0,l.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,l.jsx)(p,{}),(0,l.jsx)(`span`,{className:`text-sm font-medium text-white`,children:d(n.I18nKey.BACKEND$AUTH_AWAITING)})]}),(0,l.jsx)(`p`,{className:`text-sm leading-5 text-[var(--oh-text-tertiary)]`,children:d(n.I18nKey.BACKEND$AUTH_BROWSER_OPENED)}),e?(0,l.jsxs)(`div`,{className:`flex flex-col gap-1 rounded-md border border-[var(--oh-border)] bg-[var(--oh-surface)] p-3 text-xs text-[var(--oh-muted)]`,children:[(0,l.jsx)(`p`,{children:d(n.I18nKey.BACKEND$AUTH_OPEN_MANUALLY)}),(0,l.jsx)(`a`,{href:e,target:`_blank`,rel:`noopener noreferrer`,className:`max-h-16 overflow-auto break-all text-blue-400 hover:underline`,children:e})]}):null,(0,l.jsx)(o.BrandButton,{type:`button`,variant:`secondary`,onClick:s,testId:`${a}-auth-cancel`,className:`w-full`,children:d(n.I18nKey.BACKEND$AUTH_CANCEL)})]})}return(0,l.jsxs)(`div`,{className:`flex flex-col gap-3 rounded-lg border border-red-700 bg-red-900/20 p-4`,"data-testid":`${a}-auth-error`,role:`alert`,children:[(0,l.jsx)(`p`,{className:`text-sm text-red-400`,children:r}),(0,l.jsx)(o.BrandButton,{type:`button`,variant:`secondary`,onClick:c,testId:`${a}-auth-retry`,className:`w-full`,children:d(n.I18nKey.BACKEND$AUTH_RETRY)})]})}function p(){return(0,l.jsx)(`svg`,{className:`animate-spin h-4 w-4 text-white`,xmlns:`http://www.w3.org/2000/svg`,fill:`none`,viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,l.jsx)(`path`,{className:`opacity-75`,fill:`currentColor`,d:`M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z`})})}exports.DeviceFlowAuth=d;
1
+ const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../utils/utils.cjs`),i=require(`../../shared/modals/modal-backdrop.cjs`),a=require(`../../shared/modals/modal-body.cjs`),o=require(`../settings/brand-button.cjs`),s=require(`../../../hooks/use-device-flow.cjs`);let c=require(`react`);c=e.__toESM(c,1);let l=require(`react/jsx-runtime`);function u(e){try{return new URL(e).protocol===`https:`}catch{return!1}}function d({host:e,onSuccess:d,testIdRoot:p,isDisabled:m=!1,idleButtonLabel:h,idleButtonContent:g,className:_,buttonClassName:v,buttonVariant:y=`primary`,statusDisplay:b=`inline`,analyticsSource:x}){let{t:S}=t.useTranslation(`openhands`),C=s.useDeviceFlow(),w=c.default.useRef(null);c.default.useEffect(()=>()=>{w.current?.close()},[]),c.default.useEffect(()=>{if(C.status===`awaiting_authorization`&&C.verificationUrl&&w.current&&!w.current.closed){if(!u(C.verificationUrl)){console.error(`Invalid verification URL protocol`);return}try{w.current.location.href=C.verificationUrl}catch{w.current=window.open(C.verificationUrl,`_blank`,`noopener,noreferrer`)}}},[C.status,C.verificationUrl]),c.default.useEffect(()=>{if(C.status===`success`&&C.apiKey)try{d(C.apiKey)}finally{C.reset(),w.current?.close()}},[C.status,C.apiKey,C.reset,d]);let T=()=>{let t=e.trim().replace(/\/+$/,``),n=/^https?:\/\//i.test(t)?t:`https://${t}`;try{let e=new URL(n);if(e.username||e.password)throw Error(`Invalid URL format`)}catch{return}w.current=window.open(`about:blank`,`_blank`),w.current||console.warn(`Popup blocked - user will need to use manual link`),C.start(n,x)},E=()=>{C.cancel(),w.current?.close()},D=(0,l.jsx)(f,{status:C.status,error:C.error,verificationUrl:C.verificationUrl,testIdRoot:p,onCancel:E,onRetry:T}),O=b===`modal`&&C.status!==`idle`,k=h??S(n.I18nKey.BACKEND$LOGIN_WITH_OPENHANDS);return(0,l.jsxs)(`div`,{"data-testid":`${p}-device-flow`,className:r.cn(`flex flex-col gap-3`,_),children:[C.status===`idle`&&y===`unstyled`&&(0,l.jsx)(`button`,{type:`button`,onClick:T,"data-testid":`${p}-login-button`,className:v,disabled:m,"aria-label":g?k:void 0,children:g??k}),C.status===`idle`&&y!==`unstyled`&&(0,l.jsx)(o.BrandButton,{type:`button`,variant:y,onClick:T,testId:`${p}-login-button`,className:r.cn(`w-full`,v),isDisabled:m,ariaLabel:g?k:void 0,children:g??k}),b===`inline`?D:null,O?(0,l.jsx)(i.ModalBackdrop,{onClose:E,"aria-label":S(n.I18nKey.BACKEND$LOGIN_WITH_OPENHANDS),closeOnBackdropClick:!1,children:(0,l.jsx)(a.ModalBody,{testID:`${p}-auth-modal`,width:`sm`,className:`items-stretch border border-[var(--oh-border)]`,children:D})}):null]})}function f({status:e,error:r,verificationUrl:i,testIdRoot:a,onCancel:s,onRetry:c}){let{t:d}=t.useTranslation(`openhands`);if(e===`idle`||e===`success`)return null;if(e===`starting`)return(0,l.jsxs)(`div`,{className:`flex items-center gap-2 rounded-lg bg-base-tertiary p-3`,"data-testid":`${a}-auth-starting`,role:`status`,"aria-live":`polite`,children:[(0,l.jsx)(p,{}),(0,l.jsx)(`span`,{className:`text-sm text-[var(--oh-text-tertiary)]`,children:d(n.I18nKey.BACKEND$AUTH_STARTING)})]});if(e===`awaiting_authorization`){let e=i&&u(i)?i:null;return(0,l.jsxs)(`div`,{className:`flex flex-col gap-4 rounded-lg bg-base-tertiary p-4`,"data-testid":`${a}-auth-awaiting`,role:`status`,"aria-live":`polite`,children:[(0,l.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,l.jsx)(p,{}),(0,l.jsx)(`span`,{className:`text-sm font-medium text-white`,children:d(n.I18nKey.BACKEND$AUTH_AWAITING)})]}),(0,l.jsx)(`p`,{className:`text-sm leading-5 text-[var(--oh-text-tertiary)]`,children:d(n.I18nKey.BACKEND$AUTH_BROWSER_OPENED)}),e?(0,l.jsxs)(`div`,{className:`flex flex-col gap-1 rounded-md border border-[var(--oh-border)] bg-[var(--oh-surface)] p-3 text-xs text-[var(--oh-muted)]`,children:[(0,l.jsx)(`p`,{children:d(n.I18nKey.BACKEND$AUTH_OPEN_MANUALLY)}),(0,l.jsx)(`a`,{href:e,target:`_blank`,rel:`noopener noreferrer`,className:`max-h-16 overflow-auto break-all text-blue-400 hover:underline`,children:e})]}):null,(0,l.jsx)(o.BrandButton,{type:`button`,variant:`secondary`,onClick:s,testId:`${a}-auth-cancel`,className:`w-full`,children:d(n.I18nKey.BACKEND$AUTH_CANCEL)})]})}return(0,l.jsxs)(`div`,{className:`flex flex-col gap-3 rounded-lg border border-red-700 bg-red-900/20 p-4`,"data-testid":`${a}-auth-error`,role:`alert`,children:[(0,l.jsx)(`p`,{className:`text-sm text-red-400`,children:r}),(0,l.jsx)(o.BrandButton,{type:`button`,variant:`secondary`,onClick:c,testId:`${a}-auth-retry`,className:`w-full`,children:d(n.I18nKey.BACKEND$AUTH_RETRY)})]})}function p(){return(0,l.jsx)(`svg`,{className:`animate-spin h-4 w-4 text-white`,xmlns:`http://www.w3.org/2000/svg`,fill:`none`,viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,l.jsx)(`path`,{className:`opacity-75`,fill:`currentColor`,d:`M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z`})})}exports.DeviceFlowAuth=d;
2
2
  //# sourceMappingURL=device-flow-auth.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"device-flow-auth.cjs","names":[],"sources":["../../../../src/components/features/backends/device-flow-auth.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalBody } from \"#/components/shared/modals/modal-body\";\nimport { useDeviceFlow, type DeviceFlowStatus } from \"#/hooks/use-device-flow\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { cn } from \"#/utils/utils\";\n\ntype DeviceFlowButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"tertiary\"\n | \"unstyled\";\ntype DeviceFlowStatusDisplay = \"inline\" | \"modal\";\n\ninterface DeviceFlowAuthProps {\n /** The host URL for the cloud backend */\n host: string;\n /** Callback when authentication succeeds with the API key */\n onSuccess: (apiKey: string) => void;\n /** Test ID prefix for the component */\n testIdRoot: string;\n /** Whether the login button should be disabled (e.g., when no host is entered) */\n isDisabled?: boolean;\n /** Override for the idle button label and icon-only accessible name. */\n idleButtonLabel?: string;\n /** Optional visible content for the idle button. Defaults to the idle label. */\n idleButtonContent?: React.ReactNode;\n /** Optional classes for the root wrapper. */\n className?: string;\n /** Optional classes for the idle button. */\n buttonClassName?: string;\n /** Visual variant for the idle button. */\n buttonVariant?: DeviceFlowButtonVariant;\n /** Whether in-progress auth content should render inline or in a modal. */\n statusDisplay?: DeviceFlowStatusDisplay;\n}\n\n/**\n * Device Flow authentication UI component.\n *\n * Shows a \"Login with OpenHands Cloud\" button that initiates OAuth 2.0 Device Flow\n * authentication. Displays status during the auth process and auto-opens\n * the browser for user authorization.\n */\n/**\n * Validate that a URL is safe to open in a popup.\n * Prevents XSS via javascript: URLs or other malicious schemes.\n */\nfunction isValidVerificationUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n\nexport function DeviceFlowAuth({\n host,\n onSuccess,\n testIdRoot,\n isDisabled = false,\n idleButtonLabel,\n idleButtonContent,\n className,\n buttonClassName,\n buttonVariant = \"primary\",\n statusDisplay = \"inline\",\n}: DeviceFlowAuthProps) {\n const { t } = useTranslation(\"openhands\");\n const deviceFlow = useDeviceFlow();\n const popupRef = React.useRef<Window | null>(null);\n\n // Close popup on unmount or when auth completes/errors\n React.useEffect(() => {\n return () => {\n popupRef.current?.close();\n };\n }, []);\n\n // Update popup URL when verification URL becomes available\n React.useEffect(() => {\n if (\n deviceFlow.status === \"awaiting_authorization\" &&\n deviceFlow.verificationUrl &&\n popupRef.current &&\n !popupRef.current.closed\n ) {\n // Validate URL before assigning to prevent XSS\n if (!isValidVerificationUrl(deviceFlow.verificationUrl)) {\n console.error(\"Invalid verification URL protocol\");\n return;\n }\n try {\n popupRef.current.location.href = deviceFlow.verificationUrl;\n } catch {\n // Cross-origin error - popup was navigated away\n // Open a new one as fallback\n popupRef.current = window.open(\n deviceFlow.verificationUrl,\n \"_blank\",\n \"noopener,noreferrer\",\n );\n }\n }\n }, [deviceFlow.status, deviceFlow.verificationUrl]);\n\n // Call onSuccess when authentication completes\n React.useEffect(() => {\n if (deviceFlow.status === \"success\" && deviceFlow.apiKey) {\n try {\n onSuccess(deviceFlow.apiKey);\n } finally {\n deviceFlow.reset();\n popupRef.current?.close();\n }\n }\n }, [deviceFlow.status, deviceFlow.apiKey, deviceFlow.reset, onSuccess]);\n\n const handleStartAuth = () => {\n // Normalize and validate the host URL\n const normalizedHost = host.trim().replace(/\\/+$/, \"\");\n const fullHost = /^https?:\\/\\//i.test(normalizedHost)\n ? normalizedHost\n : `https://${normalizedHost}`;\n\n // Validate URL before proceeding\n try {\n const url = new URL(fullHost);\n // Check for URL manipulation attacks\n if (url.username || url.password) {\n throw new Error(\"Invalid URL format\");\n }\n } catch {\n return; // Invalid URL, don't proceed\n }\n\n // Open popup immediately on user click to avoid popup blocker\n // Start with about:blank and update URL once we have verification URL\n // Note: We intentionally don't use \"noopener\" here because we need to\n // maintain a reference to update the popup's location when the\n // verification URL becomes available\n popupRef.current = window.open(\"about:blank\", \"_blank\");\n\n if (!popupRef.current) {\n // Popup was blocked - flow will still work, user can click manual link\n console.warn(\"Popup blocked - user will need to use manual link\");\n }\n\n deviceFlow.start(fullHost);\n };\n\n const handleCancel = () => {\n deviceFlow.cancel();\n popupRef.current?.close();\n };\n\n const statusContent = (\n <DeviceFlowStatusContent\n status={deviceFlow.status}\n error={deviceFlow.error}\n verificationUrl={deviceFlow.verificationUrl}\n testIdRoot={testIdRoot}\n onCancel={handleCancel}\n onRetry={handleStartAuth}\n />\n );\n const showStatusModal =\n statusDisplay === \"modal\" && deviceFlow.status !== \"idle\";\n const idleLabel = idleButtonLabel ?? t(I18nKey.BACKEND$LOGIN_WITH_OPENHANDS);\n\n return (\n <div\n data-testid={`${testIdRoot}-device-flow`}\n className={cn(\"flex flex-col gap-3\", className)}\n >\n {deviceFlow.status === \"idle\" && buttonVariant === \"unstyled\" && (\n <button\n type=\"button\"\n onClick={handleStartAuth}\n data-testid={`${testIdRoot}-login-button`}\n className={buttonClassName}\n disabled={isDisabled}\n aria-label={idleButtonContent ? idleLabel : undefined}\n >\n {idleButtonContent ?? idleLabel}\n </button>\n )}\n\n {deviceFlow.status === \"idle\" && buttonVariant !== \"unstyled\" && (\n <BrandButton\n type=\"button\"\n variant={buttonVariant}\n onClick={handleStartAuth}\n testId={`${testIdRoot}-login-button`}\n className={cn(\"w-full\", buttonClassName)}\n isDisabled={isDisabled}\n ariaLabel={idleButtonContent ? idleLabel : undefined}\n >\n {idleButtonContent ?? idleLabel}\n </BrandButton>\n )}\n\n {statusDisplay === \"inline\" ? statusContent : null}\n\n {showStatusModal ? (\n <ModalBackdrop\n onClose={handleCancel}\n aria-label={t(I18nKey.BACKEND$LOGIN_WITH_OPENHANDS)}\n closeOnBackdropClick={false}\n >\n <ModalBody\n testID={`${testIdRoot}-auth-modal`}\n width=\"sm\"\n className=\"items-stretch border border-[var(--oh-border)]\"\n >\n {statusContent}\n </ModalBody>\n </ModalBackdrop>\n ) : null}\n </div>\n );\n}\n\ninterface DeviceFlowStatusContentProps {\n status: DeviceFlowStatus;\n error: string | null;\n verificationUrl: string | null;\n testIdRoot: string;\n onCancel: () => void;\n onRetry: () => void;\n}\n\nfunction DeviceFlowStatusContent({\n status,\n error,\n verificationUrl,\n testIdRoot,\n onCancel,\n onRetry,\n}: DeviceFlowStatusContentProps) {\n const { t } = useTranslation(\"openhands\");\n\n if (status === \"idle\" || status === \"success\") return null;\n\n if (status === \"starting\") {\n return (\n <div\n className=\"flex items-center gap-2 rounded-lg bg-base-tertiary p-3\"\n data-testid={`${testIdRoot}-auth-starting`}\n role=\"status\"\n aria-live=\"polite\"\n >\n <LoadingSpinner />\n <span className=\"text-sm text-[var(--oh-text-tertiary)]\">\n {t(I18nKey.BACKEND$AUTH_STARTING)}\n </span>\n </div>\n );\n }\n\n if (status === \"awaiting_authorization\") {\n const validVerificationUrl =\n verificationUrl && isValidVerificationUrl(verificationUrl)\n ? verificationUrl\n : null;\n\n return (\n <div\n className=\"flex flex-col gap-4 rounded-lg bg-base-tertiary p-4\"\n data-testid={`${testIdRoot}-auth-awaiting`}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"flex items-center gap-2\">\n <LoadingSpinner />\n <span className=\"text-sm font-medium text-white\">\n {t(I18nKey.BACKEND$AUTH_AWAITING)}\n </span>\n </div>\n <p className=\"text-sm leading-5 text-[var(--oh-text-tertiary)]\">\n {t(I18nKey.BACKEND$AUTH_BROWSER_OPENED)}\n </p>\n {validVerificationUrl ? (\n <div className=\"flex flex-col gap-1 rounded-md border border-[var(--oh-border)] bg-[var(--oh-surface)] p-3 text-xs text-[var(--oh-muted)]\">\n <p>{t(I18nKey.BACKEND$AUTH_OPEN_MANUALLY)}</p>\n <a\n href={validVerificationUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"max-h-16 overflow-auto break-all text-blue-400 hover:underline\"\n >\n {validVerificationUrl}\n </a>\n </div>\n ) : null}\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={onCancel}\n testId={`${testIdRoot}-auth-cancel`}\n className=\"w-full\"\n >\n {t(I18nKey.BACKEND$AUTH_CANCEL)}\n </BrandButton>\n </div>\n );\n }\n\n return (\n <div\n className=\"flex flex-col gap-3 rounded-lg border border-red-700 bg-red-900/20 p-4\"\n data-testid={`${testIdRoot}-auth-error`}\n role=\"alert\"\n >\n <p className=\"text-sm text-red-400\">{error}</p>\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={onRetry}\n testId={`${testIdRoot}-auth-retry`}\n className=\"w-full\"\n >\n {t(I18nKey.BACKEND$AUTH_RETRY)}\n </BrandButton>\n </div>\n );\n}\n\nfunction LoadingSpinner() {\n return (\n <svg\n className=\"animate-spin h-4 w-4 text-white\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"device-flow-auth.cjs","names":[],"sources":["../../../../src/components/features/backends/device-flow-auth.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalBody } from \"#/components/shared/modals/modal-body\";\nimport { useDeviceFlow, type DeviceFlowStatus } from \"#/hooks/use-device-flow\";\nimport type { CloudConnectionSource } from \"#/services/cloud-funnel-analytics\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { cn } from \"#/utils/utils\";\n\ntype DeviceFlowButtonVariant =\n | \"primary\"\n | \"secondary\"\n | \"tertiary\"\n | \"unstyled\";\ntype DeviceFlowStatusDisplay = \"inline\" | \"modal\";\n\ninterface DeviceFlowAuthProps {\n /** The host URL for the cloud backend */\n host: string;\n /** Callback when authentication succeeds with the API key */\n onSuccess: (apiKey: string) => void;\n /** Test ID prefix for the component */\n testIdRoot: string;\n /** Whether the login button should be disabled (e.g., when no host is entered) */\n isDisabled?: boolean;\n /** Override for the idle button label and icon-only accessible name. */\n idleButtonLabel?: string;\n /** Optional visible content for the idle button. Defaults to the idle label. */\n idleButtonContent?: React.ReactNode;\n /** Optional classes for the root wrapper. */\n className?: string;\n /** Optional classes for the idle button. */\n buttonClassName?: string;\n /** Visual variant for the idle button. */\n buttonVariant?: DeviceFlowButtonVariant;\n /** Whether in-progress auth content should render inline or in a modal. */\n statusDisplay?: DeviceFlowStatusDisplay;\n /** Product surface that initiated this authorization attempt. */\n analyticsSource?: CloudConnectionSource;\n}\n\n/**\n * Device Flow authentication UI component.\n *\n * Shows a \"Login with OpenHands Cloud\" button that initiates OAuth 2.0 Device Flow\n * authentication. Displays status during the auth process and auto-opens\n * the browser for user authorization.\n */\n/**\n * Validate that a URL is safe to open in a popup.\n * Prevents XSS via javascript: URLs or other malicious schemes.\n */\nfunction isValidVerificationUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n\nexport function DeviceFlowAuth({\n host,\n onSuccess,\n testIdRoot,\n isDisabled = false,\n idleButtonLabel,\n idleButtonContent,\n className,\n buttonClassName,\n buttonVariant = \"primary\",\n statusDisplay = \"inline\",\n analyticsSource,\n}: DeviceFlowAuthProps) {\n const { t } = useTranslation(\"openhands\");\n const deviceFlow = useDeviceFlow();\n const popupRef = React.useRef<Window | null>(null);\n\n // Close popup on unmount or when auth completes/errors\n React.useEffect(() => {\n return () => {\n popupRef.current?.close();\n };\n }, []);\n\n // Update popup URL when verification URL becomes available\n React.useEffect(() => {\n if (\n deviceFlow.status === \"awaiting_authorization\" &&\n deviceFlow.verificationUrl &&\n popupRef.current &&\n !popupRef.current.closed\n ) {\n // Validate URL before assigning to prevent XSS\n if (!isValidVerificationUrl(deviceFlow.verificationUrl)) {\n console.error(\"Invalid verification URL protocol\");\n return;\n }\n try {\n popupRef.current.location.href = deviceFlow.verificationUrl;\n } catch {\n // Cross-origin error - popup was navigated away\n // Open a new one as fallback\n popupRef.current = window.open(\n deviceFlow.verificationUrl,\n \"_blank\",\n \"noopener,noreferrer\",\n );\n }\n }\n }, [deviceFlow.status, deviceFlow.verificationUrl]);\n\n // Call onSuccess when authentication completes\n React.useEffect(() => {\n if (deviceFlow.status === \"success\" && deviceFlow.apiKey) {\n try {\n onSuccess(deviceFlow.apiKey);\n } finally {\n deviceFlow.reset();\n popupRef.current?.close();\n }\n }\n }, [deviceFlow.status, deviceFlow.apiKey, deviceFlow.reset, onSuccess]);\n\n const handleStartAuth = () => {\n // Normalize and validate the host URL\n const normalizedHost = host.trim().replace(/\\/+$/, \"\");\n const fullHost = /^https?:\\/\\//i.test(normalizedHost)\n ? normalizedHost\n : `https://${normalizedHost}`;\n\n // Validate URL before proceeding\n try {\n const url = new URL(fullHost);\n // Check for URL manipulation attacks\n if (url.username || url.password) {\n throw new Error(\"Invalid URL format\");\n }\n } catch {\n return; // Invalid URL, don't proceed\n }\n\n // Open popup immediately on user click to avoid popup blocker\n // Start with about:blank and update URL once we have verification URL\n // Note: We intentionally don't use \"noopener\" here because we need to\n // maintain a reference to update the popup's location when the\n // verification URL becomes available\n popupRef.current = window.open(\"about:blank\", \"_blank\");\n\n if (!popupRef.current) {\n // Popup was blocked - flow will still work, user can click manual link\n console.warn(\"Popup blocked - user will need to use manual link\");\n }\n\n deviceFlow.start(fullHost, analyticsSource);\n };\n\n const handleCancel = () => {\n deviceFlow.cancel();\n popupRef.current?.close();\n };\n\n const statusContent = (\n <DeviceFlowStatusContent\n status={deviceFlow.status}\n error={deviceFlow.error}\n verificationUrl={deviceFlow.verificationUrl}\n testIdRoot={testIdRoot}\n onCancel={handleCancel}\n onRetry={handleStartAuth}\n />\n );\n const showStatusModal =\n statusDisplay === \"modal\" && deviceFlow.status !== \"idle\";\n const idleLabel = idleButtonLabel ?? t(I18nKey.BACKEND$LOGIN_WITH_OPENHANDS);\n\n return (\n <div\n data-testid={`${testIdRoot}-device-flow`}\n className={cn(\"flex flex-col gap-3\", className)}\n >\n {deviceFlow.status === \"idle\" && buttonVariant === \"unstyled\" && (\n <button\n type=\"button\"\n onClick={handleStartAuth}\n data-testid={`${testIdRoot}-login-button`}\n className={buttonClassName}\n disabled={isDisabled}\n aria-label={idleButtonContent ? idleLabel : undefined}\n >\n {idleButtonContent ?? idleLabel}\n </button>\n )}\n\n {deviceFlow.status === \"idle\" && buttonVariant !== \"unstyled\" && (\n <BrandButton\n type=\"button\"\n variant={buttonVariant}\n onClick={handleStartAuth}\n testId={`${testIdRoot}-login-button`}\n className={cn(\"w-full\", buttonClassName)}\n isDisabled={isDisabled}\n ariaLabel={idleButtonContent ? idleLabel : undefined}\n >\n {idleButtonContent ?? idleLabel}\n </BrandButton>\n )}\n\n {statusDisplay === \"inline\" ? statusContent : null}\n\n {showStatusModal ? (\n <ModalBackdrop\n onClose={handleCancel}\n aria-label={t(I18nKey.BACKEND$LOGIN_WITH_OPENHANDS)}\n closeOnBackdropClick={false}\n >\n <ModalBody\n testID={`${testIdRoot}-auth-modal`}\n width=\"sm\"\n className=\"items-stretch border border-[var(--oh-border)]\"\n >\n {statusContent}\n </ModalBody>\n </ModalBackdrop>\n ) : null}\n </div>\n );\n}\n\ninterface DeviceFlowStatusContentProps {\n status: DeviceFlowStatus;\n error: string | null;\n verificationUrl: string | null;\n testIdRoot: string;\n onCancel: () => void;\n onRetry: () => void;\n}\n\nfunction DeviceFlowStatusContent({\n status,\n error,\n verificationUrl,\n testIdRoot,\n onCancel,\n onRetry,\n}: DeviceFlowStatusContentProps) {\n const { t } = useTranslation(\"openhands\");\n\n if (status === \"idle\" || status === \"success\") return null;\n\n if (status === \"starting\") {\n return (\n <div\n className=\"flex items-center gap-2 rounded-lg bg-base-tertiary p-3\"\n data-testid={`${testIdRoot}-auth-starting`}\n role=\"status\"\n aria-live=\"polite\"\n >\n <LoadingSpinner />\n <span className=\"text-sm text-[var(--oh-text-tertiary)]\">\n {t(I18nKey.BACKEND$AUTH_STARTING)}\n </span>\n </div>\n );\n }\n\n if (status === \"awaiting_authorization\") {\n const validVerificationUrl =\n verificationUrl && isValidVerificationUrl(verificationUrl)\n ? verificationUrl\n : null;\n\n return (\n <div\n className=\"flex flex-col gap-4 rounded-lg bg-base-tertiary p-4\"\n data-testid={`${testIdRoot}-auth-awaiting`}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"flex items-center gap-2\">\n <LoadingSpinner />\n <span className=\"text-sm font-medium text-white\">\n {t(I18nKey.BACKEND$AUTH_AWAITING)}\n </span>\n </div>\n <p className=\"text-sm leading-5 text-[var(--oh-text-tertiary)]\">\n {t(I18nKey.BACKEND$AUTH_BROWSER_OPENED)}\n </p>\n {validVerificationUrl ? (\n <div className=\"flex flex-col gap-1 rounded-md border border-[var(--oh-border)] bg-[var(--oh-surface)] p-3 text-xs text-[var(--oh-muted)]\">\n <p>{t(I18nKey.BACKEND$AUTH_OPEN_MANUALLY)}</p>\n <a\n href={validVerificationUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"max-h-16 overflow-auto break-all text-blue-400 hover:underline\"\n >\n {validVerificationUrl}\n </a>\n </div>\n ) : null}\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={onCancel}\n testId={`${testIdRoot}-auth-cancel`}\n className=\"w-full\"\n >\n {t(I18nKey.BACKEND$AUTH_CANCEL)}\n </BrandButton>\n </div>\n );\n }\n\n return (\n <div\n className=\"flex flex-col gap-3 rounded-lg border border-red-700 bg-red-900/20 p-4\"\n data-testid={`${testIdRoot}-auth-error`}\n role=\"alert\"\n >\n <p className=\"text-sm text-red-400\">{error}</p>\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n onClick={onRetry}\n testId={`${testIdRoot}-auth-retry`}\n className=\"w-full\"\n >\n {t(I18nKey.BACKEND$AUTH_RETRY)}\n </BrandButton>\n </div>\n );\n}\n\nfunction LoadingSpinner() {\n return (\n <svg\n className=\"animate-spin h-4 w-4 text-white\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import type { CloudConnectionSource } from "#/services/cloud-funnel-analytics";
2
3
  type DeviceFlowButtonVariant = "primary" | "secondary" | "tertiary" | "unstyled";
3
4
  type DeviceFlowStatusDisplay = "inline" | "modal";
4
5
  interface DeviceFlowAuthProps {
@@ -22,6 +23,8 @@ interface DeviceFlowAuthProps {
22
23
  buttonVariant?: DeviceFlowButtonVariant;
23
24
  /** Whether in-progress auth content should render inline or in a modal. */
24
25
  statusDisplay?: DeviceFlowStatusDisplay;
26
+ /** Product surface that initiated this authorization attempt. */
27
+ analyticsSource?: CloudConnectionSource;
25
28
  }
26
- export declare function DeviceFlowAuth({ host, onSuccess, testIdRoot, isDisabled, idleButtonLabel, idleButtonContent, className, buttonClassName, buttonVariant, statusDisplay, }: DeviceFlowAuthProps): import("react/jsx-runtime").JSX.Element;
29
+ export declare function DeviceFlowAuth({ host, onSuccess, testIdRoot, isDisabled, idleButtonLabel, idleButtonContent, className, buttonClassName, buttonVariant, statusDisplay, analyticsSource, }: DeviceFlowAuthProps): import("react/jsx-runtime").JSX.Element;
27
30
  export {};
@@ -15,35 +15,35 @@ function u(e) {
15
15
  return !1;
16
16
  }
17
17
  }
18
- function d({ host: d, onSuccess: p, testIdRoot: m, isDisabled: h = !1, idleButtonLabel: g, idleButtonContent: _, className: v, buttonClassName: y, buttonVariant: b = "primary", statusDisplay: x = "inline" }) {
19
- let { t: S } = e("openhands"), C = o(), w = s.useRef(null);
18
+ function d({ host: d, onSuccess: p, testIdRoot: m, isDisabled: h = !1, idleButtonLabel: g, idleButtonContent: _, className: v, buttonClassName: y, buttonVariant: b = "primary", statusDisplay: x = "inline", analyticsSource: S }) {
19
+ let { t: C } = e("openhands"), w = o(), T = s.useRef(null);
20
20
  s.useEffect(() => () => {
21
- w.current?.close();
21
+ T.current?.close();
22
22
  }, []), s.useEffect(() => {
23
- if (C.status === "awaiting_authorization" && C.verificationUrl && w.current && !w.current.closed) {
24
- if (!u(C.verificationUrl)) {
23
+ if (w.status === "awaiting_authorization" && w.verificationUrl && T.current && !T.current.closed) {
24
+ if (!u(w.verificationUrl)) {
25
25
  console.error("Invalid verification URL protocol");
26
26
  return;
27
27
  }
28
28
  try {
29
- w.current.location.href = C.verificationUrl;
29
+ T.current.location.href = w.verificationUrl;
30
30
  } catch {
31
- w.current = window.open(C.verificationUrl, "_blank", "noopener,noreferrer");
31
+ T.current = window.open(w.verificationUrl, "_blank", "noopener,noreferrer");
32
32
  }
33
33
  }
34
- }, [C.status, C.verificationUrl]), s.useEffect(() => {
35
- if (C.status === "success" && C.apiKey) try {
36
- p(C.apiKey);
34
+ }, [w.status, w.verificationUrl]), s.useEffect(() => {
35
+ if (w.status === "success" && w.apiKey) try {
36
+ p(w.apiKey);
37
37
  } finally {
38
- C.reset(), w.current?.close();
38
+ w.reset(), T.current?.close();
39
39
  }
40
40
  }, [
41
- C.status,
42
- C.apiKey,
43
- C.reset,
41
+ w.status,
42
+ w.apiKey,
43
+ w.reset,
44
44
  p
45
45
  ]);
46
- let T = () => {
46
+ let E = () => {
47
47
  let e = d.trim().replace(/\/+$/, ""), t = /^https?:\/\//i.test(e) ? e : `https://${e}`;
48
48
  try {
49
49
  let e = new URL(t);
@@ -51,50 +51,50 @@ function d({ host: d, onSuccess: p, testIdRoot: m, isDisabled: h = !1, idleButto
51
51
  } catch {
52
52
  return;
53
53
  }
54
- w.current = window.open("about:blank", "_blank"), w.current || console.warn("Popup blocked - user will need to use manual link"), C.start(t);
55
- }, E = () => {
56
- C.cancel(), w.current?.close();
57
- }, D = /* @__PURE__ */ c(f, {
58
- status: C.status,
59
- error: C.error,
60
- verificationUrl: C.verificationUrl,
54
+ T.current = window.open("about:blank", "_blank"), T.current || console.warn("Popup blocked - user will need to use manual link"), w.start(t, S);
55
+ }, D = () => {
56
+ w.cancel(), T.current?.close();
57
+ }, O = /* @__PURE__ */ c(f, {
58
+ status: w.status,
59
+ error: w.error,
60
+ verificationUrl: w.verificationUrl,
61
61
  testIdRoot: m,
62
- onCancel: E,
63
- onRetry: T
64
- }), O = x === "modal" && C.status !== "idle", k = g ?? S(t.BACKEND$LOGIN_WITH_OPENHANDS);
62
+ onCancel: D,
63
+ onRetry: E
64
+ }), k = x === "modal" && w.status !== "idle", A = g ?? C(t.BACKEND$LOGIN_WITH_OPENHANDS);
65
65
  return /* @__PURE__ */ l("div", {
66
66
  "data-testid": `${m}-device-flow`,
67
67
  className: n("flex flex-col gap-3", v),
68
68
  children: [
69
- C.status === "idle" && b === "unstyled" && /* @__PURE__ */ c("button", {
69
+ w.status === "idle" && b === "unstyled" && /* @__PURE__ */ c("button", {
70
70
  type: "button",
71
- onClick: T,
71
+ onClick: E,
72
72
  "data-testid": `${m}-login-button`,
73
73
  className: y,
74
74
  disabled: h,
75
- "aria-label": _ ? k : void 0,
76
- children: _ ?? k
75
+ "aria-label": _ ? A : void 0,
76
+ children: _ ?? A
77
77
  }),
78
- C.status === "idle" && b !== "unstyled" && /* @__PURE__ */ c(a, {
78
+ w.status === "idle" && b !== "unstyled" && /* @__PURE__ */ c(a, {
79
79
  type: "button",
80
80
  variant: b,
81
- onClick: T,
81
+ onClick: E,
82
82
  testId: `${m}-login-button`,
83
83
  className: n("w-full", y),
84
84
  isDisabled: h,
85
- ariaLabel: _ ? k : void 0,
86
- children: _ ?? k
85
+ ariaLabel: _ ? A : void 0,
86
+ children: _ ?? A
87
87
  }),
88
- x === "inline" ? D : null,
89
- O ? /* @__PURE__ */ c(r, {
90
- onClose: E,
91
- "aria-label": S(t.BACKEND$LOGIN_WITH_OPENHANDS),
88
+ x === "inline" ? O : null,
89
+ k ? /* @__PURE__ */ c(r, {
90
+ onClose: D,
91
+ "aria-label": C(t.BACKEND$LOGIN_WITH_OPENHANDS),
92
92
  closeOnBackdropClick: !1,
93
93
  children: /* @__PURE__ */ c(i, {
94
94
  testID: `${m}-auth-modal`,
95
95
  width: "sm",
96
96
  className: "items-stretch border border-[var(--oh-border)]",
97
- children: D
97
+ children: O
98
98
  })
99
99
  }) : null
100
100
  ]