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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (409) hide show
  1. package/README.md +17 -6
  2. package/bin/agent-canvas.mjs +22 -2
  3. package/build/assets/{QueryClientProvider-B7kl84Kj.js → QueryClientProvider-CkGuhXg-.js} +1 -1
  4. package/build/assets/{Trans-1j65oy9O.js → Trans-Cvm_-SMi.js} +1 -1
  5. package/build/assets/acp-providers-CbiRekh9.js +1 -0
  6. package/build/assets/{acp-route-guard-CQTmeJwM.js → acp-route-guard-B2yoBZ_4.js} +1 -1
  7. package/build/assets/{active-backend-context-TVbjnvmP.js → active-backend-context-cCM1vYYZ.js} +1 -1
  8. package/build/assets/add-backend-modal-DIUQzMPa.js +1 -0
  9. package/build/assets/agent-server-client-options-Bc5ZorQZ.js +1 -0
  10. package/build/assets/agent-server-compatibility-BlkUsrX2.js +1 -0
  11. package/build/assets/agent-server-conversation-service.api-DFvqqEDo.js +5 -0
  12. package/build/assets/{agent-settings-B247S9G3.js → agent-settings-CnGSCmK8.js} +1 -1
  13. package/build/assets/{alert-banner-BWoqueRw.js → alert-banner-DtzAX654.js} +1 -1
  14. package/build/assets/{analytics-consent-form-modal-C7sXfxRh.js → analytics-consent-form-modal-CHZ3I37v.js} +1 -1
  15. package/build/assets/api-key-entry-screen-B2gynaCp.js +1 -0
  16. package/build/assets/{app-settings-BVeSaty9.js → app-settings-Db9ITeJH.js} +1 -1
  17. package/build/assets/{automation-detail-g5-RZ0da.js → automation-detail-Di7EOIZD.js} +1 -1
  18. package/build/assets/{automations-list-DHoq_0MM.js → automations-list-IsIWdDiw.js} +1 -1
  19. package/build/assets/backend-form-modal-Dnk33xA_.js +1 -0
  20. package/build/assets/{backend-synced-settings-badge-nAfiUWvM.js → backend-synced-settings-badge-Dc6c7GT4.js} +1 -1
  21. package/build/assets/{base-modal-CQRvRHu1.js → base-modal-_dYTw1ri.js} +1 -1
  22. package/build/assets/{brand-button-C2nEKopC.js → brand-button-Br7f0kZJ.js} +1 -1
  23. package/build/assets/browser-D810xUYt.js +5 -0
  24. package/build/assets/browser-store-Couc4S5D.js +1 -0
  25. package/build/assets/browser-tab-B-aIqXRl.js +1 -0
  26. package/build/assets/{checkmark-BJJrZUF8.js → checkmark-DL7acQA7.js} +1 -1
  27. package/build/assets/{chevron-left-small-CSh-sE9L.js → chevron-left-small-CVWf8TI6.js} +1 -1
  28. package/build/assets/{circle-plus-check-toggle-qs8Va1cC.js → circle-plus-check-toggle-P7ZZToV4.js} +1 -1
  29. package/build/assets/{clock-ZR4Kn-_Y.js → clock-BRjCgHTc.js} +1 -1
  30. package/build/assets/{close-BdmyeRqS.js → close-B5LROHR3.js} +1 -1
  31. package/build/assets/{combobox-caret-B53O9Hsq.js → combobox-caret-to1O8irE.js} +1 -1
  32. package/build/assets/{condenser-settings-A35V3yng.js → condenser-settings-wnEKhBof.js} +1 -1
  33. package/build/assets/{confirmation-modal-C9-La0h3.js → confirmation-modal-Dau3w_sa.js} +1 -1
  34. package/build/assets/{context-menu-list-item-Buu9nc0q.js → context-menu-list-item-CWNFpuiC.js} +1 -1
  35. package/build/assets/conversation-HlncOV7n.js +19 -0
  36. package/build/assets/conversation-MtnkpqA9.js +1 -0
  37. package/build/assets/conversation-panel-DxnM6tRe.js +1 -0
  38. package/build/assets/{conversation-service.api-C8pYCyV6.js → conversation-service.api-nb5W1PqR.js} +1 -1
  39. package/build/assets/{conversation-tab-empty-state-D8dNvo-V.js → conversation-tab-empty-state-DyssnnWa.js} +1 -1
  40. package/build/assets/conversation-websocket-context-C8_PkGLi.js +3 -0
  41. package/build/assets/{copy-C7Ti2d8C.js → copy-DYgmUdIw.js} +1 -1
  42. package/build/assets/{custom-toast-handlers-BOc3qeQ7.js → custom-toast-handlers-C-SZFmto.js} +1 -1
  43. package/build/assets/declaration-BNMqORFE.js +1 -0
  44. package/build/assets/{device-verify-CMusn8nX.js → device-verify-DqDlphsG.js} +1 -1
  45. package/build/assets/{dist-DZHSA2e6.js → dist-C6t0EXL7.js} +1 -1
  46. package/build/assets/{edit-automation-modal-Dnjxbjn7.js → edit-automation-modal-BGzR3nfZ.js} +1 -1
  47. package/build/assets/{ellipsis-button-ugUATsNo.js → ellipsis-button-ZyLMPURn.js} +1 -1
  48. package/build/assets/{entry.client-D9uR9Blz.js → entry.client-1VMHpktY.js} +2 -2
  49. package/build/assets/{enum-filter-dropdown-1vpOGySB.js → enum-filter-dropdown-CEgCdu4A.js} +1 -1
  50. package/build/assets/{environment-switch-overlay-CTCTQikP.js → environment-switch-overlay-XL8yCGP6.js} +1 -1
  51. package/build/assets/{extensions-hub-BSUseHVF.js → extensions-hub-C651jsVh.js} +1 -1
  52. package/build/assets/{extensions-navigation-CT1kc1u_.js → extensions-navigation-BYR8Giqq.js} +1 -1
  53. package/build/assets/files-tab-BhnLgimi.js +1 -0
  54. package/build/assets/{folder-0WSMImNX.js → folder-ZZJVGgd7.js} +1 -1
  55. package/build/assets/git-control-bar-branch-button-M34A5_vX.js +27 -0
  56. package/build/assets/{git-provider-icon-DYE9n7fs.js → git-provider-icon-D5dCNy-k.js} +1 -1
  57. package/build/assets/home-CYQv7yc_.js +1 -0
  58. package/build/assets/{i18n-DjAGhTis.js → i18n-CTohRuoO.js} +1 -1
  59. package/build/assets/install-server-modal-f31_CLrW.js +1 -0
  60. package/build/assets/{launch-hZ0ifhcV.js → launch-DHEUYn2A.js} +1 -1
  61. package/build/assets/{lesson-plan-DRYG5SLI.js → lesson-plan-dH5Bj0pN.js} +1 -1
  62. package/build/assets/{link-external-Df8J52xI.js → link-external-D2POYx4c.js} +1 -1
  63. package/build/assets/{llm-client-ChQzg4wX.js → llm-client-DaH1TuyR.js} +1 -1
  64. package/build/assets/llm-settings-Bql-vydt.js +1 -0
  65. package/build/assets/llm-settings-C_tal6Ds.js +1 -0
  66. package/build/assets/{loading-spinner-C04FGh14.js → loading-spinner-BPtYORNK.js} +1 -1
  67. package/build/assets/{manage-backends-modal-rYeyGx7j.js → manage-backends-modal-l7RkKfwX.js} +1 -1
  68. package/build/assets/{manage-workspaces-modal-C5EuW8m1.js → manage-workspaces-modal-DhKF_8z3.js} +1 -1
  69. package/build/assets/manifest-9fee01b9.js +1 -0
  70. package/build/assets/{markdown-renderer-CEX4Becj.js → markdown-renderer-DMzf2i4x.js} +1 -1
  71. package/build/assets/mcp-D2onbwVk.js +9 -0
  72. package/build/assets/{messages-T2ewVkbp.js → messages-BMzyOW2V.js} +1 -1
  73. package/build/assets/{modal-backdrop-DTYGVmOR.js → modal-backdrop-BAbgYsqB.js} +1 -1
  74. package/build/assets/{modal-body-YElmM1dV.js → modal-body-BI6Ru2Qr.js} +1 -1
  75. package/build/assets/{modal-close-button-C_GpQt9F.js → modal-close-button-t1Gh3qmL.js} +1 -1
  76. package/build/assets/{model-selector-DeMmw-Xa.js → model-selector-SM9IUz-q.js} +1 -1
  77. package/build/assets/{mutation-Cz7N4XAo.js → mutation-D0OogFCz.js} +1 -1
  78. package/build/assets/{navigation-context-DeIPtGPp.js → navigation-context-D0YWpT8d.js} +1 -1
  79. package/build/assets/{navigation-link-C9JD4PYD.js → navigation-link-Cn7KP3c5.js} +1 -1
  80. package/build/assets/{openhands-logo-CI5Fhn1W.js → openhands-logo-CnrF6LKb.js} +1 -1
  81. package/build/assets/{option-service.api-DsI1UW7N.js → option-service.api-KvY_mZMY.js} +1 -1
  82. package/build/assets/{organization-service.api-COwMPFg5.js → organization-service.api-DzYTHTYC.js} +1 -1
  83. package/build/assets/{path-utils-CqJboYxo.js → path-utils-YohAYyMv.js} +1 -1
  84. package/build/assets/{plan-components-DEjMuDDG.js → plan-components-atxXCF0R.js} +1 -1
  85. package/build/assets/{planner-tab-BrntFmb1.js → planner-tab-CFc-hV07.js} +1 -1
  86. package/build/assets/{profiles-client-BGkKEV9j.js → profiles-client-D6IkTJof.js} +1 -1
  87. package/build/assets/{providers-DXvCAN_u.js → providers-Bx6EfrzZ.js} +1 -1
  88. package/build/assets/{proxy-CurRmrqf.js → proxy-CxydCnis.js} +1 -1
  89. package/build/assets/{query-client-config-Ba7qAAoO.js → query-client-config-B7u9asM0.js} +1 -1
  90. package/build/assets/{recommended-automations-launcher-BI9NhG8Y.js → recommended-automations-launcher-sgvfU62c.js} +3 -3
  91. package/build/assets/root-BXWU99D-.js +2 -0
  92. package/build/assets/{root-layout-BLjAEgle.js → root-layout-DVepR4To.js} +2 -2
  93. package/build/assets/sdk-section-page-DOIKvwSL.js +1 -0
  94. package/build/assets/{sdk-settings-schema-QBYH-ONX.js → sdk-settings-schema-DsUf9wu1.js} +1 -1
  95. package/build/assets/{search-Cq_cFrDt.js → search-27Owlc3A.js} +1 -1
  96. package/build/assets/{secrets-service-Bwd5DeUs.js → secrets-service-BsnKFc2x.js} +1 -1
  97. package/build/assets/secrets-settings-Bz_UohPJ.js +1 -0
  98. package/build/assets/{server-client-C3mC8Hl3.js → server-client-DyAQ3NZ_.js} +1 -1
  99. package/build/assets/{settings-D7E2U5tK.js → settings-BYkVX7vW.js} +1 -1
  100. package/build/assets/{settings-client-CwjfwoiB.js → settings-client-C73C7IgV.js} +1 -1
  101. package/build/assets/{settings-dropdown-input-VwAXNrOb.js → settings-dropdown-input-BJYvGdg-.js} +1 -1
  102. package/build/assets/{settings-gear-BJwWR1ej.js → settings-gear-C77PgE_O.js} +1 -1
  103. package/build/assets/{settings-index-J-3BNR0W.js → settings-index-Dz0BmdJD.js} +1 -1
  104. package/build/assets/{settings-input-DBywAnA7.js → settings-input-Bn7F5C75.js} +1 -1
  105. package/build/assets/{settings-list-classes-BOS092DR.js → settings-list-classes-Bf80tWtc.js} +1 -1
  106. package/build/assets/{settings-modal-B8vgWDTe.js → settings-modal-Brzgh5Yw.js} +1 -1
  107. package/build/assets/{settings-section-header-context-32x6WTyL.js → settings-section-header-context-BgZe5YkE.js} +1 -1
  108. package/build/assets/{settings-service.api-FvJGK45W.js → settings-service.api-CZ3uWx4v.js} +1 -1
  109. package/build/assets/{settings-switch-DTKmHC8F.js → settings-switch-BeIKrWms.js} +1 -1
  110. package/build/assets/{shared-conversation-a0QV8o99.js → shared-conversation-DChOdb0t.js} +1 -1
  111. package/build/assets/{sidebar-mobile-menu-toggle-DTUNI1WQ.js → sidebar-mobile-menu-toggle-BWuf4PRH.js} +1 -1
  112. package/build/assets/{sidebar-nav-link-CnWoZcwc.js → sidebar-nav-link-BGjiJq-4.js} +1 -1
  113. package/build/assets/{skill-card-pill-row-tZ599jli.js → skill-card-pill-row-DF1axQCG.js} +1 -1
  114. package/build/assets/{skills-ZyAO5dyK.js → skills-ChIKZPK4.js} +1 -1
  115. package/build/assets/{skills-plugins-BSRz041I.js → skills-plugins-CcI_19lM.js} +1 -1
  116. package/build/assets/{skills-settings-DOnMn9q1.js → skills-settings-DlA5hlXw.js} +1 -1
  117. package/build/assets/{status-CsatcFbK.js → status-hp6M6E7E.js} +1 -1
  118. package/build/assets/{styled-tooltip-CS3mB_1X.js → styled-tooltip-CBzrri6o.js} +1 -1
  119. package/build/assets/{switch-skeleton-C-CfhYYV.js → switch-skeleton-DnC9wLp7.js} +1 -1
  120. package/build/assets/{task-list-tab-Day9nhRT.js → task-list-tab-DUJn1sgz.js} +1 -1
  121. package/build/assets/{terminal-ro4SNjUU.js → terminal-CRf9S0Z2.js} +1 -1
  122. package/build/assets/{terminal-LNa-iU5c.js → terminal-RmuaSdhJ.js} +1 -1
  123. package/build/assets/{toggle-switch-k-IZCDbt.js → toggle-switch-Pvyp2RAN.js} +1 -1
  124. package/build/assets/{typography-vVUMoNUg.js → typography-gpuWmrQO.js} +1 -1
  125. package/build/assets/{u-check-circle-DplbarS5.js → u-check-circle-IUIfACQQ.js} +1 -1
  126. package/build/assets/{u-check-circle-half-yDuiSZHC.js → u-check-circle-half-C1YxB6py.js} +1 -1
  127. package/build/assets/{u-circuit-C9tYkpeK.js → u-circuit-BmVikJHu.js} +1 -1
  128. package/build/assets/{u-edit-KAUlufD8.js → u-edit-CFvXHqZk.js} +1 -1
  129. package/build/assets/use-active-conversation-Db3IWSPK.js +1 -0
  130. package/build/assets/{use-agent-settings-schema-Bvp5UzV8.js → use-agent-settings-schema-33Un7UF2.js} +1 -1
  131. package/build/assets/{use-agent-state-DE5dlEXJ.js → use-agent-state-Bn8vS5sY.js} +1 -1
  132. package/build/assets/{use-cloud-current-user-id-DWVar4st.js → use-cloud-current-user-id-CvkXFnTT.js} +1 -1
  133. package/build/assets/use-config-Co1O8-Ey.js +1 -0
  134. package/build/assets/{use-create-conversation-DW7AGgLA.js → use-create-conversation-CKS3EAHu.js} +1 -1
  135. package/build/assets/{use-event-store-CQZCcVz-.js → use-event-store-BT_gV3ut.js} +1 -1
  136. package/build/assets/use-get-secrets-DuhdIA59.js +1 -0
  137. package/build/assets/{use-handle-plan-click-DpgEQDAV.js → use-handle-plan-click-C9zJpK8A.js} +1 -1
  138. package/build/assets/use-is-authed-BggE5wPj.js +1 -0
  139. package/build/assets/{use-is-creating-conversation-DhDeeWfA.js → use-is-creating-conversation-BZ5hB_Bg.js} +1 -1
  140. package/build/assets/{use-launch-skill-in-chat-DVGPFrbI.js → use-launch-skill-in-chat-fNN_xGZG.js} +1 -1
  141. package/build/assets/{use-llm-profiles-D3-KXwQ0.js → use-llm-profiles-DDOol3gK.js} +1 -1
  142. package/build/assets/use-runtime-is-ready-CQCE3xZC.js +1 -0
  143. package/build/assets/{use-save-settings-CEEKSTWG.js → use-save-settings-VUrj_QNG.js} +1 -1
  144. package/build/assets/{use-settings-DQ7Oo1Hj.js → use-settings-DQIZmIov.js} +1 -1
  145. package/build/assets/{use-settings-nav-items-YmrXrjn9.js → use-settings-nav-items-1ZvovKSr.js} +1 -1
  146. package/build/assets/use-skills-DAMLFjKU.js +1 -0
  147. package/build/assets/{use-task-list-Bs90uF2N.js → use-task-list-CLJbuJgM.js} +1 -1
  148. package/build/assets/use-unified-vscode-url-sZt29HrC.js +1 -0
  149. package/build/assets/use-user-conversation-DfgEB6RW.js +1 -0
  150. package/build/assets/{useMutation-B4OUESdw.js → useMutation-DqrumCWD.js} +1 -1
  151. package/build/assets/{useTranslation-CpIcQBq6.js → useTranslation-DCOdSSMl.js} +1 -1
  152. package/build/assets/{utils-D-HX7JCe.js → utils-i18rdUj2.js} +1 -1
  153. package/build/assets/v4-CNn21NXa.js +1 -0
  154. package/build/assets/{vendor~browser-Dr71AdrG.js → vendor~browser-BNjNhjFU.js} +1 -1
  155. package/build/assets/{vendor~browser-tab-BiVxfjJo.js → vendor~browser-tab-BgwV1mxF.js} +1 -1
  156. package/build/assets/{vendor~conversation-panel~conversation-BlCIz9XQ.js → vendor~conversation-panel~conversation-a9SyrrhV.js} +1 -1
  157. package/build/assets/{vendor~files-tab-DtLR-QD9.js → vendor~files-tab-BGKayPiK.js} +1 -1
  158. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Ds9quNZ9.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-smY2r837.js} +1 -1
  159. package/build/assets/{vendor~home~mcp~automations-list-C5PoHCy6.js → vendor~home~mcp~automations-list-Ccy2I0KU.js} +1 -1
  160. package/build/assets/{vendor~home~mcp~automations-list-BUBGGGYz.js → vendor~home~mcp~automations-list-DoPfwaXj.js} +1 -1
  161. package/build/assets/{vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-CGlZoBKa.js → vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-DbfELDJu.js} +2 -2
  162. package/build/assets/{vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-DE11mPxp.js → vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-Z3nsiNNq.js} +1 -1
  163. package/build/assets/{vendor~launch-Dg--Ssk6.js → vendor~launch-vdeRTWFu.js} +1 -1
  164. package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-DrXgiSCq.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-DW31UyBp.js} +1 -1
  165. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-8b8V5bfO.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-BkQGKpye.js} +1 -1
  166. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-Dy7L6fMG.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-DzIXV3Ui.js} +1 -1
  167. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~k776hupu-D40EXhZx.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~k776hupu-Bbs7UJ5U.js} +2 -2
  168. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~dp08i1qy-CHrEOFl6.js → vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~dp08i1qy-DTwbEEcX.js} +1 -1
  169. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-BP1SKG0F.js → vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-d2oallMa.js} +1 -1
  170. package/build/assets/{vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~kyz9p27j-CyUbhpbm.js → vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~f2l2lr17-CDXvdvb2.js} +1 -1
  171. package/build/assets/{verification-settings-BtlTiHP8.js → verification-settings-CsbvQcYS.js} +1 -1
  172. package/build/assets/{vscode-tab-C0ShhiSU.js → vscode-tab-Zb-QbTuV.js} +1 -1
  173. package/build/assets/{waiting-for-runtime-message-DWPl_Yby.js → waiting-for-runtime-message-CntjExbU.js} +1 -1
  174. package/build/assets/{x-mark-CWI0f9yI.js → x-mark-CrpjscNc.js} +1 -1
  175. package/build/index.html +4 -4
  176. package/build/locales/ar/openhands.json +7 -0
  177. package/build/locales/ca/openhands.json +7 -0
  178. package/build/locales/de/openhands.json +7 -0
  179. package/build/locales/en/openhands.json +7 -0
  180. package/build/locales/es/openhands.json +7 -0
  181. package/build/locales/fr/openhands.json +7 -0
  182. package/build/locales/it/openhands.json +7 -0
  183. package/build/locales/ja/openhands.json +7 -0
  184. package/build/locales/ko-KR/openhands.json +7 -0
  185. package/build/locales/no/openhands.json +7 -0
  186. package/build/locales/pt/openhands.json +7 -0
  187. package/build/locales/tr/openhands.json +7 -0
  188. package/build/locales/uk/openhands.json +7 -0
  189. package/build/locales/zh-CN/openhands.json +7 -0
  190. package/build/locales/zh-TW/openhands.json +7 -0
  191. package/config/defaults.json +0 -4
  192. package/dist/api/agent-server-adapter.cjs +1 -1
  193. package/dist/api/agent-server-adapter.cjs.map +1 -1
  194. package/dist/api/agent-server-adapter.js +2 -1
  195. package/dist/api/agent-server-adapter.js.map +1 -1
  196. package/dist/api/agent-server-compatibility.cjs +1 -1
  197. package/dist/api/agent-server-compatibility.cjs.map +1 -1
  198. package/dist/api/agent-server-compatibility.d.ts +16 -0
  199. package/dist/api/agent-server-compatibility.js +31 -20
  200. package/dist/api/agent-server-compatibility.js.map +1 -1
  201. package/dist/api/agent-server-config.cjs +1 -1
  202. package/dist/api/agent-server-config.cjs.map +1 -1
  203. package/dist/api/agent-server-config.d.ts +45 -0
  204. package/dist/api/agent-server-config.js +49 -21
  205. package/dist/api/agent-server-config.js.map +1 -1
  206. package/dist/api/backend-registry/storage.cjs +1 -1
  207. package/dist/api/backend-registry/storage.cjs.map +1 -1
  208. package/dist/api/backend-registry/storage.js +34 -32
  209. package/dist/api/backend-registry/storage.js.map +1 -1
  210. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  211. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  212. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +5 -4
  213. package/dist/api/conversation-service/agent-server-conversation-service.api.js +70 -76
  214. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  215. package/dist/api/skills-service.cjs +1 -1
  216. package/dist/api/skills-service.cjs.map +1 -1
  217. package/dist/api/skills-service.d.ts +1 -1
  218. package/dist/api/skills-service.js +2 -2
  219. package/dist/api/skills-service.js.map +1 -1
  220. package/dist/components/features/backends/api-key-entry-screen.d.ts +10 -0
  221. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  222. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  223. package/dist/components/features/backends/backend-form-modal.d.ts +23 -2
  224. package/dist/components/features/backends/backend-form-modal.js +185 -173
  225. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  226. package/dist/components/features/browser/browser.cjs +1 -1
  227. package/dist/components/features/browser/browser.cjs.map +1 -1
  228. package/dist/components/features/browser/browser.js +10 -16
  229. package/dist/components/features/browser/browser.js.map +1 -1
  230. package/dist/components/features/conversation-panel/skills-modal.cjs +1 -1
  231. package/dist/components/features/conversation-panel/skills-modal.cjs.map +1 -1
  232. package/dist/components/features/conversation-panel/skills-modal.js +1 -1
  233. package/dist/components/features/conversation-panel/skills-modal.js.map +1 -1
  234. package/dist/components/features/mcp-page/install-server-modal.cjs +1 -1
  235. package/dist/components/features/mcp-page/install-server-modal.cjs.map +1 -1
  236. package/dist/components/features/mcp-page/install-server-modal.js +123 -116
  237. package/dist/components/features/mcp-page/install-server-modal.js.map +1 -1
  238. package/dist/components/features/mcp-page/installed-server-card.cjs +1 -1
  239. package/dist/components/features/mcp-page/installed-server-card.cjs.map +1 -1
  240. package/dist/components/features/mcp-page/installed-server-card.js +40 -40
  241. package/dist/components/features/mcp-page/installed-server-card.js.map +1 -1
  242. package/dist/components/features/mcp-page/marketplace-card.cjs +1 -1
  243. package/dist/components/features/mcp-page/marketplace-card.cjs.map +1 -1
  244. package/dist/components/features/mcp-page/marketplace-card.js +2 -3
  245. package/dist/components/features/mcp-page/marketplace-card.js.map +1 -1
  246. package/dist/components/features/mcp-page/marketplace-section.cjs +1 -1
  247. package/dist/components/features/mcp-page/marketplace-section.cjs.map +1 -1
  248. package/dist/components/features/mcp-page/marketplace-section.js +21 -21
  249. package/dist/components/features/mcp-page/marketplace-section.js.map +1 -1
  250. package/dist/components/features/onboarding/steps/setup-acp-secrets-step.d.ts +27 -0
  251. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.cjs +1 -1
  252. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.js +2 -0
  253. package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs +1 -1
  254. package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs.map +1 -1
  255. package/dist/components/features/settings/sdk-settings/sdk-section-page.d.ts +10 -1
  256. package/dist/components/features/settings/sdk-settings/sdk-section-page.js +87 -84
  257. package/dist/components/features/settings/sdk-settings/sdk-section-page.js.map +1 -1
  258. package/dist/constants/acp-providers.cjs +1 -1
  259. package/dist/constants/acp-providers.cjs.map +1 -1
  260. package/dist/constants/acp-providers.d.ts +25 -0
  261. package/dist/constants/acp-providers.js +1 -0
  262. package/dist/constants/acp-providers.js.map +1 -1
  263. package/dist/contexts/conversation-websocket-context.cjs +3 -3
  264. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  265. package/dist/contexts/conversation-websocket-context.js +177 -165
  266. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  267. package/dist/hooks/chat/use-model-interceptor.cjs.map +1 -1
  268. package/dist/hooks/chat/use-model-interceptor.js.map +1 -1
  269. package/dist/hooks/chat/use-slash-command.cjs +1 -1
  270. package/dist/hooks/chat/use-slash-command.cjs.map +1 -1
  271. package/dist/hooks/chat/use-slash-command.js +1 -1
  272. package/dist/hooks/chat/use-slash-command.js.map +1 -1
  273. package/dist/hooks/mutation/use-switch-llm-profile.cjs.map +1 -1
  274. package/dist/hooks/mutation/use-switch-llm-profile.d.ts +1 -1
  275. package/dist/hooks/mutation/use-switch-llm-profile.js.map +1 -1
  276. package/dist/hooks/query/use-config.cjs +1 -1
  277. package/dist/hooks/query/use-config.cjs.map +1 -1
  278. package/dist/hooks/query/use-config.js +10 -10
  279. package/dist/hooks/query/use-config.js.map +1 -1
  280. package/dist/hooks/query/use-conversation-skills.cjs +2 -0
  281. package/dist/hooks/query/use-conversation-skills.cjs.map +1 -0
  282. package/dist/hooks/query/use-conversation-skills.d.ts +7 -0
  283. package/dist/hooks/query/use-conversation-skills.js +8 -0
  284. package/dist/hooks/query/use-conversation-skills.js.map +1 -0
  285. package/dist/hooks/query/use-local-git-info.cjs +3 -3
  286. package/dist/hooks/query/use-local-git-info.cjs.map +1 -1
  287. package/dist/hooks/query/use-local-git-info.js +24 -25
  288. package/dist/hooks/query/use-local-git-info.js.map +1 -1
  289. package/dist/hooks/query/use-skills.cjs +1 -1
  290. package/dist/hooks/query/use-skills.cjs.map +1 -1
  291. package/dist/hooks/query/use-skills.d.ts +6 -1
  292. package/dist/hooks/query/use-skills.js +3 -3
  293. package/dist/hooks/query/use-skills.js.map +1 -1
  294. package/dist/i18n/declaration.cjs +1 -1
  295. package/dist/i18n/declaration.cjs.map +1 -1
  296. package/dist/i18n/declaration.d.ts +7 -0
  297. package/dist/i18n/declaration.js +1 -1
  298. package/dist/i18n/declaration.js.map +1 -1
  299. package/dist/i18n/translation.cjs +2 -2
  300. package/dist/i18n/translation.cjs.map +1 -1
  301. package/dist/i18n/translation.js +119 -0
  302. package/dist/i18n/translation.js.map +1 -1
  303. package/dist/locales/ar/openhands.json +7 -0
  304. package/dist/locales/ca/openhands.json +7 -0
  305. package/dist/locales/de/openhands.json +7 -0
  306. package/dist/locales/en/openhands.json +7 -0
  307. package/dist/locales/es/openhands.json +7 -0
  308. package/dist/locales/fr/openhands.json +7 -0
  309. package/dist/locales/it/openhands.json +7 -0
  310. package/dist/locales/ja/openhands.json +7 -0
  311. package/dist/locales/ko-KR/openhands.json +7 -0
  312. package/dist/locales/no/openhands.json +7 -0
  313. package/dist/locales/pt/openhands.json +7 -0
  314. package/dist/locales/tr/openhands.json +7 -0
  315. package/dist/locales/uk/openhands.json +7 -0
  316. package/dist/locales/zh-CN/openhands.json +7 -0
  317. package/dist/locales/zh-TW/openhands.json +7 -0
  318. package/dist/package.cjs +1 -1
  319. package/dist/package.cjs.map +1 -1
  320. package/dist/package.js +3 -3
  321. package/dist/package.js.map +1 -1
  322. package/dist/routes/conversation.cjs +1 -1
  323. package/dist/routes/conversation.cjs.map +1 -1
  324. package/dist/routes/conversation.js +61 -63
  325. package/dist/routes/conversation.js.map +1 -1
  326. package/dist/routes/mcp.cjs +1 -1
  327. package/dist/routes/mcp.cjs.map +1 -1
  328. package/dist/routes/mcp.js +64 -64
  329. package/dist/routes/mcp.js.map +1 -1
  330. package/dist/stores/browser-store.cjs +1 -1
  331. package/dist/stores/browser-store.cjs.map +1 -1
  332. package/dist/stores/browser-store.js +1 -1
  333. package/dist/stores/browser-store.js.map +1 -1
  334. package/dist/stores/use-event-store.cjs +1 -1
  335. package/dist/stores/use-event-store.cjs.map +1 -1
  336. package/dist/stores/use-event-store.d.ts +22 -0
  337. package/dist/stores/use-event-store.js +9 -1
  338. package/dist/stores/use-event-store.js.map +1 -1
  339. package/dist/ui/context-menu.d.ts +1 -1
  340. package/dist/ui/help-link.d.ts +1 -1
  341. package/dist/utils/mcp-marketplace-utils.cjs +1 -1
  342. package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
  343. package/dist/utils/mcp-marketplace-utils.d.ts +13 -22
  344. package/dist/utils/mcp-marketplace-utils.js +46 -28
  345. package/dist/utils/mcp-marketplace-utils.js.map +1 -1
  346. package/dist/utils/sdk-settings-schema.cjs +1 -1
  347. package/dist/utils/sdk-settings-schema.cjs.map +1 -1
  348. package/dist/utils/sdk-settings-schema.d.ts +1 -0
  349. package/dist/utils/sdk-settings-schema.js +1 -1
  350. package/dist/utils/sdk-settings-schema.js.map +1 -1
  351. package/package.json +3 -3
  352. package/scripts/dev-safe.mjs +94 -57
  353. package/scripts/dev-static.mjs +2 -3
  354. package/scripts/dev-with-automation.mjs +98 -67
  355. package/scripts/static-server.mjs +77 -35
  356. package/tools/canvas_ui_tool.py +4 -0
  357. package/build/assets/acp-providers-DauuOsW9.js +0 -1
  358. package/build/assets/add-backend-modal-KMmPQNZU.js +0 -1
  359. package/build/assets/agent-server-client-options-DT2GP6VJ.js +0 -1
  360. package/build/assets/agent-server-compatibility-2aOx5iWd.js +0 -1
  361. package/build/assets/agent-server-conversation-service.api-DSl9G5UR.js +0 -5
  362. package/build/assets/backend-form-modal-K6IMCr3p.js +0 -1
  363. package/build/assets/browser-DKG63inJ.js +0 -5
  364. package/build/assets/browser-store-C3AqxAO7.js +0 -1
  365. package/build/assets/browser-tab-B_BuTvrO.js +0 -1
  366. package/build/assets/conversation-BD5WemJI.js +0 -19
  367. package/build/assets/conversation-C47K62n8.js +0 -1
  368. package/build/assets/conversation-panel-Dn-S56Gk.js +0 -1
  369. package/build/assets/conversation-websocket-context-Ywrxd_9p.js +0 -3
  370. package/build/assets/declaration-D378OjpZ.js +0 -1
  371. package/build/assets/files-tab-B3A1NDlZ.js +0 -1
  372. package/build/assets/git-control-bar-branch-button-CcIpmyfM.js +0 -27
  373. package/build/assets/home-dIzxi5Dd.js +0 -1
  374. package/build/assets/install-server-modal-z5VaHeXd.js +0 -1
  375. package/build/assets/llm-settings-2036m7Wt.js +0 -1
  376. package/build/assets/llm-settings-CcHqGOYL.js +0 -1
  377. package/build/assets/manifest-97e839da.js +0 -1
  378. package/build/assets/mcp-C06YssEI.js +0 -9
  379. package/build/assets/root-BS1Td78t.js +0 -2
  380. package/build/assets/sdk-section-page-CJW0G04-.js +0 -1
  381. package/build/assets/secrets-settings-MLXqOtX2.js +0 -1
  382. package/build/assets/use-active-conversation-D15D9GgR.js +0 -1
  383. package/build/assets/use-config-BSu_53GL.js +0 -1
  384. package/build/assets/use-conversation-id-DajhCn2A.js +0 -1
  385. package/build/assets/use-is-authed-hXC8vxgT.js +0 -1
  386. package/build/assets/use-runtime-is-ready-XFbT16BD.js +0 -1
  387. package/build/assets/use-skills-Xe0vjPMt.js +0 -1
  388. package/build/assets/use-unified-vscode-url-BOsIOd-b.js +0 -1
  389. package/build/assets/use-user-conversation-Mc0mQgkl.js +0 -1
  390. /package/build/assets/{automation-XLxhq3I8.js → automation-IdgZq6ZK.js} +0 -0
  391. /package/build/assets/{common-SMkEaBSr.js → common-DR1t-EeP.js} +0 -0
  392. /package/build/assets/{conversation-state-store-Bc0slAjL.js → conversation-state-store-u5jepov0.js} +0 -0
  393. /package/build/assets/{dist-yMQV8IUk.js → dist-BxBP7tFD.js} +0 -0
  394. /package/build/assets/{git-status-mapper-BI8FyUVp.js → git-status-mapper-DnL9OC8_.js} +0 -0
  395. /package/build/assets/{handle-capture-consent-BfZATzpI.js → handle-capture-consent-3XrjZ8wi.js} +0 -0
  396. /package/build/assets/{iconBase-C7N9pPOs.js → iconBase-DE30Zj_-.js} +0 -0
  397. /package/build/assets/{settings-D5am1n6X.js → settings-D_H-qsRm.js} +0 -0
  398. /package/build/assets/{settings-like-page-layout-classes-Bn-M9oOa.js → settings-like-page-layout-classes-I0BDBEoq.js} +0 -0
  399. /package/build/assets/{settings-utils-BsvSU3OM.js → settings-utils-B6Nl07io.js} +0 -0
  400. /package/build/assets/{sidebar-store-cOeaKmIm.js → sidebar-store-Uy3v0AOV.js} +0 -0
  401. /package/build/assets/{use-breakpoint-B86yKT9n.js → use-breakpoint-DbJ6FkQ-.js} +0 -0
  402. /package/build/assets/{use-click-outside-element-835W9pC6.js → use-click-outside-element-DffgWWoZ.js} +0 -0
  403. /package/build/assets/{vendor~browser-BpdPBhgZ.js → vendor~browser-DDiZgqD3.js} +0 -0
  404. /package/build/assets/{vendor~conversation-panel~conversation~alert-banner-Df7_G0zR.js → vendor~conversation-panel~conversation~alert-banner-DbvX3OcM.js} +0 -0
  405. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~b4cctr4k-B7YVdv1X.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~g56ukk6u-DsSvIDZQ.js} +0 -0
  406. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~i9dbt75i-CI82Did1.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~hkqzh1hb-BZ0HXuHD.js} +0 -0
  407. /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~pfbaerbd-zhv9fooy.js → vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~ninslayh-D9P8e98a.js} +0 -0
  408. /package/build/assets/{vendor~terminal-BUxzHKcC.js → vendor~terminal-DUrOWGFE.js} +0 -0
  409. /package/build/assets/{vscode-url-helper-jesbpos5.js → vscode-url-helper-Cwy1A62q.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"agent-server-compatibility.cjs","names":[],"sources":["../../src/api/agent-server-compatibility.ts"],"sourcesContent":["import { ServerClient } from \"@openhands/typescript-client/clients\";\nimport type { ServerInfo as BaseServerInfo } from \"@openhands/typescript-client\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport { getEffectiveLocalBackend } from \"#/api/backend-registry/active-store\";\n\nconst AGENT_SERVER_INFO_TIMEOUT_MS = 5000;\n\nexport interface AgentServerInfo extends BaseServerInfo {\n usable_tools?: string[] | null;\n}\n\nlet cachedAgentServerInfo: AgentServerInfo | null = null;\n\nconst getAdvertisedTools = (serverInfo: AgentServerInfo | null) => {\n if (Array.isArray(serverInfo?.usable_tools)) {\n return serverInfo.usable_tools;\n }\n return null;\n};\n\nexport class AgentServerUnavailableError extends Error {\n readonly details: string | null;\n\n constructor(details?: string | null) {\n super(\n \"Agent server not found. Could not connect to the configured agent server. Start a compatible agent server and reload the page.\",\n );\n this.name = \"AgentServerUnavailableError\";\n this.details = details ?? null;\n }\n}\n\nexport const isAgentServerUnavailableError = (\n error: unknown,\n): error is AgentServerUnavailableError =>\n error instanceof AgentServerUnavailableError ||\n (typeof error === \"object\" &&\n error !== null &&\n \"name\" in error &&\n error.name === \"AgentServerUnavailableError\");\n\nexport function clearCachedAgentServerInfo() {\n cachedAgentServerInfo = null;\n}\n\nexport function isAgentServerToolAvailable(toolName: string) {\n const availableTools = getAdvertisedTools(cachedAgentServerInfo);\n if (!Array.isArray(availableTools)) {\n return true;\n }\n return availableTools.includes(toolName);\n}\n\nfunction isSdkHttpError(error: unknown) {\n return (\n error instanceof Error &&\n error.name === \"HttpError\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n );\n}\n\nexport async function loadAgentServerInfo() {\n // The probe is a *local* agent-server concern — it verifies the runtime\n // hosting the GUI is reachable. It must NEVER run against the active\n // backend when that backend is cloud, because cloud hosts don't\n // expose /api/server_info and would fail with a CORS error besides.\n const local = getEffectiveLocalBackend();\n let serverInfo: AgentServerInfo;\n\n try {\n serverInfo = (await new ServerClient(\n getAgentServerClientOptions({\n host: local.host,\n sessionApiKey: local.apiKey || null,\n timeout: AGENT_SERVER_INFO_TIMEOUT_MS,\n }),\n ).getServerInfo()) as AgentServerInfo;\n } catch (error) {\n clearCachedAgentServerInfo();\n if (isSdkHttpError(error)) {\n throw error;\n }\n\n const details = error instanceof Error ? error.message : null;\n throw new AgentServerUnavailableError(details);\n }\n\n cachedAgentServerInfo = serverInfo;\n return serverInfo;\n}\n"],"mappings":"2OAKA,IAAM,EAA+B,IAMjC,EAAgD,KAE9C,EAAsB,GACtB,MAAM,QAAQ,GAAY,aAAa,CAClC,EAAW,aAEb,KAGI,EAAb,cAAiD,KAAM,CACrD,QAEA,YAAY,EAAyB,CACnC,MACE,iIACD,CACD,KAAK,KAAO,8BACZ,KAAK,QAAU,GAAW,OAIjB,EACX,GAEA,aAAiB,GAChB,OAAO,GAAU,YAChB,GACA,SAAU,GACV,EAAM,OAAS,8BAEnB,SAAgB,GAA6B,CAC3C,EAAwB,KAG1B,SAAgB,EAA2B,EAAkB,CAC3D,IAAM,EAAiB,EAAmB,EAAsB,CAIhE,OAHK,MAAM,QAAQ,EAAe,CAG3B,EAAe,SAAS,EAAS,CAF/B,GAKX,SAAS,EAAe,EAAgB,CACtC,OACE,aAAiB,OACjB,EAAM,OAAS,aACf,WAAY,GACZ,OAAO,EAAM,QAAW,SAI5B,eAAsB,GAAsB,CAK1C,IAAM,EAAQ,EAAA,0BAA0B,CACpC,EAEJ,GAAI,CACF,EAAc,MAAM,IAAI,EAAA,aACtB,EAAA,4BAA4B,CAC1B,KAAM,EAAM,KACZ,cAAe,EAAM,QAAU,KAC/B,QAAS,EACV,CAAC,CACH,CAAC,eAAe,OACV,EAAO,CAOd,MANA,GAA4B,CACxB,EAAe,EAAM,CACjB,EAIF,IAAI,EADM,aAAiB,MAAQ,EAAM,QAAU,KACX,CAIhD,MADA,GAAwB,EACjB"}
1
+ {"version":3,"file":"agent-server-compatibility.cjs","names":[],"sources":["../../src/api/agent-server-compatibility.ts"],"sourcesContent":["import {\n ServerClient,\n SettingsClient,\n} from \"@openhands/typescript-client/clients\";\nimport type { ServerInfo as BaseServerInfo } from \"@openhands/typescript-client\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport { isAuthRequired } from \"#/api/agent-server-config\";\nimport { getEffectiveLocalBackend } from \"#/api/backend-registry/active-store\";\n\nconst AGENT_SERVER_INFO_TIMEOUT_MS = 5000;\n\nexport interface AgentServerInfo extends BaseServerInfo {\n usable_tools?: string[] | null;\n}\n\nlet cachedAgentServerInfo: AgentServerInfo | null = null;\n\nconst getAdvertisedTools = (serverInfo: AgentServerInfo | null) => {\n if (Array.isArray(serverInfo?.usable_tools)) {\n return serverInfo.usable_tools;\n }\n return null;\n};\n\nexport class AgentServerUnavailableError extends Error {\n readonly details: string | null;\n\n constructor(details?: string | null) {\n super(\n \"Agent server not found. Could not connect to the configured agent server. Start a compatible agent server and reload the page.\",\n );\n this.name = \"AgentServerUnavailableError\";\n this.details = details ?? null;\n }\n}\n\nexport const isAgentServerUnavailableError = (\n error: unknown,\n): error is AgentServerUnavailableError =>\n error instanceof AgentServerUnavailableError ||\n (typeof error === \"object\" &&\n error !== null &&\n \"name\" in error &&\n error.name === \"AgentServerUnavailableError\");\n\n/**\n * Returns true when the agent-server probe failed with HTTP 401.\n * In public mode this means the stored key is stale (server restarted\n * with a different `LOCAL_BACKEND_API_KEY`). Only meaningful when\n * auth is required — a 401 in local mode is a misconfiguration, not a\n * key-rotation event. Uses {@link isAuthRequired} so both the build-time\n * `VITE_AUTH_REQUIRED` flag and the runtime `window.__AGENT_CANVAS_AUTH_REQUIRED__`\n * injection (used by pre-built static binaries) are honoured.\n */\nexport const isAgentServerAuthError = (error: unknown): boolean =>\n isAuthRequired() && isSdkHttpStatusError(error, 401);\n\nexport function clearCachedAgentServerInfo() {\n cachedAgentServerInfo = null;\n}\n\nexport function isAgentServerToolAvailable(toolName: string) {\n const availableTools = getAdvertisedTools(cachedAgentServerInfo);\n if (!Array.isArray(availableTools)) {\n return true;\n }\n return availableTools.includes(toolName);\n}\n\nexport function isSdkHttpError(error: unknown) {\n return (\n error instanceof Error &&\n error.name === \"HttpError\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n );\n}\n\n/**\n * Narrows an SDK HTTP error to a specific status code.\n * Use instead of manually casting `(err as { status: number }).status`.\n */\nexport function isSdkHttpStatusError(error: unknown, status: number): boolean {\n return (\n isSdkHttpError(error) && (error as { status: number }).status === status\n );\n}\n\nexport async function loadAgentServerInfo() {\n // The probe is a *local* agent-server concern — it verifies the runtime\n // hosting the GUI is reachable. It must NEVER run against the active\n // backend when that backend is cloud, because cloud hosts don't\n // expose /api/server_info and would fail with a CORS error besides.\n const local = getEffectiveLocalBackend();\n const clientOptions = getAgentServerClientOptions({\n host: local.host,\n sessionApiKey: local.apiKey || null,\n timeout: AGENT_SERVER_INFO_TIMEOUT_MS,\n });\n let serverInfo: AgentServerInfo;\n\n try {\n serverInfo = (await new ServerClient(\n clientOptions,\n ).getServerInfo()) as AgentServerInfo;\n } catch (error) {\n clearCachedAgentServerInfo();\n if (isSdkHttpError(error)) {\n throw error;\n }\n\n const details = error instanceof Error ? error.message : null;\n throw new AgentServerUnavailableError(details);\n }\n\n // /server_info is unprotected, so a stale session key still gets 200.\n // In public mode, validate the key against a protected endpoint so a\n // server restart with a new LOCAL_BACKEND_API_KEY surfaces immediately\n // instead of letting the app load and fail on every subsequent call.\n if (isAuthRequired()) {\n try {\n await new SettingsClient(clientOptions).getSettings();\n } catch (error) {\n // Only rethrow 401 — that means the stored key is invalid /\n // rotated. Other HTTP errors (403, 5xx) and non-HTTP errors\n // (network, timeout) are swallowed: the server *is* up (we just\n // reached /server_info), so let the app proceed with an\n // unvalidated key rather than blocking the UI.\n // NOTE: If the connection drops between the /server_info and\n // getSettings() probes, the app loads with an unvalidated key and\n // subsequent 401s won't trigger the auth screen (they come from\n // React Query hooks, not this bootstrap path). Acceptable for now\n // since the window is narrow and a page refresh recovers.\n if (isSdkHttpStatusError(error, 401)) {\n throw error;\n }\n\n console.warn(\n \"[agent-server] getSettings() probe failed (non-401):\",\n error,\n );\n }\n }\n\n cachedAgentServerInfo = serverInfo;\n return serverInfo;\n}\n"],"mappings":"4WASA,IAAM,EAA+B,IAMjC,EAAgD,KAE9C,EAAsB,GACtB,MAAM,QAAQ,GAAY,aAAa,CAClC,EAAW,aAEb,KAGI,EAAb,cAAiD,KAAM,CACrD,QAEA,YAAY,EAAyB,CACnC,MACE,iIACD,CACD,KAAK,KAAO,8BACZ,KAAK,QAAU,GAAW,OAIjB,EACX,GAEA,aAAiB,GAChB,OAAO,GAAU,YAChB,GACA,SAAU,GACV,EAAM,OAAS,8BAWN,EAA0B,GACrC,EAAA,gBAAgB,EAAI,EAAqB,EAAO,IAAI,CAEtD,SAAgB,GAA6B,CAC3C,EAAwB,KAG1B,SAAgB,EAA2B,EAAkB,CAC3D,IAAM,EAAiB,EAAmB,EAAsB,CAIhE,OAHK,MAAM,QAAQ,EAAe,CAG3B,EAAe,SAAS,EAAS,CAF/B,GAKX,SAAgB,EAAe,EAAgB,CAC7C,OACE,aAAiB,OACjB,EAAM,OAAS,aACf,WAAY,GACZ,OAAO,EAAM,QAAW,SAQ5B,SAAgB,EAAqB,EAAgB,EAAyB,CAC5E,OACE,EAAe,EAAM,EAAK,EAA6B,SAAW,EAItE,eAAsB,GAAsB,CAK1C,IAAM,EAAQ,EAAA,0BAA0B,CAClC,EAAgB,EAAA,4BAA4B,CAChD,KAAM,EAAM,KACZ,cAAe,EAAM,QAAU,KAC/B,QAAS,EACV,CAAC,CACE,EAEJ,GAAI,CACF,EAAc,MAAM,IAAI,EAAA,aACtB,EACD,CAAC,eAAe,OACV,EAAO,CAOd,MANA,GAA4B,CACxB,EAAe,EAAM,CACjB,EAIF,IAAI,EADM,aAAiB,MAAQ,EAAM,QAAU,KACX,CAOhD,GAAI,EAAA,gBAAgB,CAClB,GAAI,CACF,MAAM,IAAI,EAAA,eAAe,EAAc,CAAC,aAAa,OAC9C,EAAO,CAWd,GAAI,EAAqB,EAAO,IAAI,CAClC,MAAM,EAGR,QAAQ,KACN,uDACA,EACD,CAKL,MADA,GAAwB,EACjB"}
@@ -7,6 +7,22 @@ export declare class AgentServerUnavailableError extends Error {
7
7
  constructor(details?: string | null);
8
8
  }
9
9
  export declare const isAgentServerUnavailableError: (error: unknown) => error is AgentServerUnavailableError;
10
+ /**
11
+ * Returns true when the agent-server probe failed with HTTP 401.
12
+ * In public mode this means the stored key is stale (server restarted
13
+ * with a different `LOCAL_BACKEND_API_KEY`). Only meaningful when
14
+ * auth is required — a 401 in local mode is a misconfiguration, not a
15
+ * key-rotation event. Uses {@link isAuthRequired} so both the build-time
16
+ * `VITE_AUTH_REQUIRED` flag and the runtime `window.__AGENT_CANVAS_AUTH_REQUIRED__`
17
+ * injection (used by pre-built static binaries) are honoured.
18
+ */
19
+ export declare const isAgentServerAuthError: (error: unknown) => boolean;
10
20
  export declare function clearCachedAgentServerInfo(): void;
11
21
  export declare function isAgentServerToolAvailable(toolName: string): boolean;
22
+ export declare function isSdkHttpError(error: unknown): boolean;
23
+ /**
24
+ * Narrows an SDK HTTP error to a specific status code.
25
+ * Use instead of manually casting `(err as { status: number }).status`.
26
+ */
27
+ export declare function isSdkHttpStatusError(error: unknown, status: number): boolean;
12
28
  export declare function loadAgentServerInfo(): Promise<AgentServerInfo>;
@@ -1,37 +1,48 @@
1
- import { getEffectiveLocalBackend as e } from "./backend-registry/active-store.js";
2
- import { ServerClient as t } from "../node_modules/@openhands/typescript-client/dist/client/server-client.js";
3
- import { getAgentServerClientOptions as n } from "./agent-server-client-options.js";
1
+ import { isAuthRequired as e } from "./agent-server-config.js";
2
+ import { getEffectiveLocalBackend as t } from "./backend-registry/active-store.js";
3
+ import { ServerClient as n } from "../node_modules/@openhands/typescript-client/dist/client/server-client.js";
4
+ import { SettingsClient as r } from "../node_modules/@openhands/typescript-client/dist/client/settings-client.js";
5
+ import { getAgentServerClientOptions as i } from "./agent-server-client-options.js";
4
6
  //#region src/api/agent-server-compatibility.ts
5
- var r = 5e3, i = null, a = (e) => Array.isArray(e?.usable_tools) ? e.usable_tools : null, o = class extends Error {
7
+ var a = 5e3, o = null, s = (e) => Array.isArray(e?.usable_tools) ? e.usable_tools : null, c = class extends Error {
6
8
  details;
7
9
  constructor(e) {
8
10
  super("Agent server not found. Could not connect to the configured agent server. Start a compatible agent server and reload the page."), this.name = "AgentServerUnavailableError", this.details = e ?? null;
9
11
  }
10
- }, s = (e) => e instanceof o || typeof e == "object" && !!e && "name" in e && e.name === "AgentServerUnavailableError";
11
- function c() {
12
- i = null;
12
+ }, l = (e) => e instanceof c || typeof e == "object" && !!e && "name" in e && e.name === "AgentServerUnavailableError", u = (t) => e() && m(t, 401);
13
+ function d() {
14
+ o = null;
13
15
  }
14
- function l(e) {
15
- let t = a(i);
16
+ function f(e) {
17
+ let t = s(o);
16
18
  return Array.isArray(t) ? t.includes(e) : !0;
17
19
  }
18
- function u(e) {
20
+ function p(e) {
19
21
  return e instanceof Error && e.name === "HttpError" && "status" in e && typeof e.status == "number";
20
22
  }
21
- async function d() {
22
- let a = e(), s;
23
+ function m(e, t) {
24
+ return p(e) && e.status === t;
25
+ }
26
+ async function h() {
27
+ let s = t(), l = i({
28
+ host: s.host,
29
+ sessionApiKey: s.apiKey || null,
30
+ timeout: a
31
+ }), u;
23
32
  try {
24
- s = await new t(n({
25
- host: a.host,
26
- sessionApiKey: a.apiKey || null,
27
- timeout: r
28
- })).getServerInfo();
33
+ u = await new n(l).getServerInfo();
34
+ } catch (e) {
35
+ throw d(), p(e) ? e : new c(e instanceof Error ? e.message : null);
36
+ }
37
+ if (e()) try {
38
+ await new r(l).getSettings();
29
39
  } catch (e) {
30
- throw c(), u(e) ? e : new o(e instanceof Error ? e.message : null);
40
+ if (m(e, 401)) throw e;
41
+ console.warn("[agent-server] getSettings() probe failed (non-401):", e);
31
42
  }
32
- return i = s, s;
43
+ return o = u, u;
33
44
  }
34
45
  //#endregion
35
- export { l as isAgentServerToolAvailable, s as isAgentServerUnavailableError, d as loadAgentServerInfo };
46
+ export { u as isAgentServerAuthError, f as isAgentServerToolAvailable, l as isAgentServerUnavailableError, h as loadAgentServerInfo };
36
47
 
37
48
  //# sourceMappingURL=agent-server-compatibility.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-server-compatibility.js","names":[],"sources":["../../src/api/agent-server-compatibility.ts"],"sourcesContent":["import { ServerClient } from \"@openhands/typescript-client/clients\";\nimport type { ServerInfo as BaseServerInfo } from \"@openhands/typescript-client\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport { getEffectiveLocalBackend } from \"#/api/backend-registry/active-store\";\n\nconst AGENT_SERVER_INFO_TIMEOUT_MS = 5000;\n\nexport interface AgentServerInfo extends BaseServerInfo {\n usable_tools?: string[] | null;\n}\n\nlet cachedAgentServerInfo: AgentServerInfo | null = null;\n\nconst getAdvertisedTools = (serverInfo: AgentServerInfo | null) => {\n if (Array.isArray(serverInfo?.usable_tools)) {\n return serverInfo.usable_tools;\n }\n return null;\n};\n\nexport class AgentServerUnavailableError extends Error {\n readonly details: string | null;\n\n constructor(details?: string | null) {\n super(\n \"Agent server not found. Could not connect to the configured agent server. Start a compatible agent server and reload the page.\",\n );\n this.name = \"AgentServerUnavailableError\";\n this.details = details ?? null;\n }\n}\n\nexport const isAgentServerUnavailableError = (\n error: unknown,\n): error is AgentServerUnavailableError =>\n error instanceof AgentServerUnavailableError ||\n (typeof error === \"object\" &&\n error !== null &&\n \"name\" in error &&\n error.name === \"AgentServerUnavailableError\");\n\nexport function clearCachedAgentServerInfo() {\n cachedAgentServerInfo = null;\n}\n\nexport function isAgentServerToolAvailable(toolName: string) {\n const availableTools = getAdvertisedTools(cachedAgentServerInfo);\n if (!Array.isArray(availableTools)) {\n return true;\n }\n return availableTools.includes(toolName);\n}\n\nfunction isSdkHttpError(error: unknown) {\n return (\n error instanceof Error &&\n error.name === \"HttpError\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n );\n}\n\nexport async function loadAgentServerInfo() {\n // The probe is a *local* agent-server concern — it verifies the runtime\n // hosting the GUI is reachable. It must NEVER run against the active\n // backend when that backend is cloud, because cloud hosts don't\n // expose /api/server_info and would fail with a CORS error besides.\n const local = getEffectiveLocalBackend();\n let serverInfo: AgentServerInfo;\n\n try {\n serverInfo = (await new ServerClient(\n getAgentServerClientOptions({\n host: local.host,\n sessionApiKey: local.apiKey || null,\n timeout: AGENT_SERVER_INFO_TIMEOUT_MS,\n }),\n ).getServerInfo()) as AgentServerInfo;\n } catch (error) {\n clearCachedAgentServerInfo();\n if (isSdkHttpError(error)) {\n throw error;\n }\n\n const details = error instanceof Error ? error.message : null;\n throw new AgentServerUnavailableError(details);\n }\n\n cachedAgentServerInfo = serverInfo;\n return serverInfo;\n}\n"],"mappings":";;;;AAKA,IAAM,IAA+B,KAMjC,IAAgD,MAE9C,KAAsB,MACtB,MAAM,QAAQ,GAAY,aAAa,GAClC,EAAW,eAEb,MAGI,IAAb,cAAiD,MAAM;CACrD;CAEA,YAAY,GAAyB;AAKnC,EAJA,MACE,iIACD,EACD,KAAK,OAAO,+BACZ,KAAK,UAAU,KAAW;;GAIjB,KACX,MAEA,aAAiB,KAChB,OAAO,KAAU,cAChB,KACA,UAAU,KACV,EAAM,SAAS;AAEnB,SAAgB,IAA6B;AAC3C,KAAwB;;AAG1B,SAAgB,EAA2B,GAAkB;CAC3D,IAAM,IAAiB,EAAmB,EAAsB;AAIhE,QAHK,MAAM,QAAQ,EAAe,GAG3B,EAAe,SAAS,EAAS,GAF/B;;AAKX,SAAS,EAAe,GAAgB;AACtC,QACE,aAAiB,SACjB,EAAM,SAAS,eACf,YAAY,KACZ,OAAO,EAAM,UAAW;;AAI5B,eAAsB,IAAsB;CAK1C,IAAM,IAAQ,GAA0B,EACpC;AAEJ,KAAI;AACF,MAAc,MAAM,IAAI,EACtB,EAA4B;GAC1B,MAAM,EAAM;GACZ,eAAe,EAAM,UAAU;GAC/B,SAAS;GACV,CAAC,CACH,CAAC,eAAe;UACV,GAAO;AAOd,QANA,GAA4B,EACxB,EAAe,EAAM,GACjB,IAIF,IAAI,EADM,aAAiB,QAAQ,EAAM,UAAU,KACX;;AAIhD,QADA,IAAwB,GACjB"}
1
+ {"version":3,"file":"agent-server-compatibility.js","names":[],"sources":["../../src/api/agent-server-compatibility.ts"],"sourcesContent":["import {\n ServerClient,\n SettingsClient,\n} from \"@openhands/typescript-client/clients\";\nimport type { ServerInfo as BaseServerInfo } from \"@openhands/typescript-client\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport { isAuthRequired } from \"#/api/agent-server-config\";\nimport { getEffectiveLocalBackend } from \"#/api/backend-registry/active-store\";\n\nconst AGENT_SERVER_INFO_TIMEOUT_MS = 5000;\n\nexport interface AgentServerInfo extends BaseServerInfo {\n usable_tools?: string[] | null;\n}\n\nlet cachedAgentServerInfo: AgentServerInfo | null = null;\n\nconst getAdvertisedTools = (serverInfo: AgentServerInfo | null) => {\n if (Array.isArray(serverInfo?.usable_tools)) {\n return serverInfo.usable_tools;\n }\n return null;\n};\n\nexport class AgentServerUnavailableError extends Error {\n readonly details: string | null;\n\n constructor(details?: string | null) {\n super(\n \"Agent server not found. Could not connect to the configured agent server. Start a compatible agent server and reload the page.\",\n );\n this.name = \"AgentServerUnavailableError\";\n this.details = details ?? null;\n }\n}\n\nexport const isAgentServerUnavailableError = (\n error: unknown,\n): error is AgentServerUnavailableError =>\n error instanceof AgentServerUnavailableError ||\n (typeof error === \"object\" &&\n error !== null &&\n \"name\" in error &&\n error.name === \"AgentServerUnavailableError\");\n\n/**\n * Returns true when the agent-server probe failed with HTTP 401.\n * In public mode this means the stored key is stale (server restarted\n * with a different `LOCAL_BACKEND_API_KEY`). Only meaningful when\n * auth is required — a 401 in local mode is a misconfiguration, not a\n * key-rotation event. Uses {@link isAuthRequired} so both the build-time\n * `VITE_AUTH_REQUIRED` flag and the runtime `window.__AGENT_CANVAS_AUTH_REQUIRED__`\n * injection (used by pre-built static binaries) are honoured.\n */\nexport const isAgentServerAuthError = (error: unknown): boolean =>\n isAuthRequired() && isSdkHttpStatusError(error, 401);\n\nexport function clearCachedAgentServerInfo() {\n cachedAgentServerInfo = null;\n}\n\nexport function isAgentServerToolAvailable(toolName: string) {\n const availableTools = getAdvertisedTools(cachedAgentServerInfo);\n if (!Array.isArray(availableTools)) {\n return true;\n }\n return availableTools.includes(toolName);\n}\n\nexport function isSdkHttpError(error: unknown) {\n return (\n error instanceof Error &&\n error.name === \"HttpError\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n );\n}\n\n/**\n * Narrows an SDK HTTP error to a specific status code.\n * Use instead of manually casting `(err as { status: number }).status`.\n */\nexport function isSdkHttpStatusError(error: unknown, status: number): boolean {\n return (\n isSdkHttpError(error) && (error as { status: number }).status === status\n );\n}\n\nexport async function loadAgentServerInfo() {\n // The probe is a *local* agent-server concern — it verifies the runtime\n // hosting the GUI is reachable. It must NEVER run against the active\n // backend when that backend is cloud, because cloud hosts don't\n // expose /api/server_info and would fail with a CORS error besides.\n const local = getEffectiveLocalBackend();\n const clientOptions = getAgentServerClientOptions({\n host: local.host,\n sessionApiKey: local.apiKey || null,\n timeout: AGENT_SERVER_INFO_TIMEOUT_MS,\n });\n let serverInfo: AgentServerInfo;\n\n try {\n serverInfo = (await new ServerClient(\n clientOptions,\n ).getServerInfo()) as AgentServerInfo;\n } catch (error) {\n clearCachedAgentServerInfo();\n if (isSdkHttpError(error)) {\n throw error;\n }\n\n const details = error instanceof Error ? error.message : null;\n throw new AgentServerUnavailableError(details);\n }\n\n // /server_info is unprotected, so a stale session key still gets 200.\n // In public mode, validate the key against a protected endpoint so a\n // server restart with a new LOCAL_BACKEND_API_KEY surfaces immediately\n // instead of letting the app load and fail on every subsequent call.\n if (isAuthRequired()) {\n try {\n await new SettingsClient(clientOptions).getSettings();\n } catch (error) {\n // Only rethrow 401 — that means the stored key is invalid /\n // rotated. Other HTTP errors (403, 5xx) and non-HTTP errors\n // (network, timeout) are swallowed: the server *is* up (we just\n // reached /server_info), so let the app proceed with an\n // unvalidated key rather than blocking the UI.\n // NOTE: If the connection drops between the /server_info and\n // getSettings() probes, the app loads with an unvalidated key and\n // subsequent 401s won't trigger the auth screen (they come from\n // React Query hooks, not this bootstrap path). Acceptable for now\n // since the window is narrow and a page refresh recovers.\n if (isSdkHttpStatusError(error, 401)) {\n throw error;\n }\n\n console.warn(\n \"[agent-server] getSettings() probe failed (non-401):\",\n error,\n );\n }\n }\n\n cachedAgentServerInfo = serverInfo;\n return serverInfo;\n}\n"],"mappings":";;;;;;AASA,IAAM,IAA+B,KAMjC,IAAgD,MAE9C,KAAsB,MACtB,MAAM,QAAQ,GAAY,aAAa,GAClC,EAAW,eAEb,MAGI,IAAb,cAAiD,MAAM;CACrD;CAEA,YAAY,GAAyB;AAKnC,EAJA,MACE,iIACD,EACD,KAAK,OAAO,+BACZ,KAAK,UAAU,KAAW;;GAIjB,KACX,MAEA,aAAiB,KAChB,OAAO,KAAU,cAChB,KACA,UAAU,KACV,EAAM,SAAS,+BAWN,KAA0B,MACrC,GAAgB,IAAI,EAAqB,GAAO,IAAI;AAEtD,SAAgB,IAA6B;AAC3C,KAAwB;;AAG1B,SAAgB,EAA2B,GAAkB;CAC3D,IAAM,IAAiB,EAAmB,EAAsB;AAIhE,QAHK,MAAM,QAAQ,EAAe,GAG3B,EAAe,SAAS,EAAS,GAF/B;;AAKX,SAAgB,EAAe,GAAgB;AAC7C,QACE,aAAiB,SACjB,EAAM,SAAS,eACf,YAAY,KACZ,OAAO,EAAM,UAAW;;AAQ5B,SAAgB,EAAqB,GAAgB,GAAyB;AAC5E,QACE,EAAe,EAAM,IAAK,EAA6B,WAAW;;AAItE,eAAsB,IAAsB;CAK1C,IAAM,IAAQ,GAA0B,EAClC,IAAgB,EAA4B;EAChD,MAAM,EAAM;EACZ,eAAe,EAAM,UAAU;EAC/B,SAAS;EACV,CAAC,EACE;AAEJ,KAAI;AACF,MAAc,MAAM,IAAI,EACtB,EACD,CAAC,eAAe;UACV,GAAO;AAOd,QANA,GAA4B,EACxB,EAAe,EAAM,GACjB,IAIF,IAAI,EADM,aAAiB,QAAQ,EAAM,UAAU,KACX;;AAOhD,KAAI,GAAgB,CAClB,KAAI;AACF,QAAM,IAAI,EAAe,EAAc,CAAC,aAAa;UAC9C,GAAO;AAWd,MAAI,EAAqB,GAAO,IAAI,CAClC,OAAM;AAGR,UAAQ,KACN,wDACA,EACD;;AAKL,QADA,IAAwB,GACjB"}
@@ -1,2 +1,2 @@
1
- require(`../_virtual/_rolldown/runtime.cjs`);var e=`openhands-agent-server-config`,t=`workspace/project`;function n(){if(typeof window>`u`)return{};try{let t=window.localStorage.getItem(e);return t?JSON.parse(t)??{}:{}}catch{return{}}}function r(e){return e?.trim()||null}function i(e){if(!e)return null;let t=e.trim().replace(/\/$/,``);return t?/^https?:\/\//i.test(t)?t:typeof window<`u`?`${window.location.protocol}//${t}`:`http://${t}`:null}function a(){return i(n().baseUrl)||i(void 0)}function o(){return r(n().sessionApiKey)||r(void 0)}function s(e){if(typeof window>`u`)return!1;try{let t=new URL(e),n=new Set([`127.0.0.1`,`localhost`,`0.0.0.0`]),r=window.location.hostname;return n.has(t.hostname)&&!n.has(r)}catch{return!1}}function c(e){return e?s(e)?window.location.origin:e:null}function l(){return c(a())||(typeof window<`u`?window.location.origin:`http://127.0.0.1:8000`)}function u(){return o()}function d(){return(void 0)?.trim()||n().workingDir?.trim()||t}function f(e){return`${d().replace(/\/+$/,``)}/${e.replace(/-/g,``)}`}function p(){let e=u();return e?{"X-Session-API-Key":e}:{}}function m(){return!0}exports.DEFAULT_WORKING_DIR=t,exports.buildConversationWorkingDir=f,exports.getAgentServerBaseUrl=l,exports.getAgentServerHeaders=p,exports.getAgentServerSessionApiKey=u,exports.getAgentServerWorkingDir=d,exports.shouldLoadPublicSkills=m;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);var e=`openhands-agent-server-config`,t=`workspace/project`;function n(){if(typeof window>`u`)return{};try{let t=window.localStorage.getItem(e);return t?JSON.parse(t)??{}:{}}catch{return{}}}function r(t){if(typeof window>`u`)return;let n=Object.fromEntries(Object.entries(t).flatMap(([e,t])=>{if(typeof t!=`string`)return[];let n=t.trim();return n?[[e,n]]:[]}));if(Object.keys(n).length===0){window.localStorage.removeItem(e);return}window.localStorage.setItem(e,JSON.stringify(n))}function i(e){return e?.trim()||null}function a(e){if(!e)return null;let t=e.trim().replace(/\/$/,``);return t?/^https?:\/\//i.test(t)?t:typeof window<`u`?`${window.location.protocol}//${t}`:`http://${t}`:null}function o(){return a(n().baseUrl)||a(void 0)}function s(){return i(void 0)}function c(){return i(n().sessionApiKey)||s()}function l(){let e=s();if(!e)return;let t=n(),a=i(t.sessionApiKey);a&&a!==e&&r({...t,sessionApiKey:e})}function u(e){if(typeof window>`u`)return!1;try{let t=new URL(e),n=new Set([`127.0.0.1`,`localhost`,`0.0.0.0`]),r=window.location.hostname;return n.has(t.hostname)&&(!n.has(r)||t.hostname!==r)}catch{return!1}}function d(e){return e?u(e)?window.location.origin:e:null}function f(){return d(o())||(typeof window<`u`?window.location.origin:`http://127.0.0.1:8000`)}function p(){return c()}function m(){return(void 0)?.trim()||n().workingDir?.trim()||t}function h(e){return`${m().replace(/\/+$/,``)}/${e.replace(/-/g,``)}`}function g(){let e=p();return e?{"X-Session-API-Key":e}:{}}function _(){return!0}function v(){return typeof window<`u`&&window.__AGENT_CANVAS_AUTH_REQUIRED__===!0}exports.DEFAULT_WORKING_DIR=t,exports.buildConversationWorkingDir=h,exports.getAgentServerBaseUrl=f,exports.getAgentServerHeaders=g,exports.getAgentServerSessionApiKey=p,exports.getAgentServerWorkingDir=m,exports.isAuthRequired=v,exports.shouldLoadPublicSkills=_,exports.syncBakedSessionApiKey=l;
2
2
  //# sourceMappingURL=agent-server-config.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-server-config.cjs","names":[],"sources":["../../src/api/agent-server-config.ts"],"sourcesContent":["export const AGENT_SERVER_CONFIG_STORAGE_KEY = \"openhands-agent-server-config\";\nexport const DEFAULT_WORKING_DIR = \"workspace/project\";\n\ninterface StoredAgentServerConfig {\n baseUrl?: string | null;\n sessionApiKey?: string | null;\n workingDir?: string | null;\n}\n\nexport interface AgentServerFormDefaults {\n baseUrl: string;\n sessionApiKey: string;\n}\n\nfunction readStoredConfig(): StoredAgentServerConfig {\n if (typeof window === \"undefined\") return {};\n\n try {\n const raw = window.localStorage.getItem(AGENT_SERVER_CONFIG_STORAGE_KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw) as StoredAgentServerConfig;\n return parsed ?? {};\n } catch {\n return {};\n }\n}\n\nfunction writeStoredConfig(config: StoredAgentServerConfig): void {\n if (typeof window === \"undefined\") return;\n\n const nextConfig = Object.fromEntries(\n Object.entries(config).flatMap(([key, value]) => {\n if (typeof value !== \"string\") return [];\n\n const trimmed = value.trim();\n if (!trimmed) return [];\n\n return [[key, trimmed]];\n }),\n ) as StoredAgentServerConfig;\n\n if (Object.keys(nextConfig).length === 0) {\n window.localStorage.removeItem(AGENT_SERVER_CONFIG_STORAGE_KEY);\n return;\n }\n\n window.localStorage.setItem(\n AGENT_SERVER_CONFIG_STORAGE_KEY,\n JSON.stringify(nextConfig),\n );\n}\n\nfunction trimToNull(value?: string | null): string | null {\n return value?.trim() || null;\n}\n\nfunction normalizeBaseUrl(value?: string | null): string | null {\n if (!value) return null;\n\n const trimmed = value.trim().replace(/\\/$/, \"\");\n if (!trimmed) return null;\n\n if (/^https?:\\/\\//i.test(trimmed)) {\n return trimmed;\n }\n\n if (typeof window !== \"undefined\") {\n return `${window.location.protocol}//${trimmed}`;\n }\n\n return `http://${trimmed}`;\n}\n\nfunction getConfiguredBaseUrl(): string | null {\n const storedUrl = normalizeBaseUrl(readStoredConfig().baseUrl);\n if (storedUrl) return storedUrl;\n\n return normalizeBaseUrl(import.meta.env.VITE_BACKEND_BASE_URL);\n}\n\nfunction getConfiguredSessionApiKey(): string | null {\n const storedKey = trimToNull(readStoredConfig().sessionApiKey);\n if (storedKey) return storedKey;\n\n return trimToNull(import.meta.env.VITE_SESSION_API_KEY);\n}\n\nfunction shouldUseProxyOrigin(baseUrl: string): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n try {\n const configuredUrl = new URL(baseUrl);\n const localHosts = new Set([\"127.0.0.1\", \"localhost\", \"0.0.0.0\"]);\n const browserHostname = window.location.hostname;\n\n return (\n localHosts.has(configuredUrl.hostname) && !localHosts.has(browserHostname)\n );\n } catch {\n return false;\n }\n}\n\nfunction resolveAgentServerBaseUrl(baseUrl: string | null): string | null {\n if (!baseUrl) {\n return null;\n }\n\n if (shouldUseProxyOrigin(baseUrl)) {\n return window.location.origin;\n }\n\n return baseUrl;\n}\n\nexport function getAgentServerFormDefaults(): AgentServerFormDefaults {\n return {\n baseUrl: getConfiguredBaseUrl() ?? \"\",\n sessionApiKey: getConfiguredSessionApiKey() ?? \"\",\n };\n}\n\nexport function saveAgentServerConfig(config: AgentServerFormDefaults): void {\n const currentConfig = readStoredConfig();\n\n writeStoredConfig({\n ...currentConfig,\n baseUrl: normalizeBaseUrl(config.baseUrl),\n sessionApiKey: trimToNull(config.sessionApiKey),\n });\n}\n\nexport function getAgentServerBaseUrl(): string {\n const configuredUrl = resolveAgentServerBaseUrl(getConfiguredBaseUrl());\n if (configuredUrl) return configuredUrl;\n\n if (typeof window !== \"undefined\") {\n return window.location.origin;\n }\n\n return \"http://127.0.0.1:8000\";\n}\n\nexport function getAgentServerSessionApiKey(): string | null {\n return getConfiguredSessionApiKey();\n}\n\nexport function getAgentServerWorkingDir(): string {\n const envDir = import.meta.env.VITE_WORKING_DIR?.trim();\n if (envDir) return envDir;\n\n const storedDir = readStoredConfig().workingDir?.trim();\n if (storedDir) return storedDir;\n\n return DEFAULT_WORKING_DIR;\n}\n\nexport function buildConversationWorkingDir(conversationId: string): string {\n const base = getAgentServerWorkingDir().replace(/\\/+$/, \"\");\n const hex = conversationId.replace(/-/g, \"\");\n return `${base}/${hex}`;\n}\n\nexport function getConfiguredWorkerUrls(): string[] {\n const raw = import.meta.env.VITE_WORKER_URLS?.trim();\n if (!raw) return [];\n\n return raw\n .split(\",\")\n .map((url: string) => normalizeBaseUrl(url))\n .filter((url: string | null): url is string => Boolean(url));\n}\n\nexport function getAgentServerHeaders(): Record<string, string> {\n const sessionApiKey = getAgentServerSessionApiKey();\n return sessionApiKey ? { \"X-Session-API-Key\": sessionApiKey } : {};\n}\n\n/**\n * Returns whether public skills from the OpenHands extensions marketplace\n * (https://github.com/OpenHands/extensions) should be loaded.\n *\n * Defaults to true. Set VITE_LOAD_PUBLIC_SKILLS=false to disable.\n */\nexport function shouldLoadPublicSkills(): boolean {\n return import.meta.env.VITE_LOAD_PUBLIC_SKILLS !== \"false\";\n}\n"],"mappings":"6CAAA,IAAa,EAAkC,gCAClC,EAAsB,oBAanC,SAAS,GAA4C,CACnD,GAAI,OAAO,OAAW,IAAa,MAAO,EAAE,CAE5C,GAAI,CACF,IAAM,EAAM,OAAO,aAAa,QAAQ,EAAgC,CAGxE,OAFK,EACU,KAAK,MAAM,EACnB,EAAU,EAAE,CAFF,EAAE,MAGb,CACN,MAAO,EAAE,EA6Bb,SAAS,EAAW,EAAsC,CACxD,OAAO,GAAO,MAAM,EAAI,KAG1B,SAAS,EAAiB,EAAsC,CAC9D,GAAI,CAAC,EAAO,OAAO,KAEnB,IAAM,EAAU,EAAM,MAAM,CAAC,QAAQ,MAAO,GAAG,CAW/C,OAVK,EAED,gBAAgB,KAAK,EAAQ,CACxB,EAGL,OAAO,OAAW,IACb,GAAG,OAAO,SAAS,SAAS,IAAI,IAGlC,UAAU,IAVI,KAavB,SAAS,GAAsC,CAI7C,OAHkB,EAAiB,GAAkB,CAAC,QAClD,EAEG,EAAA,IAAA,GAAuD,CAGhE,SAAS,GAA4C,CAInD,OAHkB,EAAW,GAAkB,CAAC,cAC5C,EAEG,EAAA,IAAA,GAAgD,CAGzD,SAAS,EAAqB,EAA0B,CACtD,GAAI,OAAO,OAAW,IACpB,MAAO,GAGT,GAAI,CACF,IAAM,EAAgB,IAAI,IAAI,EAAQ,CAChC,EAAa,IAAI,IAAI,CAAC,YAAa,YAAa,UAAU,CAAC,CAC3D,EAAkB,OAAO,SAAS,SAExC,OACE,EAAW,IAAI,EAAc,SAAS,EAAI,CAAC,EAAW,IAAI,EAAgB,MAEtE,CACN,MAAO,IAIX,SAAS,EAA0B,EAAuC,CASxE,OARK,EAID,EAAqB,EAAQ,CACxB,OAAO,SAAS,OAGlB,EAPE,KA2BX,SAAgB,GAAgC,CAQ9C,OAPsB,EAA0B,GAAsB,CAClE,GAEA,OAAO,OAAW,IACb,OAAO,SAAS,OAGlB,yBAGT,SAAgB,GAA6C,CAC3D,OAAO,GAA4B,CAGrC,SAAgB,GAAmC,CAOjD,OANM,IAAA,KAA2C,MAAM,EAGrC,GAAkB,CAAC,YAAY,MAAM,EAGhD,EAGT,SAAgB,EAA4B,EAAgC,CAG1E,MAAO,GAFM,GAA0B,CAAC,QAAQ,OAAQ,GAE9C,CAAK,GADH,EAAe,QAAQ,KAAM,GACvB,GAapB,SAAgB,GAAgD,CAC9D,IAAM,EAAgB,GAA6B,CACnD,OAAO,EAAgB,CAAE,oBAAqB,EAAe,CAAG,EAAE,CASpE,SAAgB,GAAkC,CAChD,MAAO"}
1
+ {"version":3,"file":"agent-server-config.cjs","names":[],"sources":["../../src/api/agent-server-config.ts"],"sourcesContent":["export const AGENT_SERVER_CONFIG_STORAGE_KEY = \"openhands-agent-server-config\";\nexport const DEFAULT_WORKING_DIR = \"workspace/project\";\n\ninterface StoredAgentServerConfig {\n baseUrl?: string | null;\n sessionApiKey?: string | null;\n workingDir?: string | null;\n}\n\nexport interface AgentServerFormDefaults {\n baseUrl: string;\n sessionApiKey: string;\n}\n\nfunction readStoredConfig(): StoredAgentServerConfig {\n if (typeof window === \"undefined\") return {};\n\n try {\n const raw = window.localStorage.getItem(AGENT_SERVER_CONFIG_STORAGE_KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw) as StoredAgentServerConfig;\n return parsed ?? {};\n } catch {\n return {};\n }\n}\n\nfunction writeStoredConfig(config: StoredAgentServerConfig): void {\n if (typeof window === \"undefined\") return;\n\n const nextConfig = Object.fromEntries(\n Object.entries(config).flatMap(([key, value]) => {\n if (typeof value !== \"string\") return [];\n\n const trimmed = value.trim();\n if (!trimmed) return [];\n\n return [[key, trimmed]];\n }),\n ) as StoredAgentServerConfig;\n\n if (Object.keys(nextConfig).length === 0) {\n window.localStorage.removeItem(AGENT_SERVER_CONFIG_STORAGE_KEY);\n return;\n }\n\n window.localStorage.setItem(\n AGENT_SERVER_CONFIG_STORAGE_KEY,\n JSON.stringify(nextConfig),\n );\n}\n\nfunction trimToNull(value?: string | null): string | null {\n return value?.trim() || null;\n}\n\nfunction normalizeBaseUrl(value?: string | null): string | null {\n if (!value) return null;\n\n const trimmed = value.trim().replace(/\\/$/, \"\");\n if (!trimmed) return null;\n\n if (/^https?:\\/\\//i.test(trimmed)) {\n return trimmed;\n }\n\n if (typeof window !== \"undefined\") {\n return `${window.location.protocol}//${trimmed}`;\n }\n\n return `http://${trimmed}`;\n}\n\nfunction getConfiguredBaseUrl(): string | null {\n const storedUrl = normalizeBaseUrl(readStoredConfig().baseUrl);\n if (storedUrl) return storedUrl;\n\n return normalizeBaseUrl(import.meta.env.VITE_BACKEND_BASE_URL);\n}\n\n/**\n * Return the baked-in session API key from the Vite env or the runtime\n * injection by static-server.mjs. This represents the *server's* truth\n * and is only set in non-public (local) mode.\n */\nexport function getBakedSessionApiKey(): string | null {\n return trimToNull(import.meta.env.VITE_SESSION_API_KEY);\n}\n\nfunction getConfiguredSessionApiKey(): string | null {\n const storedKey = trimToNull(readStoredConfig().sessionApiKey);\n if (storedKey) return storedKey;\n\n return getBakedSessionApiKey();\n}\n\n/**\n * Sync the baked-in session API key into `openhands-agent-server-config`\n * localStorage when the stored value has drifted.\n *\n * In non-public (local) mode the dev scripts bake the session key into\n * `VITE_SESSION_API_KEY` (Vite dev) or inject it via `static-server.mjs`\n * (`--session-api-key`). That key represents the *server's* truth — the\n * agent-server was started with the same value as `OH_SESSION_API_KEYS_0`.\n *\n * If a user restarts the stack with a different `LOCAL_BACKEND_API_KEY`,\n * the baked-in key changes but the old value may still be persisted in\n * localStorage (written by the onboarding form, the Settings page, or a\n * previous key injection). Without this sync the stale stored key would\n * shadow the new baked key everywhere (`getConfiguredSessionApiKey()`\n * reads localStorage first), causing 401s.\n *\n * Must run **before** any call to `getConfiguredSessionApiKey()` or\n * `makeDefaultLocalBackend()` — called from `readStoredBackends()` in\n * `storage.ts` which is evaluated at module init time.\n */\nexport function syncBakedSessionApiKey(): void {\n const bakedKey = getBakedSessionApiKey();\n if (!bakedKey) return; // public mode or no key baked in\n\n const storedConfig = readStoredConfig();\n const storedKey = trimToNull(storedConfig.sessionApiKey);\n if (storedKey && storedKey !== bakedKey) {\n writeStoredConfig({ ...storedConfig, sessionApiKey: bakedKey });\n }\n}\n\nfunction shouldUseProxyOrigin(baseUrl: string): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n try {\n const configuredUrl = new URL(baseUrl);\n const localHosts = new Set([\"127.0.0.1\", \"localhost\", \"0.0.0.0\"]);\n const browserHostname = window.location.hostname;\n\n return (\n localHosts.has(configuredUrl.hostname) &&\n (!localHosts.has(browserHostname) ||\n configuredUrl.hostname !== browserHostname)\n );\n } catch {\n return false;\n }\n}\n\nfunction resolveAgentServerBaseUrl(baseUrl: string | null): string | null {\n if (!baseUrl) {\n return null;\n }\n\n if (shouldUseProxyOrigin(baseUrl)) {\n return window.location.origin;\n }\n\n return baseUrl;\n}\n\nexport function getAgentServerFormDefaults(): AgentServerFormDefaults {\n return {\n baseUrl: getConfiguredBaseUrl() ?? \"\",\n sessionApiKey: getConfiguredSessionApiKey() ?? \"\",\n };\n}\n\nexport function saveAgentServerConfig(config: AgentServerFormDefaults): void {\n const currentConfig = readStoredConfig();\n\n writeStoredConfig({\n ...currentConfig,\n baseUrl: normalizeBaseUrl(config.baseUrl),\n sessionApiKey: trimToNull(config.sessionApiKey),\n });\n}\n\nexport function getAgentServerBaseUrl(): string {\n const configuredUrl = resolveAgentServerBaseUrl(getConfiguredBaseUrl());\n if (configuredUrl) return configuredUrl;\n\n if (typeof window !== \"undefined\") {\n return window.location.origin;\n }\n\n return \"http://127.0.0.1:8000\";\n}\n\nexport function getAgentServerSessionApiKey(): string | null {\n return getConfiguredSessionApiKey();\n}\n\nexport function getAgentServerWorkingDir(): string {\n const envDir = import.meta.env.VITE_WORKING_DIR?.trim();\n if (envDir) return envDir;\n\n const storedDir = readStoredConfig().workingDir?.trim();\n if (storedDir) return storedDir;\n\n return DEFAULT_WORKING_DIR;\n}\n\nexport function buildConversationWorkingDir(conversationId: string): string {\n const base = getAgentServerWorkingDir().replace(/\\/+$/, \"\");\n const hex = conversationId.replace(/-/g, \"\");\n return `${base}/${hex}`;\n}\n\nexport function getConfiguredWorkerUrls(): string[] {\n const raw = import.meta.env.VITE_WORKER_URLS?.trim();\n if (!raw) return [];\n\n return raw\n .split(\",\")\n .map((url: string) => normalizeBaseUrl(url))\n .filter((url: string | null): url is string => Boolean(url));\n}\n\nexport function getAgentServerHeaders(): Record<string, string> {\n const sessionApiKey = getAgentServerSessionApiKey();\n return sessionApiKey ? { \"X-Session-API-Key\": sessionApiKey } : {};\n}\n\n/**\n * Returns whether public skills from the OpenHands extensions marketplace\n * (https://github.com/OpenHands/extensions) should be loaded.\n *\n * Defaults to true. Set VITE_LOAD_PUBLIC_SKILLS=false to disable.\n */\nexport function shouldLoadPublicSkills(): boolean {\n return import.meta.env.VITE_LOAD_PUBLIC_SKILLS !== \"false\";\n}\n\n/**\n * Whether the deployment requires an API key from the user (public mode).\n *\n * Checks both the Vite build-time env var (`VITE_AUTH_REQUIRED`) and the\n * runtime flag injected by static-server.mjs (`window.__AGENT_CANVAS_AUTH_REQUIRED__`).\n * The runtime flag is needed for pre-built static binaries where\n * `VITE_AUTH_REQUIRED` was not set at build time.\n */\nexport function isAuthRequired(): boolean {\n return (\n import.meta.env.VITE_AUTH_REQUIRED === \"true\" ||\n (typeof window !== \"undefined\" &&\n (window as unknown as Record<string, unknown>)\n .__AGENT_CANVAS_AUTH_REQUIRED__ === true)\n );\n}\n\n/**\n * Returns true when the server was started in public mode and the user\n * has not yet pasted an API key (nothing in localStorage, nothing baked\n * in via `VITE_SESSION_API_KEY`).\n *\n * Used by `root.tsx` to gate the app behind {@link ApiKeyEntryScreen}\n * before any network request is attempted.\n */\nexport function isAuthRequiredAndMissing(): boolean {\n if (!isAuthRequired()) return false;\n return !getConfiguredSessionApiKey();\n}\n"],"mappings":"6CAAA,IAAa,EAAkC,gCAClC,EAAsB,oBAanC,SAAS,GAA4C,CACnD,GAAI,OAAO,OAAW,IAAa,MAAO,EAAE,CAE5C,GAAI,CACF,IAAM,EAAM,OAAO,aAAa,QAAQ,EAAgC,CAGxE,OAFK,EACU,KAAK,MAAM,EACnB,EAAU,EAAE,CAFF,EAAE,MAGb,CACN,MAAO,EAAE,EAIb,SAAS,EAAkB,EAAuC,CAChE,GAAI,OAAO,OAAW,IAAa,OAEnC,IAAM,EAAa,OAAO,YACxB,OAAO,QAAQ,EAAO,CAAC,SAAS,CAAC,EAAK,KAAW,CAC/C,GAAI,OAAO,GAAU,SAAU,MAAO,EAAE,CAExC,IAAM,EAAU,EAAM,MAAM,CAG5B,OAFK,EAEE,CAAC,CAAC,EAAK,EAAQ,CAAC,CAFF,EAAE,EAGvB,CACH,CAED,GAAI,OAAO,KAAK,EAAW,CAAC,SAAW,EAAG,CACxC,OAAO,aAAa,WAAW,EAAgC,CAC/D,OAGF,OAAO,aAAa,QAClB,EACA,KAAK,UAAU,EAAW,CAC3B,CAGH,SAAS,EAAW,EAAsC,CACxD,OAAO,GAAO,MAAM,EAAI,KAG1B,SAAS,EAAiB,EAAsC,CAC9D,GAAI,CAAC,EAAO,OAAO,KAEnB,IAAM,EAAU,EAAM,MAAM,CAAC,QAAQ,MAAO,GAAG,CAW/C,OAVK,EAED,gBAAgB,KAAK,EAAQ,CACxB,EAGL,OAAO,OAAW,IACb,GAAG,OAAO,SAAS,SAAS,IAAI,IAGlC,UAAU,IAVI,KAavB,SAAS,GAAsC,CAI7C,OAHkB,EAAiB,GAAkB,CAAC,QAClD,EAEG,EAAA,IAAA,GAAuD,CAQhE,SAAgB,GAAuC,CACrD,OAAO,EAAA,IAAA,GAAgD,CAGzD,SAAS,GAA4C,CAInD,OAHkB,EAAW,GAAkB,CAAC,cAC5C,EAEG,GAAuB,CAuBhC,SAAgB,GAA+B,CAC7C,IAAM,EAAW,GAAuB,CACxC,GAAI,CAAC,EAAU,OAEf,IAAM,EAAe,GAAkB,CACjC,EAAY,EAAW,EAAa,cAAc,CACpD,GAAa,IAAc,GAC7B,EAAkB,CAAE,GAAG,EAAc,cAAe,EAAU,CAAC,CAInE,SAAS,EAAqB,EAA0B,CACtD,GAAI,OAAO,OAAW,IACpB,MAAO,GAGT,GAAI,CACF,IAAM,EAAgB,IAAI,IAAI,EAAQ,CAChC,EAAa,IAAI,IAAI,CAAC,YAAa,YAAa,UAAU,CAAC,CAC3D,EAAkB,OAAO,SAAS,SAExC,OACE,EAAW,IAAI,EAAc,SAAS,GACrC,CAAC,EAAW,IAAI,EAAgB,EAC/B,EAAc,WAAa,QAEzB,CACN,MAAO,IAIX,SAAS,EAA0B,EAAuC,CASxE,OARK,EAID,EAAqB,EAAQ,CACxB,OAAO,SAAS,OAGlB,EAPE,KA2BX,SAAgB,GAAgC,CAQ9C,OAPsB,EAA0B,GAAsB,CAClE,GAEA,OAAO,OAAW,IACb,OAAO,SAAS,OAGlB,yBAGT,SAAgB,GAA6C,CAC3D,OAAO,GAA4B,CAGrC,SAAgB,GAAmC,CAOjD,OANM,IAAA,KAA2C,MAAM,EAGrC,GAAkB,CAAC,YAAY,MAAM,EAGhD,EAGT,SAAgB,EAA4B,EAAgC,CAG1E,MAAO,GAFM,GAA0B,CAAC,QAAQ,OAAQ,GAE9C,CAAK,GADH,EAAe,QAAQ,KAAM,GACvB,GAapB,SAAgB,GAAgD,CAC9D,IAAM,EAAgB,GAA6B,CACnD,OAAO,EAAgB,CAAE,oBAAqB,EAAe,CAAG,EAAE,CASpE,SAAgB,GAAkC,CAChD,MAAO,GAWT,SAAgB,GAA0B,CACxC,OAEG,OAAO,OAAW,KAChB,OACE,iCAAmC"}
@@ -4,6 +4,33 @@ export interface AgentServerFormDefaults {
4
4
  baseUrl: string;
5
5
  sessionApiKey: string;
6
6
  }
7
+ /**
8
+ * Return the baked-in session API key from the Vite env or the runtime
9
+ * injection by static-server.mjs. This represents the *server's* truth
10
+ * and is only set in non-public (local) mode.
11
+ */
12
+ export declare function getBakedSessionApiKey(): string | null;
13
+ /**
14
+ * Sync the baked-in session API key into `openhands-agent-server-config`
15
+ * localStorage when the stored value has drifted.
16
+ *
17
+ * In non-public (local) mode the dev scripts bake the session key into
18
+ * `VITE_SESSION_API_KEY` (Vite dev) or inject it via `static-server.mjs`
19
+ * (`--session-api-key`). That key represents the *server's* truth — the
20
+ * agent-server was started with the same value as `OH_SESSION_API_KEYS_0`.
21
+ *
22
+ * If a user restarts the stack with a different `LOCAL_BACKEND_API_KEY`,
23
+ * the baked-in key changes but the old value may still be persisted in
24
+ * localStorage (written by the onboarding form, the Settings page, or a
25
+ * previous key injection). Without this sync the stale stored key would
26
+ * shadow the new baked key everywhere (`getConfiguredSessionApiKey()`
27
+ * reads localStorage first), causing 401s.
28
+ *
29
+ * Must run **before** any call to `getConfiguredSessionApiKey()` or
30
+ * `makeDefaultLocalBackend()` — called from `readStoredBackends()` in
31
+ * `storage.ts` which is evaluated at module init time.
32
+ */
33
+ export declare function syncBakedSessionApiKey(): void;
7
34
  export declare function getAgentServerFormDefaults(): AgentServerFormDefaults;
8
35
  export declare function saveAgentServerConfig(config: AgentServerFormDefaults): void;
9
36
  export declare function getAgentServerBaseUrl(): string;
@@ -19,3 +46,21 @@ export declare function getAgentServerHeaders(): Record<string, string>;
19
46
  * Defaults to true. Set VITE_LOAD_PUBLIC_SKILLS=false to disable.
20
47
  */
21
48
  export declare function shouldLoadPublicSkills(): boolean;
49
+ /**
50
+ * Whether the deployment requires an API key from the user (public mode).
51
+ *
52
+ * Checks both the Vite build-time env var (`VITE_AUTH_REQUIRED`) and the
53
+ * runtime flag injected by static-server.mjs (`window.__AGENT_CANVAS_AUTH_REQUIRED__`).
54
+ * The runtime flag is needed for pre-built static binaries where
55
+ * `VITE_AUTH_REQUIRED` was not set at build time.
56
+ */
57
+ export declare function isAuthRequired(): boolean;
58
+ /**
59
+ * Returns true when the server was started in public mode and the user
60
+ * has not yet pasted an API key (nothing in localStorage, nothing baked
61
+ * in via `VITE_SESSION_API_KEY`).
62
+ *
63
+ * Used by `root.tsx` to gate the app behind {@link ApiKeyEntryScreen}
64
+ * before any network request is attempted.
65
+ */
66
+ export declare function isAuthRequiredAndMissing(): boolean;
@@ -9,21 +9,46 @@ function n() {
9
9
  return {};
10
10
  }
11
11
  }
12
- function r(e) {
13
- return e?.trim() || null;
12
+ function r(t) {
13
+ if (typeof window > "u") return;
14
+ let n = Object.fromEntries(Object.entries(t).flatMap(([e, t]) => {
15
+ if (typeof t != "string") return [];
16
+ let n = t.trim();
17
+ return n ? [[e, n]] : [];
18
+ }));
19
+ if (Object.keys(n).length === 0) {
20
+ window.localStorage.removeItem(e);
21
+ return;
22
+ }
23
+ window.localStorage.setItem(e, JSON.stringify(n));
14
24
  }
15
25
  function i(e) {
26
+ return e?.trim() || null;
27
+ }
28
+ function a(e) {
16
29
  if (!e) return null;
17
30
  let t = e.trim().replace(/\/$/, "");
18
31
  return t ? /^https?:\/\//i.test(t) ? t : typeof window < "u" ? `${window.location.protocol}//${t}` : `http://${t}` : null;
19
32
  }
20
- function a() {
21
- return i(n().baseUrl) || i(void 0);
22
- }
23
33
  function o() {
24
- return r(n().sessionApiKey) || r(void 0);
34
+ return a(n().baseUrl) || a(void 0);
35
+ }
36
+ function s() {
37
+ return i(void 0);
25
38
  }
26
- function s(e) {
39
+ function c() {
40
+ return i(n().sessionApiKey) || s();
41
+ }
42
+ function l() {
43
+ let e = s();
44
+ if (!e) return;
45
+ let t = n(), a = i(t.sessionApiKey);
46
+ a && a !== e && r({
47
+ ...t,
48
+ sessionApiKey: e
49
+ });
50
+ }
51
+ function u(e) {
27
52
  if (typeof window > "u") return !1;
28
53
  try {
29
54
  let t = new URL(e), n = new Set([
@@ -31,34 +56,37 @@ function s(e) {
31
56
  "localhost",
32
57
  "0.0.0.0"
33
58
  ]), r = window.location.hostname;
34
- return n.has(t.hostname) && !n.has(r);
59
+ return n.has(t.hostname) && (!n.has(r) || t.hostname !== r);
35
60
  } catch {
36
61
  return !1;
37
62
  }
38
63
  }
39
- function c(e) {
40
- return e ? s(e) ? window.location.origin : e : null;
64
+ function d(e) {
65
+ return e ? u(e) ? window.location.origin : e : null;
41
66
  }
42
- function l() {
43
- return c(a()) || (typeof window < "u" ? window.location.origin : "http://127.0.0.1:8000");
67
+ function f() {
68
+ return d(o()) || (typeof window < "u" ? window.location.origin : "http://127.0.0.1:8000");
44
69
  }
45
- function u() {
46
- return o();
70
+ function p() {
71
+ return c();
47
72
  }
48
- function d() {
73
+ function m() {
49
74
  return (void 0)?.trim() || n().workingDir?.trim() || t;
50
75
  }
51
- function f(e) {
52
- return `${d().replace(/\/+$/, "")}/${e.replace(/-/g, "")}`;
76
+ function h(e) {
77
+ return `${m().replace(/\/+$/, "")}/${e.replace(/-/g, "")}`;
53
78
  }
54
- function p() {
55
- let e = u();
79
+ function g() {
80
+ let e = p();
56
81
  return e ? { "X-Session-API-Key": e } : {};
57
82
  }
58
- function m() {
83
+ function _() {
59
84
  return !0;
60
85
  }
86
+ function v() {
87
+ return typeof window < "u" && window.__AGENT_CANVAS_AUTH_REQUIRED__ === !0;
88
+ }
61
89
  //#endregion
62
- export { t as DEFAULT_WORKING_DIR, f as buildConversationWorkingDir, l as getAgentServerBaseUrl, p as getAgentServerHeaders, u as getAgentServerSessionApiKey, d as getAgentServerWorkingDir, m as shouldLoadPublicSkills };
90
+ export { t as DEFAULT_WORKING_DIR, h as buildConversationWorkingDir, f as getAgentServerBaseUrl, g as getAgentServerHeaders, p as getAgentServerSessionApiKey, m as getAgentServerWorkingDir, v as isAuthRequired, _ as shouldLoadPublicSkills, l as syncBakedSessionApiKey };
63
91
 
64
92
  //# sourceMappingURL=agent-server-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-server-config.js","names":[],"sources":["../../src/api/agent-server-config.ts"],"sourcesContent":["export const AGENT_SERVER_CONFIG_STORAGE_KEY = \"openhands-agent-server-config\";\nexport const DEFAULT_WORKING_DIR = \"workspace/project\";\n\ninterface StoredAgentServerConfig {\n baseUrl?: string | null;\n sessionApiKey?: string | null;\n workingDir?: string | null;\n}\n\nexport interface AgentServerFormDefaults {\n baseUrl: string;\n sessionApiKey: string;\n}\n\nfunction readStoredConfig(): StoredAgentServerConfig {\n if (typeof window === \"undefined\") return {};\n\n try {\n const raw = window.localStorage.getItem(AGENT_SERVER_CONFIG_STORAGE_KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw) as StoredAgentServerConfig;\n return parsed ?? {};\n } catch {\n return {};\n }\n}\n\nfunction writeStoredConfig(config: StoredAgentServerConfig): void {\n if (typeof window === \"undefined\") return;\n\n const nextConfig = Object.fromEntries(\n Object.entries(config).flatMap(([key, value]) => {\n if (typeof value !== \"string\") return [];\n\n const trimmed = value.trim();\n if (!trimmed) return [];\n\n return [[key, trimmed]];\n }),\n ) as StoredAgentServerConfig;\n\n if (Object.keys(nextConfig).length === 0) {\n window.localStorage.removeItem(AGENT_SERVER_CONFIG_STORAGE_KEY);\n return;\n }\n\n window.localStorage.setItem(\n AGENT_SERVER_CONFIG_STORAGE_KEY,\n JSON.stringify(nextConfig),\n );\n}\n\nfunction trimToNull(value?: string | null): string | null {\n return value?.trim() || null;\n}\n\nfunction normalizeBaseUrl(value?: string | null): string | null {\n if (!value) return null;\n\n const trimmed = value.trim().replace(/\\/$/, \"\");\n if (!trimmed) return null;\n\n if (/^https?:\\/\\//i.test(trimmed)) {\n return trimmed;\n }\n\n if (typeof window !== \"undefined\") {\n return `${window.location.protocol}//${trimmed}`;\n }\n\n return `http://${trimmed}`;\n}\n\nfunction getConfiguredBaseUrl(): string | null {\n const storedUrl = normalizeBaseUrl(readStoredConfig().baseUrl);\n if (storedUrl) return storedUrl;\n\n return normalizeBaseUrl(import.meta.env.VITE_BACKEND_BASE_URL);\n}\n\nfunction getConfiguredSessionApiKey(): string | null {\n const storedKey = trimToNull(readStoredConfig().sessionApiKey);\n if (storedKey) return storedKey;\n\n return trimToNull(import.meta.env.VITE_SESSION_API_KEY);\n}\n\nfunction shouldUseProxyOrigin(baseUrl: string): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n try {\n const configuredUrl = new URL(baseUrl);\n const localHosts = new Set([\"127.0.0.1\", \"localhost\", \"0.0.0.0\"]);\n const browserHostname = window.location.hostname;\n\n return (\n localHosts.has(configuredUrl.hostname) && !localHosts.has(browserHostname)\n );\n } catch {\n return false;\n }\n}\n\nfunction resolveAgentServerBaseUrl(baseUrl: string | null): string | null {\n if (!baseUrl) {\n return null;\n }\n\n if (shouldUseProxyOrigin(baseUrl)) {\n return window.location.origin;\n }\n\n return baseUrl;\n}\n\nexport function getAgentServerFormDefaults(): AgentServerFormDefaults {\n return {\n baseUrl: getConfiguredBaseUrl() ?? \"\",\n sessionApiKey: getConfiguredSessionApiKey() ?? \"\",\n };\n}\n\nexport function saveAgentServerConfig(config: AgentServerFormDefaults): void {\n const currentConfig = readStoredConfig();\n\n writeStoredConfig({\n ...currentConfig,\n baseUrl: normalizeBaseUrl(config.baseUrl),\n sessionApiKey: trimToNull(config.sessionApiKey),\n });\n}\n\nexport function getAgentServerBaseUrl(): string {\n const configuredUrl = resolveAgentServerBaseUrl(getConfiguredBaseUrl());\n if (configuredUrl) return configuredUrl;\n\n if (typeof window !== \"undefined\") {\n return window.location.origin;\n }\n\n return \"http://127.0.0.1:8000\";\n}\n\nexport function getAgentServerSessionApiKey(): string | null {\n return getConfiguredSessionApiKey();\n}\n\nexport function getAgentServerWorkingDir(): string {\n const envDir = import.meta.env.VITE_WORKING_DIR?.trim();\n if (envDir) return envDir;\n\n const storedDir = readStoredConfig().workingDir?.trim();\n if (storedDir) return storedDir;\n\n return DEFAULT_WORKING_DIR;\n}\n\nexport function buildConversationWorkingDir(conversationId: string): string {\n const base = getAgentServerWorkingDir().replace(/\\/+$/, \"\");\n const hex = conversationId.replace(/-/g, \"\");\n return `${base}/${hex}`;\n}\n\nexport function getConfiguredWorkerUrls(): string[] {\n const raw = import.meta.env.VITE_WORKER_URLS?.trim();\n if (!raw) return [];\n\n return raw\n .split(\",\")\n .map((url: string) => normalizeBaseUrl(url))\n .filter((url: string | null): url is string => Boolean(url));\n}\n\nexport function getAgentServerHeaders(): Record<string, string> {\n const sessionApiKey = getAgentServerSessionApiKey();\n return sessionApiKey ? { \"X-Session-API-Key\": sessionApiKey } : {};\n}\n\n/**\n * Returns whether public skills from the OpenHands extensions marketplace\n * (https://github.com/OpenHands/extensions) should be loaded.\n *\n * Defaults to true. Set VITE_LOAD_PUBLIC_SKILLS=false to disable.\n */\nexport function shouldLoadPublicSkills(): boolean {\n return import.meta.env.VITE_LOAD_PUBLIC_SKILLS !== \"false\";\n}\n"],"mappings":";AAAA,IAAa,IAAkC,iCAClC,IAAsB;AAanC,SAAS,IAA4C;AACnD,KAAI,OAAO,SAAW,IAAa,QAAO,EAAE;AAE5C,KAAI;EACF,IAAM,IAAM,OAAO,aAAa,QAAQ,EAAgC;AAGxE,SAFK,IACU,KAAK,MAAM,EACnB,IAAU,EAAE,GAFF,EAAE;SAGb;AACN,SAAO,EAAE;;;AA6Bb,SAAS,EAAW,GAAsC;AACxD,QAAO,GAAO,MAAM,IAAI;;AAG1B,SAAS,EAAiB,GAAsC;AAC9D,KAAI,CAAC,EAAO,QAAO;CAEnB,IAAM,IAAU,EAAM,MAAM,CAAC,QAAQ,OAAO,GAAG;AAW/C,QAVK,IAED,gBAAgB,KAAK,EAAQ,GACxB,IAGL,OAAO,SAAW,MACb,GAAG,OAAO,SAAS,SAAS,IAAI,MAGlC,UAAU,MAVI;;AAavB,SAAS,IAAsC;AAI7C,QAHkB,EAAiB,GAAkB,CAAC,QAClD,IAEG,EAAA,KAAA,EAAuD;;AAGhE,SAAS,IAA4C;AAInD,QAHkB,EAAW,GAAkB,CAAC,cAC5C,IAEG,EAAA,KAAA,EAAgD;;AAGzD,SAAS,EAAqB,GAA0B;AACtD,KAAI,OAAO,SAAW,IACpB,QAAO;AAGT,KAAI;EACF,IAAM,IAAgB,IAAI,IAAI,EAAQ,EAChC,IAAa,IAAI,IAAI;GAAC;GAAa;GAAa;GAAU,CAAC,EAC3D,IAAkB,OAAO,SAAS;AAExC,SACE,EAAW,IAAI,EAAc,SAAS,IAAI,CAAC,EAAW,IAAI,EAAgB;SAEtE;AACN,SAAO;;;AAIX,SAAS,EAA0B,GAAuC;AASxE,QARK,IAID,EAAqB,EAAQ,GACxB,OAAO,SAAS,SAGlB,IAPE;;AA2BX,SAAgB,IAAgC;AAQ9C,QAPsB,EAA0B,GAAsB,CAClE,KAEA,OAAO,SAAW,MACb,OAAO,SAAS,SAGlB;;AAGT,SAAgB,IAA6C;AAC3D,QAAO,GAA4B;;AAGrC,SAAgB,IAAmC;AAOjD,SANM,KAAA,IAA2C,MAAM,IAGrC,GAAkB,CAAC,YAAY,MAAM,IAGhD;;AAGT,SAAgB,EAA4B,GAAgC;AAG1E,QAAO,GAFM,GAA0B,CAAC,QAAQ,QAAQ,GAE9C,CAAK,GADH,EAAe,QAAQ,MAAM,GACvB;;AAapB,SAAgB,IAAgD;CAC9D,IAAM,IAAgB,GAA6B;AACnD,QAAO,IAAgB,EAAE,qBAAqB,GAAe,GAAG,EAAE;;AASpE,SAAgB,IAAkC;AAChD,QAAO"}
1
+ {"version":3,"file":"agent-server-config.js","names":[],"sources":["../../src/api/agent-server-config.ts"],"sourcesContent":["export const AGENT_SERVER_CONFIG_STORAGE_KEY = \"openhands-agent-server-config\";\nexport const DEFAULT_WORKING_DIR = \"workspace/project\";\n\ninterface StoredAgentServerConfig {\n baseUrl?: string | null;\n sessionApiKey?: string | null;\n workingDir?: string | null;\n}\n\nexport interface AgentServerFormDefaults {\n baseUrl: string;\n sessionApiKey: string;\n}\n\nfunction readStoredConfig(): StoredAgentServerConfig {\n if (typeof window === \"undefined\") return {};\n\n try {\n const raw = window.localStorage.getItem(AGENT_SERVER_CONFIG_STORAGE_KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw) as StoredAgentServerConfig;\n return parsed ?? {};\n } catch {\n return {};\n }\n}\n\nfunction writeStoredConfig(config: StoredAgentServerConfig): void {\n if (typeof window === \"undefined\") return;\n\n const nextConfig = Object.fromEntries(\n Object.entries(config).flatMap(([key, value]) => {\n if (typeof value !== \"string\") return [];\n\n const trimmed = value.trim();\n if (!trimmed) return [];\n\n return [[key, trimmed]];\n }),\n ) as StoredAgentServerConfig;\n\n if (Object.keys(nextConfig).length === 0) {\n window.localStorage.removeItem(AGENT_SERVER_CONFIG_STORAGE_KEY);\n return;\n }\n\n window.localStorage.setItem(\n AGENT_SERVER_CONFIG_STORAGE_KEY,\n JSON.stringify(nextConfig),\n );\n}\n\nfunction trimToNull(value?: string | null): string | null {\n return value?.trim() || null;\n}\n\nfunction normalizeBaseUrl(value?: string | null): string | null {\n if (!value) return null;\n\n const trimmed = value.trim().replace(/\\/$/, \"\");\n if (!trimmed) return null;\n\n if (/^https?:\\/\\//i.test(trimmed)) {\n return trimmed;\n }\n\n if (typeof window !== \"undefined\") {\n return `${window.location.protocol}//${trimmed}`;\n }\n\n return `http://${trimmed}`;\n}\n\nfunction getConfiguredBaseUrl(): string | null {\n const storedUrl = normalizeBaseUrl(readStoredConfig().baseUrl);\n if (storedUrl) return storedUrl;\n\n return normalizeBaseUrl(import.meta.env.VITE_BACKEND_BASE_URL);\n}\n\n/**\n * Return the baked-in session API key from the Vite env or the runtime\n * injection by static-server.mjs. This represents the *server's* truth\n * and is only set in non-public (local) mode.\n */\nexport function getBakedSessionApiKey(): string | null {\n return trimToNull(import.meta.env.VITE_SESSION_API_KEY);\n}\n\nfunction getConfiguredSessionApiKey(): string | null {\n const storedKey = trimToNull(readStoredConfig().sessionApiKey);\n if (storedKey) return storedKey;\n\n return getBakedSessionApiKey();\n}\n\n/**\n * Sync the baked-in session API key into `openhands-agent-server-config`\n * localStorage when the stored value has drifted.\n *\n * In non-public (local) mode the dev scripts bake the session key into\n * `VITE_SESSION_API_KEY` (Vite dev) or inject it via `static-server.mjs`\n * (`--session-api-key`). That key represents the *server's* truth — the\n * agent-server was started with the same value as `OH_SESSION_API_KEYS_0`.\n *\n * If a user restarts the stack with a different `LOCAL_BACKEND_API_KEY`,\n * the baked-in key changes but the old value may still be persisted in\n * localStorage (written by the onboarding form, the Settings page, or a\n * previous key injection). Without this sync the stale stored key would\n * shadow the new baked key everywhere (`getConfiguredSessionApiKey()`\n * reads localStorage first), causing 401s.\n *\n * Must run **before** any call to `getConfiguredSessionApiKey()` or\n * `makeDefaultLocalBackend()` — called from `readStoredBackends()` in\n * `storage.ts` which is evaluated at module init time.\n */\nexport function syncBakedSessionApiKey(): void {\n const bakedKey = getBakedSessionApiKey();\n if (!bakedKey) return; // public mode or no key baked in\n\n const storedConfig = readStoredConfig();\n const storedKey = trimToNull(storedConfig.sessionApiKey);\n if (storedKey && storedKey !== bakedKey) {\n writeStoredConfig({ ...storedConfig, sessionApiKey: bakedKey });\n }\n}\n\nfunction shouldUseProxyOrigin(baseUrl: string): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n try {\n const configuredUrl = new URL(baseUrl);\n const localHosts = new Set([\"127.0.0.1\", \"localhost\", \"0.0.0.0\"]);\n const browserHostname = window.location.hostname;\n\n return (\n localHosts.has(configuredUrl.hostname) &&\n (!localHosts.has(browserHostname) ||\n configuredUrl.hostname !== browserHostname)\n );\n } catch {\n return false;\n }\n}\n\nfunction resolveAgentServerBaseUrl(baseUrl: string | null): string | null {\n if (!baseUrl) {\n return null;\n }\n\n if (shouldUseProxyOrigin(baseUrl)) {\n return window.location.origin;\n }\n\n return baseUrl;\n}\n\nexport function getAgentServerFormDefaults(): AgentServerFormDefaults {\n return {\n baseUrl: getConfiguredBaseUrl() ?? \"\",\n sessionApiKey: getConfiguredSessionApiKey() ?? \"\",\n };\n}\n\nexport function saveAgentServerConfig(config: AgentServerFormDefaults): void {\n const currentConfig = readStoredConfig();\n\n writeStoredConfig({\n ...currentConfig,\n baseUrl: normalizeBaseUrl(config.baseUrl),\n sessionApiKey: trimToNull(config.sessionApiKey),\n });\n}\n\nexport function getAgentServerBaseUrl(): string {\n const configuredUrl = resolveAgentServerBaseUrl(getConfiguredBaseUrl());\n if (configuredUrl) return configuredUrl;\n\n if (typeof window !== \"undefined\") {\n return window.location.origin;\n }\n\n return \"http://127.0.0.1:8000\";\n}\n\nexport function getAgentServerSessionApiKey(): string | null {\n return getConfiguredSessionApiKey();\n}\n\nexport function getAgentServerWorkingDir(): string {\n const envDir = import.meta.env.VITE_WORKING_DIR?.trim();\n if (envDir) return envDir;\n\n const storedDir = readStoredConfig().workingDir?.trim();\n if (storedDir) return storedDir;\n\n return DEFAULT_WORKING_DIR;\n}\n\nexport function buildConversationWorkingDir(conversationId: string): string {\n const base = getAgentServerWorkingDir().replace(/\\/+$/, \"\");\n const hex = conversationId.replace(/-/g, \"\");\n return `${base}/${hex}`;\n}\n\nexport function getConfiguredWorkerUrls(): string[] {\n const raw = import.meta.env.VITE_WORKER_URLS?.trim();\n if (!raw) return [];\n\n return raw\n .split(\",\")\n .map((url: string) => normalizeBaseUrl(url))\n .filter((url: string | null): url is string => Boolean(url));\n}\n\nexport function getAgentServerHeaders(): Record<string, string> {\n const sessionApiKey = getAgentServerSessionApiKey();\n return sessionApiKey ? { \"X-Session-API-Key\": sessionApiKey } : {};\n}\n\n/**\n * Returns whether public skills from the OpenHands extensions marketplace\n * (https://github.com/OpenHands/extensions) should be loaded.\n *\n * Defaults to true. Set VITE_LOAD_PUBLIC_SKILLS=false to disable.\n */\nexport function shouldLoadPublicSkills(): boolean {\n return import.meta.env.VITE_LOAD_PUBLIC_SKILLS !== \"false\";\n}\n\n/**\n * Whether the deployment requires an API key from the user (public mode).\n *\n * Checks both the Vite build-time env var (`VITE_AUTH_REQUIRED`) and the\n * runtime flag injected by static-server.mjs (`window.__AGENT_CANVAS_AUTH_REQUIRED__`).\n * The runtime flag is needed for pre-built static binaries where\n * `VITE_AUTH_REQUIRED` was not set at build time.\n */\nexport function isAuthRequired(): boolean {\n return (\n import.meta.env.VITE_AUTH_REQUIRED === \"true\" ||\n (typeof window !== \"undefined\" &&\n (window as unknown as Record<string, unknown>)\n .__AGENT_CANVAS_AUTH_REQUIRED__ === true)\n );\n}\n\n/**\n * Returns true when the server was started in public mode and the user\n * has not yet pasted an API key (nothing in localStorage, nothing baked\n * in via `VITE_SESSION_API_KEY`).\n *\n * Used by `root.tsx` to gate the app behind {@link ApiKeyEntryScreen}\n * before any network request is attempted.\n */\nexport function isAuthRequiredAndMissing(): boolean {\n if (!isAuthRequired()) return false;\n return !getConfiguredSessionApiKey();\n}\n"],"mappings":";AAAA,IAAa,IAAkC,iCAClC,IAAsB;AAanC,SAAS,IAA4C;AACnD,KAAI,OAAO,SAAW,IAAa,QAAO,EAAE;AAE5C,KAAI;EACF,IAAM,IAAM,OAAO,aAAa,QAAQ,EAAgC;AAGxE,SAFK,IACU,KAAK,MAAM,EACnB,IAAU,EAAE,GAFF,EAAE;SAGb;AACN,SAAO,EAAE;;;AAIb,SAAS,EAAkB,GAAuC;AAChE,KAAI,OAAO,SAAW,IAAa;CAEnC,IAAM,IAAa,OAAO,YACxB,OAAO,QAAQ,EAAO,CAAC,SAAS,CAAC,GAAK,OAAW;AAC/C,MAAI,OAAO,KAAU,SAAU,QAAO,EAAE;EAExC,IAAM,IAAU,EAAM,MAAM;AAG5B,SAFK,IAEE,CAAC,CAAC,GAAK,EAAQ,CAAC,GAFF,EAAE;GAGvB,CACH;AAED,KAAI,OAAO,KAAK,EAAW,CAAC,WAAW,GAAG;AACxC,SAAO,aAAa,WAAW,EAAgC;AAC/D;;AAGF,QAAO,aAAa,QAClB,GACA,KAAK,UAAU,EAAW,CAC3B;;AAGH,SAAS,EAAW,GAAsC;AACxD,QAAO,GAAO,MAAM,IAAI;;AAG1B,SAAS,EAAiB,GAAsC;AAC9D,KAAI,CAAC,EAAO,QAAO;CAEnB,IAAM,IAAU,EAAM,MAAM,CAAC,QAAQ,OAAO,GAAG;AAW/C,QAVK,IAED,gBAAgB,KAAK,EAAQ,GACxB,IAGL,OAAO,SAAW,MACb,GAAG,OAAO,SAAS,SAAS,IAAI,MAGlC,UAAU,MAVI;;AAavB,SAAS,IAAsC;AAI7C,QAHkB,EAAiB,GAAkB,CAAC,QAClD,IAEG,EAAA,KAAA,EAAuD;;AAQhE,SAAgB,IAAuC;AACrD,QAAO,EAAA,KAAA,EAAgD;;AAGzD,SAAS,IAA4C;AAInD,QAHkB,EAAW,GAAkB,CAAC,cAC5C,IAEG,GAAuB;;AAuBhC,SAAgB,IAA+B;CAC7C,IAAM,IAAW,GAAuB;AACxC,KAAI,CAAC,EAAU;CAEf,IAAM,IAAe,GAAkB,EACjC,IAAY,EAAW,EAAa,cAAc;AACxD,CAAI,KAAa,MAAc,KAC7B,EAAkB;EAAE,GAAG;EAAc,eAAe;EAAU,CAAC;;AAInE,SAAS,EAAqB,GAA0B;AACtD,KAAI,OAAO,SAAW,IACpB,QAAO;AAGT,KAAI;EACF,IAAM,IAAgB,IAAI,IAAI,EAAQ,EAChC,IAAa,IAAI,IAAI;GAAC;GAAa;GAAa;GAAU,CAAC,EAC3D,IAAkB,OAAO,SAAS;AAExC,SACE,EAAW,IAAI,EAAc,SAAS,KACrC,CAAC,EAAW,IAAI,EAAgB,IAC/B,EAAc,aAAa;SAEzB;AACN,SAAO;;;AAIX,SAAS,EAA0B,GAAuC;AASxE,QARK,IAID,EAAqB,EAAQ,GACxB,OAAO,SAAS,SAGlB,IAPE;;AA2BX,SAAgB,IAAgC;AAQ9C,QAPsB,EAA0B,GAAsB,CAClE,KAEA,OAAO,SAAW,MACb,OAAO,SAAS,SAGlB;;AAGT,SAAgB,IAA6C;AAC3D,QAAO,GAA4B;;AAGrC,SAAgB,IAAmC;AAOjD,SANM,KAAA,IAA2C,MAAM,IAGrC,GAAkB,CAAC,YAAY,MAAM,IAGhD;;AAGT,SAAgB,EAA4B,GAAgC;AAG1E,QAAO,GAFM,GAA0B,CAAC,QAAQ,QAAQ,GAE9C,CAAK,GADH,EAAe,QAAQ,MAAM,GACvB;;AAapB,SAAgB,IAAgD;CAC9D,IAAM,IAAgB,GAA6B;AACnD,QAAO,IAAgB,EAAE,qBAAqB,GAAe,GAAG,EAAE;;AASpE,SAAgB,IAAkC;AAChD,QAAO;;AAWT,SAAgB,IAA0B;AACxC,QAEG,OAAO,SAAW,OAChB,OACE,mCAAmC"}
@@ -1,2 +1,2 @@
1
- require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`./default-backend.cjs`);var t=`openhands-backends`,n=`openhands-active-backend`;function r(e){return e===`local`||e===`cloud`}function i(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.id==`string`&&t.id.length>0&&typeof t.name==`string`&&typeof t.host==`string`&&typeof t.apiKey==`string`&&r(t.kind)}function a(e){try{return new URL(e).origin}catch{return e.replace(/\/+$/,``)}}function o(t){let n=e.makeDefaultLocalBackend();return t.id!==n.id||t.kind!==`local`||!n.apiKey||a(t.host)!==a(n.host)||t.apiKey===n.apiKey?t:{...t,apiKey:n.apiKey}}function s(e){if(!(typeof window>`u`))try{window.localStorage.setItem(t,JSON.stringify(e))}catch{}}function c(){if(typeof window>`u`)return[];try{let n=window.localStorage.getItem(t);if(n===null){let t=[e.makeDefaultLocalBackend()];return s(t),t}let r=JSON.parse(n);if(!Array.isArray(r))return[];let a=r.filter(i);if(a.length===0){let t=[e.makeDefaultLocalBackend()];return s(t),t}let c=a.map(o);return c.some((e,t)=>e!==a[t])&&s(c),c}catch{return[]}}function l(){if(typeof window>`u`)return null;try{let e=window.localStorage.getItem(n);if(!e)return null;let t=JSON.parse(e);if(typeof t!=`object`||!t||typeof t.backendId!=`string`)return null;let r=t.orgId;return{backendId:t.backendId,orgId:typeof r==`string`&&r.length>0?r:null}}catch{return null}}function u(e){if(!(typeof window>`u`))try{if(!e){window.localStorage.removeItem(n);return}window.localStorage.setItem(n,JSON.stringify({backendId:e.backendId,orgId:e.orgId??null}))}catch{}}exports.readStoredActiveBackend=l,exports.readStoredBackends=c,exports.writeStoredActiveBackend=u,exports.writeStoredBackends=s;
1
+ require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`../agent-server-config.cjs`),t=require(`./default-backend.cjs`);var n=`openhands-backends`,r=`openhands-active-backend`;function i(e){return e===`local`||e===`cloud`}function a(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.id==`string`&&t.id.length>0&&typeof t.name==`string`&&typeof t.host==`string`&&typeof t.apiKey==`string`&&i(t.kind)}function o(e){try{return new URL(e).origin}catch{return e.replace(/\/+$/,``)}}function s(e){let n=t.makeDefaultLocalBackend();return e.id!==n.id||e.kind!==`local`||!n.apiKey||o(e.host)!==o(n.host)||e.apiKey===n.apiKey?e:{...e,apiKey:n.apiKey}}function c(e){if(!(typeof window>`u`))try{window.localStorage.setItem(n,JSON.stringify(e))}catch{}}function l(){if(typeof window>`u`)return[];e.syncBakedSessionApiKey();try{let e=window.localStorage.getItem(n);if(e===null){let e=[t.makeDefaultLocalBackend()];return c(e),e}let r=JSON.parse(e);if(!Array.isArray(r))return[];let i=r.filter(a);if(i.length===0){let e=[t.makeDefaultLocalBackend()];return c(e),e}let o=i.map(s);return o.some((e,t)=>e!==i[t])&&c(o),o}catch{return[]}}function u(){if(typeof window>`u`)return null;try{let e=window.localStorage.getItem(r);if(!e)return null;let t=JSON.parse(e);if(typeof t!=`object`||!t||typeof t.backendId!=`string`)return null;let n=t.orgId;return{backendId:t.backendId,orgId:typeof n==`string`&&n.length>0?n:null}}catch{return null}}function d(e){if(!(typeof window>`u`))try{if(!e){window.localStorage.removeItem(r);return}window.localStorage.setItem(r,JSON.stringify({backendId:e.backendId,orgId:e.orgId??null}))}catch{}}exports.readStoredActiveBackend=u,exports.readStoredBackends=l,exports.writeStoredActiveBackend=d,exports.writeStoredBackends=c;
2
2
  //# sourceMappingURL=storage.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.cjs","names":[],"sources":["../../../src/api/backend-registry/storage.ts"],"sourcesContent":["import { makeDefaultLocalBackend } from \"./default-backend\";\nimport type { Backend, BackendKind, BackendSelection } from \"./types\";\n\nexport const BACKENDS_STORAGE_KEY = \"openhands-backends\";\nexport const ACTIVE_BACKEND_STORAGE_KEY = \"openhands-active-backend\";\n\nfunction isValidKind(value: unknown): value is BackendKind {\n return value === \"local\" || value === \"cloud\";\n}\n\nfunction isValidBackend(value: unknown): value is Backend {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Partial<Backend>;\n return (\n typeof v.id === \"string\" &&\n v.id.length > 0 &&\n typeof v.name === \"string\" &&\n typeof v.host === \"string\" &&\n typeof v.apiKey === \"string\" &&\n isValidKind(v.kind)\n );\n}\n\nfunction normalizeHostForComparison(host: string): string {\n try {\n return new URL(host).origin;\n } catch {\n return host.replace(/\\/+$/, \"\");\n }\n}\n\nfunction syncDefaultLocalBackendAuth(backend: Backend): Backend {\n const defaultBackend = makeDefaultLocalBackend();\n\n if (\n backend.id !== defaultBackend.id ||\n backend.kind !== \"local\" ||\n !defaultBackend.apiKey ||\n normalizeHostForComparison(backend.host) !==\n normalizeHostForComparison(defaultBackend.host)\n ) {\n return backend;\n }\n\n if (backend.apiKey === defaultBackend.apiKey) {\n return backend;\n }\n\n return {\n ...backend,\n apiKey: defaultBackend.apiKey,\n };\n}\n\nexport function writeStoredBackends(backends: Backend[]): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(BACKENDS_STORAGE_KEY, JSON.stringify(backends));\n } catch {\n /* ignore quota / serialization errors */\n }\n}\n\nexport function readStoredBackends(): Backend[] {\n if (typeof window === \"undefined\") return [];\n try {\n const raw = window.localStorage.getItem(BACKENDS_STORAGE_KEY);\n\n // First install: the storage key has never been written. Seed the\n // registry with one default local backend derived from the env /\n // agent-server-config so the user has something to talk to out of\n // the box.\n if (raw === null) {\n const seeded = [makeDefaultLocalBackend()];\n writeStoredBackends(seeded);\n return seeded;\n }\n\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n const valid = parsed.filter(isValidBackend);\n\n // If the stored array is empty (or everything in it failed validation),\n // re-seed with the default Local backend so the user always has a\n // working entry pointing at VITE_SESSION_API_KEY. With the dev scripts\n // persisting that key to ~/.openhands/agent-canvas/session-api-key.txt,\n // re-seeding is safe — the seeded entry will keep working across\n // restarts instead of going stale.\n if (valid.length === 0) {\n const seeded = [makeDefaultLocalBackend()];\n writeStoredBackends(seeded);\n return seeded;\n }\n\n const synced = valid.map(syncDefaultLocalBackendAuth);\n if (synced.some((backend, index) => backend !== valid[index])) {\n writeStoredBackends(synced);\n }\n\n return synced;\n } catch {\n return [];\n }\n}\n\nexport function readStoredActiveBackend(): BackendSelection | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(ACTIVE_BACKEND_STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as BackendSelection).backendId !== \"string\"\n ) {\n return null;\n }\n const orgIdRaw = (parsed as BackendSelection).orgId;\n return {\n backendId: (parsed as BackendSelection).backendId,\n orgId:\n typeof orgIdRaw === \"string\" && orgIdRaw.length > 0 ? orgIdRaw : null,\n };\n } catch {\n return null;\n }\n}\n\nexport function writeStoredActiveBackend(\n selection: BackendSelection | null,\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (!selection) {\n window.localStorage.removeItem(ACTIVE_BACKEND_STORAGE_KEY);\n return;\n }\n window.localStorage.setItem(\n ACTIVE_BACKEND_STORAGE_KEY,\n JSON.stringify({\n backendId: selection.backendId,\n orgId: selection.orgId ?? null,\n }),\n );\n } catch {\n /* ignore */\n }\n}\n"],"mappings":"yFAGA,IAAa,EAAuB,qBACvB,EAA6B,2BAE1C,SAAS,EAAY,EAAsC,CACzD,OAAO,IAAU,SAAW,IAAU,QAGxC,SAAS,EAAe,EAAkC,CACxD,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAI,EACV,OACE,OAAO,EAAE,IAAO,UAChB,EAAE,GAAG,OAAS,GACd,OAAO,EAAE,MAAS,UAClB,OAAO,EAAE,MAAS,UAClB,OAAO,EAAE,QAAW,UACpB,EAAY,EAAE,KAAK,CAIvB,SAAS,EAA2B,EAAsB,CACxD,GAAI,CACF,OAAO,IAAI,IAAI,EAAK,CAAC,YACf,CACN,OAAO,EAAK,QAAQ,OAAQ,GAAG,EAInC,SAAS,EAA4B,EAA2B,CAC9D,IAAM,EAAiB,EAAA,yBAAyB,CAgBhD,OAbE,EAAQ,KAAO,EAAe,IAC9B,EAAQ,OAAS,SACjB,CAAC,EAAe,QAChB,EAA2B,EAAQ,KAAK,GACtC,EAA2B,EAAe,KAAK,EAK/C,EAAQ,SAAW,EAAe,OAC7B,EAGF,CACL,GAAG,EACH,OAAQ,EAAe,OACxB,CAGH,SAAgB,EAAoB,EAA2B,CACzD,YAAO,OAAW,KACtB,GAAI,CACF,OAAO,aAAa,QAAQ,EAAsB,KAAK,UAAU,EAAS,CAAC,MACrE,GAKV,SAAgB,GAAgC,CAC9C,GAAI,OAAO,OAAW,IAAa,MAAO,EAAE,CAC5C,GAAI,CACF,IAAM,EAAM,OAAO,aAAa,QAAQ,EAAqB,CAM7D,GAAI,IAAQ,KAAM,CAChB,IAAM,EAAS,CAAC,EAAA,yBAAyB,CAAC,CAE1C,OADA,EAAoB,EAAO,CACpB,EAGT,IAAM,EAAS,KAAK,MAAM,EAAI,CAC9B,GAAI,CAAC,MAAM,QAAQ,EAAO,CAAE,MAAO,EAAE,CACrC,IAAM,EAAQ,EAAO,OAAO,EAAe,CAQ3C,GAAI,EAAM,SAAW,EAAG,CACtB,IAAM,EAAS,CAAC,EAAA,yBAAyB,CAAC,CAE1C,OADA,EAAoB,EAAO,CACpB,EAGT,IAAM,EAAS,EAAM,IAAI,EAA4B,CAKrD,OAJI,EAAO,MAAM,EAAS,IAAU,IAAY,EAAM,GAAO,EAC3D,EAAoB,EAAO,CAGtB,OACD,CACN,MAAO,EAAE,EAIb,SAAgB,GAAmD,CACjE,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,GAAI,CACF,IAAM,EAAM,OAAO,aAAa,QAAQ,EAA2B,CACnE,GAAI,CAAC,EAAK,OAAO,KACjB,IAAM,EAAS,KAAK,MAAM,EAAI,CAC9B,GACE,OAAO,GAAW,WAClB,GACA,OAAQ,EAA4B,WAAc,SAElD,OAAO,KAET,IAAM,EAAY,EAA4B,MAC9C,MAAO,CACL,UAAY,EAA4B,UACxC,MACE,OAAO,GAAa,UAAY,EAAS,OAAS,EAAI,EAAW,KACpE,MACK,CACN,OAAO,MAIX,SAAgB,EACd,EACM,CACF,YAAO,OAAW,KACtB,GAAI,CACF,GAAI,CAAC,EAAW,CACd,OAAO,aAAa,WAAW,EAA2B,CAC1D,OAEF,OAAO,aAAa,QAClB,EACA,KAAK,UAAU,CACb,UAAW,EAAU,UACrB,MAAO,EAAU,OAAS,KAC3B,CAAC,CACH,MACK"}
1
+ {"version":3,"file":"storage.cjs","names":[],"sources":["../../../src/api/backend-registry/storage.ts"],"sourcesContent":["import { syncBakedSessionApiKey } from \"../agent-server-config\";\nimport { makeDefaultLocalBackend } from \"./default-backend\";\nimport type { Backend, BackendKind, BackendSelection } from \"./types\";\n\nexport const BACKENDS_STORAGE_KEY = \"openhands-backends\";\nexport const ACTIVE_BACKEND_STORAGE_KEY = \"openhands-active-backend\";\n\nfunction isValidKind(value: unknown): value is BackendKind {\n return value === \"local\" || value === \"cloud\";\n}\n\nfunction isValidBackend(value: unknown): value is Backend {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Partial<Backend>;\n return (\n typeof v.id === \"string\" &&\n v.id.length > 0 &&\n typeof v.name === \"string\" &&\n typeof v.host === \"string\" &&\n typeof v.apiKey === \"string\" &&\n isValidKind(v.kind)\n );\n}\n\nfunction normalizeHostForComparison(host: string): string {\n try {\n return new URL(host).origin;\n } catch {\n return host.replace(/\\/+$/, \"\");\n }\n}\n\nfunction syncDefaultLocalBackendAuth(backend: Backend): Backend {\n const defaultBackend = makeDefaultLocalBackend();\n\n if (\n backend.id !== defaultBackend.id ||\n backend.kind !== \"local\" ||\n !defaultBackend.apiKey ||\n normalizeHostForComparison(backend.host) !==\n normalizeHostForComparison(defaultBackend.host)\n ) {\n return backend;\n }\n\n if (backend.apiKey === defaultBackend.apiKey) {\n return backend;\n }\n\n return {\n ...backend,\n apiKey: defaultBackend.apiKey,\n };\n}\n\nexport function writeStoredBackends(backends: Backend[]): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(BACKENDS_STORAGE_KEY, JSON.stringify(backends));\n } catch {\n /* ignore quota / serialization errors */\n }\n}\n\nexport function readStoredBackends(): Backend[] {\n if (typeof window === \"undefined\") return [];\n\n // Ensure the baked-in session API key (VITE_SESSION_API_KEY) is synced\n // into the legacy `openhands-agent-server-config` localStorage entry\n // BEFORE we read the backend registry. This matters when the user\n // restarts the stack with a different LOCAL_BACKEND_API_KEY — without\n // this call the stale key in `openhands-agent-server-config` shadows\n // the new baked key, making `makeDefaultLocalBackend()` (and the\n // downstream `syncDefaultLocalBackendAuth`) read the wrong value.\n syncBakedSessionApiKey();\n\n try {\n const raw = window.localStorage.getItem(BACKENDS_STORAGE_KEY);\n\n // First install: the storage key has never been written. Seed the\n // registry with one default local backend derived from the env /\n // agent-server-config so the user has something to talk to out of\n // the box.\n if (raw === null) {\n const seeded = [makeDefaultLocalBackend()];\n writeStoredBackends(seeded);\n return seeded;\n }\n\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n const valid = parsed.filter(isValidBackend);\n\n // If the stored array is empty (or everything in it failed validation),\n // re-seed with the default Local backend so the user always has a\n // working entry pointing at VITE_SESSION_API_KEY. With the dev scripts\n // persisting that key to ~/.openhands/agent-canvas/session-api-key.txt,\n // re-seeding is safe — the seeded entry will keep working across\n // restarts instead of going stale.\n if (valid.length === 0) {\n const seeded = [makeDefaultLocalBackend()];\n writeStoredBackends(seeded);\n return seeded;\n }\n\n const synced = valid.map(syncDefaultLocalBackendAuth);\n if (synced.some((backend, index) => backend !== valid[index])) {\n writeStoredBackends(synced);\n }\n\n return synced;\n } catch {\n return [];\n }\n}\n\nexport function readStoredActiveBackend(): BackendSelection | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(ACTIVE_BACKEND_STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as BackendSelection).backendId !== \"string\"\n ) {\n return null;\n }\n const orgIdRaw = (parsed as BackendSelection).orgId;\n return {\n backendId: (parsed as BackendSelection).backendId,\n orgId:\n typeof orgIdRaw === \"string\" && orgIdRaw.length > 0 ? orgIdRaw : null,\n };\n } catch {\n return null;\n }\n}\n\nexport function writeStoredActiveBackend(\n selection: BackendSelection | null,\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (!selection) {\n window.localStorage.removeItem(ACTIVE_BACKEND_STORAGE_KEY);\n return;\n }\n window.localStorage.setItem(\n ACTIVE_BACKEND_STORAGE_KEY,\n JSON.stringify({\n backendId: selection.backendId,\n orgId: selection.orgId ?? null,\n }),\n );\n } catch {\n /* ignore */\n }\n}\n"],"mappings":"iIAIA,IAAa,EAAuB,qBACvB,EAA6B,2BAE1C,SAAS,EAAY,EAAsC,CACzD,OAAO,IAAU,SAAW,IAAU,QAGxC,SAAS,EAAe,EAAkC,CACxD,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAI,EACV,OACE,OAAO,EAAE,IAAO,UAChB,EAAE,GAAG,OAAS,GACd,OAAO,EAAE,MAAS,UAClB,OAAO,EAAE,MAAS,UAClB,OAAO,EAAE,QAAW,UACpB,EAAY,EAAE,KAAK,CAIvB,SAAS,EAA2B,EAAsB,CACxD,GAAI,CACF,OAAO,IAAI,IAAI,EAAK,CAAC,YACf,CACN,OAAO,EAAK,QAAQ,OAAQ,GAAG,EAInC,SAAS,EAA4B,EAA2B,CAC9D,IAAM,EAAiB,EAAA,yBAAyB,CAgBhD,OAbE,EAAQ,KAAO,EAAe,IAC9B,EAAQ,OAAS,SACjB,CAAC,EAAe,QAChB,EAA2B,EAAQ,KAAK,GACtC,EAA2B,EAAe,KAAK,EAK/C,EAAQ,SAAW,EAAe,OAC7B,EAGF,CACL,GAAG,EACH,OAAQ,EAAe,OACxB,CAGH,SAAgB,EAAoB,EAA2B,CACzD,YAAO,OAAW,KACtB,GAAI,CACF,OAAO,aAAa,QAAQ,EAAsB,KAAK,UAAU,EAAS,CAAC,MACrE,GAKV,SAAgB,GAAgC,CAC9C,GAAI,OAAO,OAAW,IAAa,MAAO,EAAE,CAS5C,EAAA,wBAAwB,CAExB,GAAI,CACF,IAAM,EAAM,OAAO,aAAa,QAAQ,EAAqB,CAM7D,GAAI,IAAQ,KAAM,CAChB,IAAM,EAAS,CAAC,EAAA,yBAAyB,CAAC,CAE1C,OADA,EAAoB,EAAO,CACpB,EAGT,IAAM,EAAS,KAAK,MAAM,EAAI,CAC9B,GAAI,CAAC,MAAM,QAAQ,EAAO,CAAE,MAAO,EAAE,CACrC,IAAM,EAAQ,EAAO,OAAO,EAAe,CAQ3C,GAAI,EAAM,SAAW,EAAG,CACtB,IAAM,EAAS,CAAC,EAAA,yBAAyB,CAAC,CAE1C,OADA,EAAoB,EAAO,CACpB,EAGT,IAAM,EAAS,EAAM,IAAI,EAA4B,CAKrD,OAJI,EAAO,MAAM,EAAS,IAAU,IAAY,EAAM,GAAO,EAC3D,EAAoB,EAAO,CAGtB,OACD,CACN,MAAO,EAAE,EAIb,SAAgB,GAAmD,CACjE,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,GAAI,CACF,IAAM,EAAM,OAAO,aAAa,QAAQ,EAA2B,CACnE,GAAI,CAAC,EAAK,OAAO,KACjB,IAAM,EAAS,KAAK,MAAM,EAAI,CAC9B,GACE,OAAO,GAAW,WAClB,GACA,OAAQ,EAA4B,WAAc,SAElD,OAAO,KAET,IAAM,EAAY,EAA4B,MAC9C,MAAO,CACL,UAAY,EAA4B,UACxC,MACE,OAAO,GAAa,UAAY,EAAS,OAAS,EAAI,EAAW,KACpE,MACK,CACN,OAAO,MAIX,SAAgB,EACd,EACM,CACF,YAAO,OAAW,KACtB,GAAI,CACF,GAAI,CAAC,EAAW,CACd,OAAO,aAAa,WAAW,EAA2B,CAC1D,OAEF,OAAO,aAAa,QAClB,EACA,KAAK,UAAU,CACb,UAAW,EAAU,UACrB,MAAO,EAAU,OAAS,KAC3B,CAAC,CACH,MACK"}
@@ -1,83 +1,85 @@
1
- import { makeDefaultLocalBackend as e } from "./default-backend.js";
1
+ import { syncBakedSessionApiKey as e } from "../agent-server-config.js";
2
+ import { makeDefaultLocalBackend as t } from "./default-backend.js";
2
3
  //#region src/api/backend-registry/storage.ts
3
- var t = "openhands-backends", n = "openhands-active-backend";
4
- function r(e) {
4
+ var n = "openhands-backends", r = "openhands-active-backend";
5
+ function i(e) {
5
6
  return e === "local" || e === "cloud";
6
7
  }
7
- function i(e) {
8
+ function a(e) {
8
9
  if (typeof e != "object" || !e) return !1;
9
10
  let t = e;
10
- return typeof t.id == "string" && t.id.length > 0 && typeof t.name == "string" && typeof t.host == "string" && typeof t.apiKey == "string" && r(t.kind);
11
+ return typeof t.id == "string" && t.id.length > 0 && typeof t.name == "string" && typeof t.host == "string" && typeof t.apiKey == "string" && i(t.kind);
11
12
  }
12
- function a(e) {
13
+ function o(e) {
13
14
  try {
14
15
  return new URL(e).origin;
15
16
  } catch {
16
17
  return e.replace(/\/+$/, "");
17
18
  }
18
19
  }
19
- function o(t) {
20
- let n = e();
21
- return t.id !== n.id || t.kind !== "local" || !n.apiKey || a(t.host) !== a(n.host) || t.apiKey === n.apiKey ? t : {
22
- ...t,
20
+ function s(e) {
21
+ let n = t();
22
+ return e.id !== n.id || e.kind !== "local" || !n.apiKey || o(e.host) !== o(n.host) || e.apiKey === n.apiKey ? e : {
23
+ ...e,
23
24
  apiKey: n.apiKey
24
25
  };
25
26
  }
26
- function s(e) {
27
+ function c(e) {
27
28
  if (!(typeof window > "u")) try {
28
- window.localStorage.setItem(t, JSON.stringify(e));
29
+ window.localStorage.setItem(n, JSON.stringify(e));
29
30
  } catch {}
30
31
  }
31
- function c() {
32
+ function l() {
32
33
  if (typeof window > "u") return [];
34
+ e();
33
35
  try {
34
- let n = window.localStorage.getItem(t);
35
- if (n === null) {
36
- let t = [e()];
37
- return s(t), t;
36
+ let e = window.localStorage.getItem(n);
37
+ if (e === null) {
38
+ let e = [t()];
39
+ return c(e), e;
38
40
  }
39
- let r = JSON.parse(n);
41
+ let r = JSON.parse(e);
40
42
  if (!Array.isArray(r)) return [];
41
- let a = r.filter(i);
42
- if (a.length === 0) {
43
- let t = [e()];
44
- return s(t), t;
43
+ let i = r.filter(a);
44
+ if (i.length === 0) {
45
+ let e = [t()];
46
+ return c(e), e;
45
47
  }
46
- let c = a.map(o);
47
- return c.some((e, t) => e !== a[t]) && s(c), c;
48
+ let o = i.map(s);
49
+ return o.some((e, t) => e !== i[t]) && c(o), o;
48
50
  } catch {
49
51
  return [];
50
52
  }
51
53
  }
52
- function l() {
54
+ function u() {
53
55
  if (typeof window > "u") return null;
54
56
  try {
55
- let e = window.localStorage.getItem(n);
57
+ let e = window.localStorage.getItem(r);
56
58
  if (!e) return null;
57
59
  let t = JSON.parse(e);
58
60
  if (typeof t != "object" || !t || typeof t.backendId != "string") return null;
59
- let r = t.orgId;
61
+ let n = t.orgId;
60
62
  return {
61
63
  backendId: t.backendId,
62
- orgId: typeof r == "string" && r.length > 0 ? r : null
64
+ orgId: typeof n == "string" && n.length > 0 ? n : null
63
65
  };
64
66
  } catch {
65
67
  return null;
66
68
  }
67
69
  }
68
- function u(e) {
70
+ function d(e) {
69
71
  if (!(typeof window > "u")) try {
70
72
  if (!e) {
71
- window.localStorage.removeItem(n);
73
+ window.localStorage.removeItem(r);
72
74
  return;
73
75
  }
74
- window.localStorage.setItem(n, JSON.stringify({
76
+ window.localStorage.setItem(r, JSON.stringify({
75
77
  backendId: e.backendId,
76
78
  orgId: e.orgId ?? null
77
79
  }));
78
80
  } catch {}
79
81
  }
80
82
  //#endregion
81
- export { l as readStoredActiveBackend, c as readStoredBackends, u as writeStoredActiveBackend, s as writeStoredBackends };
83
+ export { u as readStoredActiveBackend, l as readStoredBackends, d as writeStoredActiveBackend, c as writeStoredBackends };
82
84
 
83
85
  //# sourceMappingURL=storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","names":[],"sources":["../../../src/api/backend-registry/storage.ts"],"sourcesContent":["import { makeDefaultLocalBackend } from \"./default-backend\";\nimport type { Backend, BackendKind, BackendSelection } from \"./types\";\n\nexport const BACKENDS_STORAGE_KEY = \"openhands-backends\";\nexport const ACTIVE_BACKEND_STORAGE_KEY = \"openhands-active-backend\";\n\nfunction isValidKind(value: unknown): value is BackendKind {\n return value === \"local\" || value === \"cloud\";\n}\n\nfunction isValidBackend(value: unknown): value is Backend {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Partial<Backend>;\n return (\n typeof v.id === \"string\" &&\n v.id.length > 0 &&\n typeof v.name === \"string\" &&\n typeof v.host === \"string\" &&\n typeof v.apiKey === \"string\" &&\n isValidKind(v.kind)\n );\n}\n\nfunction normalizeHostForComparison(host: string): string {\n try {\n return new URL(host).origin;\n } catch {\n return host.replace(/\\/+$/, \"\");\n }\n}\n\nfunction syncDefaultLocalBackendAuth(backend: Backend): Backend {\n const defaultBackend = makeDefaultLocalBackend();\n\n if (\n backend.id !== defaultBackend.id ||\n backend.kind !== \"local\" ||\n !defaultBackend.apiKey ||\n normalizeHostForComparison(backend.host) !==\n normalizeHostForComparison(defaultBackend.host)\n ) {\n return backend;\n }\n\n if (backend.apiKey === defaultBackend.apiKey) {\n return backend;\n }\n\n return {\n ...backend,\n apiKey: defaultBackend.apiKey,\n };\n}\n\nexport function writeStoredBackends(backends: Backend[]): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(BACKENDS_STORAGE_KEY, JSON.stringify(backends));\n } catch {\n /* ignore quota / serialization errors */\n }\n}\n\nexport function readStoredBackends(): Backend[] {\n if (typeof window === \"undefined\") return [];\n try {\n const raw = window.localStorage.getItem(BACKENDS_STORAGE_KEY);\n\n // First install: the storage key has never been written. Seed the\n // registry with one default local backend derived from the env /\n // agent-server-config so the user has something to talk to out of\n // the box.\n if (raw === null) {\n const seeded = [makeDefaultLocalBackend()];\n writeStoredBackends(seeded);\n return seeded;\n }\n\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n const valid = parsed.filter(isValidBackend);\n\n // If the stored array is empty (or everything in it failed validation),\n // re-seed with the default Local backend so the user always has a\n // working entry pointing at VITE_SESSION_API_KEY. With the dev scripts\n // persisting that key to ~/.openhands/agent-canvas/session-api-key.txt,\n // re-seeding is safe — the seeded entry will keep working across\n // restarts instead of going stale.\n if (valid.length === 0) {\n const seeded = [makeDefaultLocalBackend()];\n writeStoredBackends(seeded);\n return seeded;\n }\n\n const synced = valid.map(syncDefaultLocalBackendAuth);\n if (synced.some((backend, index) => backend !== valid[index])) {\n writeStoredBackends(synced);\n }\n\n return synced;\n } catch {\n return [];\n }\n}\n\nexport function readStoredActiveBackend(): BackendSelection | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(ACTIVE_BACKEND_STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as BackendSelection).backendId !== \"string\"\n ) {\n return null;\n }\n const orgIdRaw = (parsed as BackendSelection).orgId;\n return {\n backendId: (parsed as BackendSelection).backendId,\n orgId:\n typeof orgIdRaw === \"string\" && orgIdRaw.length > 0 ? orgIdRaw : null,\n };\n } catch {\n return null;\n }\n}\n\nexport function writeStoredActiveBackend(\n selection: BackendSelection | null,\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (!selection) {\n window.localStorage.removeItem(ACTIVE_BACKEND_STORAGE_KEY);\n return;\n }\n window.localStorage.setItem(\n ACTIVE_BACKEND_STORAGE_KEY,\n JSON.stringify({\n backendId: selection.backendId,\n orgId: selection.orgId ?? null,\n }),\n );\n } catch {\n /* ignore */\n }\n}\n"],"mappings":";;AAGA,IAAa,IAAuB,sBACvB,IAA6B;AAE1C,SAAS,EAAY,GAAsC;AACzD,QAAO,MAAU,WAAW,MAAU;;AAGxC,SAAS,EAAe,GAAkC;AACxD,KAAI,OAAO,KAAU,aAAY,EAAgB,QAAO;CACxD,IAAM,IAAI;AACV,QACE,OAAO,EAAE,MAAO,YAChB,EAAE,GAAG,SAAS,KACd,OAAO,EAAE,QAAS,YAClB,OAAO,EAAE,QAAS,YAClB,OAAO,EAAE,UAAW,YACpB,EAAY,EAAE,KAAK;;AAIvB,SAAS,EAA2B,GAAsB;AACxD,KAAI;AACF,SAAO,IAAI,IAAI,EAAK,CAAC;SACf;AACN,SAAO,EAAK,QAAQ,QAAQ,GAAG;;;AAInC,SAAS,EAA4B,GAA2B;CAC9D,IAAM,IAAiB,GAAyB;AAgBhD,QAbE,EAAQ,OAAO,EAAe,MAC9B,EAAQ,SAAS,WACjB,CAAC,EAAe,UAChB,EAA2B,EAAQ,KAAK,KACtC,EAA2B,EAAe,KAAK,IAK/C,EAAQ,WAAW,EAAe,SAC7B,IAGF;EACL,GAAG;EACH,QAAQ,EAAe;EACxB;;AAGH,SAAgB,EAAoB,GAA2B;AACzD,cAAO,SAAW,KACtB,KAAI;AACF,SAAO,aAAa,QAAQ,GAAsB,KAAK,UAAU,EAAS,CAAC;SACrE;;AAKV,SAAgB,IAAgC;AAC9C,KAAI,OAAO,SAAW,IAAa,QAAO,EAAE;AAC5C,KAAI;EACF,IAAM,IAAM,OAAO,aAAa,QAAQ,EAAqB;AAM7D,MAAI,MAAQ,MAAM;GAChB,IAAM,IAAS,CAAC,GAAyB,CAAC;AAE1C,UADA,EAAoB,EAAO,EACpB;;EAGT,IAAM,IAAS,KAAK,MAAM,EAAI;AAC9B,MAAI,CAAC,MAAM,QAAQ,EAAO,CAAE,QAAO,EAAE;EACrC,IAAM,IAAQ,EAAO,OAAO,EAAe;AAQ3C,MAAI,EAAM,WAAW,GAAG;GACtB,IAAM,IAAS,CAAC,GAAyB,CAAC;AAE1C,UADA,EAAoB,EAAO,EACpB;;EAGT,IAAM,IAAS,EAAM,IAAI,EAA4B;AAKrD,SAJI,EAAO,MAAM,GAAS,MAAU,MAAY,EAAM,GAAO,IAC3D,EAAoB,EAAO,EAGtB;SACD;AACN,SAAO,EAAE;;;AAIb,SAAgB,IAAmD;AACjE,KAAI,OAAO,SAAW,IAAa,QAAO;AAC1C,KAAI;EACF,IAAM,IAAM,OAAO,aAAa,QAAQ,EAA2B;AACnE,MAAI,CAAC,EAAK,QAAO;EACjB,IAAM,IAAS,KAAK,MAAM,EAAI;AAC9B,MACE,OAAO,KAAW,aAClB,KACA,OAAQ,EAA4B,aAAc,SAElD,QAAO;EAET,IAAM,IAAY,EAA4B;AAC9C,SAAO;GACL,WAAY,EAA4B;GACxC,OACE,OAAO,KAAa,YAAY,EAAS,SAAS,IAAI,IAAW;GACpE;SACK;AACN,SAAO;;;AAIX,SAAgB,EACd,GACM;AACF,cAAO,SAAW,KACtB,KAAI;AACF,MAAI,CAAC,GAAW;AACd,UAAO,aAAa,WAAW,EAA2B;AAC1D;;AAEF,SAAO,aAAa,QAClB,GACA,KAAK,UAAU;GACb,WAAW,EAAU;GACrB,OAAO,EAAU,SAAS;GAC3B,CAAC,CACH;SACK"}
1
+ {"version":3,"file":"storage.js","names":[],"sources":["../../../src/api/backend-registry/storage.ts"],"sourcesContent":["import { syncBakedSessionApiKey } from \"../agent-server-config\";\nimport { makeDefaultLocalBackend } from \"./default-backend\";\nimport type { Backend, BackendKind, BackendSelection } from \"./types\";\n\nexport const BACKENDS_STORAGE_KEY = \"openhands-backends\";\nexport const ACTIVE_BACKEND_STORAGE_KEY = \"openhands-active-backend\";\n\nfunction isValidKind(value: unknown): value is BackendKind {\n return value === \"local\" || value === \"cloud\";\n}\n\nfunction isValidBackend(value: unknown): value is Backend {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Partial<Backend>;\n return (\n typeof v.id === \"string\" &&\n v.id.length > 0 &&\n typeof v.name === \"string\" &&\n typeof v.host === \"string\" &&\n typeof v.apiKey === \"string\" &&\n isValidKind(v.kind)\n );\n}\n\nfunction normalizeHostForComparison(host: string): string {\n try {\n return new URL(host).origin;\n } catch {\n return host.replace(/\\/+$/, \"\");\n }\n}\n\nfunction syncDefaultLocalBackendAuth(backend: Backend): Backend {\n const defaultBackend = makeDefaultLocalBackend();\n\n if (\n backend.id !== defaultBackend.id ||\n backend.kind !== \"local\" ||\n !defaultBackend.apiKey ||\n normalizeHostForComparison(backend.host) !==\n normalizeHostForComparison(defaultBackend.host)\n ) {\n return backend;\n }\n\n if (backend.apiKey === defaultBackend.apiKey) {\n return backend;\n }\n\n return {\n ...backend,\n apiKey: defaultBackend.apiKey,\n };\n}\n\nexport function writeStoredBackends(backends: Backend[]): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(BACKENDS_STORAGE_KEY, JSON.stringify(backends));\n } catch {\n /* ignore quota / serialization errors */\n }\n}\n\nexport function readStoredBackends(): Backend[] {\n if (typeof window === \"undefined\") return [];\n\n // Ensure the baked-in session API key (VITE_SESSION_API_KEY) is synced\n // into the legacy `openhands-agent-server-config` localStorage entry\n // BEFORE we read the backend registry. This matters when the user\n // restarts the stack with a different LOCAL_BACKEND_API_KEY — without\n // this call the stale key in `openhands-agent-server-config` shadows\n // the new baked key, making `makeDefaultLocalBackend()` (and the\n // downstream `syncDefaultLocalBackendAuth`) read the wrong value.\n syncBakedSessionApiKey();\n\n try {\n const raw = window.localStorage.getItem(BACKENDS_STORAGE_KEY);\n\n // First install: the storage key has never been written. Seed the\n // registry with one default local backend derived from the env /\n // agent-server-config so the user has something to talk to out of\n // the box.\n if (raw === null) {\n const seeded = [makeDefaultLocalBackend()];\n writeStoredBackends(seeded);\n return seeded;\n }\n\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n const valid = parsed.filter(isValidBackend);\n\n // If the stored array is empty (or everything in it failed validation),\n // re-seed with the default Local backend so the user always has a\n // working entry pointing at VITE_SESSION_API_KEY. With the dev scripts\n // persisting that key to ~/.openhands/agent-canvas/session-api-key.txt,\n // re-seeding is safe — the seeded entry will keep working across\n // restarts instead of going stale.\n if (valid.length === 0) {\n const seeded = [makeDefaultLocalBackend()];\n writeStoredBackends(seeded);\n return seeded;\n }\n\n const synced = valid.map(syncDefaultLocalBackendAuth);\n if (synced.some((backend, index) => backend !== valid[index])) {\n writeStoredBackends(synced);\n }\n\n return synced;\n } catch {\n return [];\n }\n}\n\nexport function readStoredActiveBackend(): BackendSelection | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(ACTIVE_BACKEND_STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as BackendSelection).backendId !== \"string\"\n ) {\n return null;\n }\n const orgIdRaw = (parsed as BackendSelection).orgId;\n return {\n backendId: (parsed as BackendSelection).backendId,\n orgId:\n typeof orgIdRaw === \"string\" && orgIdRaw.length > 0 ? orgIdRaw : null,\n };\n } catch {\n return null;\n }\n}\n\nexport function writeStoredActiveBackend(\n selection: BackendSelection | null,\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (!selection) {\n window.localStorage.removeItem(ACTIVE_BACKEND_STORAGE_KEY);\n return;\n }\n window.localStorage.setItem(\n ACTIVE_BACKEND_STORAGE_KEY,\n JSON.stringify({\n backendId: selection.backendId,\n orgId: selection.orgId ?? null,\n }),\n );\n } catch {\n /* ignore */\n }\n}\n"],"mappings":";;;AAIA,IAAa,IAAuB,sBACvB,IAA6B;AAE1C,SAAS,EAAY,GAAsC;AACzD,QAAO,MAAU,WAAW,MAAU;;AAGxC,SAAS,EAAe,GAAkC;AACxD,KAAI,OAAO,KAAU,aAAY,EAAgB,QAAO;CACxD,IAAM,IAAI;AACV,QACE,OAAO,EAAE,MAAO,YAChB,EAAE,GAAG,SAAS,KACd,OAAO,EAAE,QAAS,YAClB,OAAO,EAAE,QAAS,YAClB,OAAO,EAAE,UAAW,YACpB,EAAY,EAAE,KAAK;;AAIvB,SAAS,EAA2B,GAAsB;AACxD,KAAI;AACF,SAAO,IAAI,IAAI,EAAK,CAAC;SACf;AACN,SAAO,EAAK,QAAQ,QAAQ,GAAG;;;AAInC,SAAS,EAA4B,GAA2B;CAC9D,IAAM,IAAiB,GAAyB;AAgBhD,QAbE,EAAQ,OAAO,EAAe,MAC9B,EAAQ,SAAS,WACjB,CAAC,EAAe,UAChB,EAA2B,EAAQ,KAAK,KACtC,EAA2B,EAAe,KAAK,IAK/C,EAAQ,WAAW,EAAe,SAC7B,IAGF;EACL,GAAG;EACH,QAAQ,EAAe;EACxB;;AAGH,SAAgB,EAAoB,GAA2B;AACzD,cAAO,SAAW,KACtB,KAAI;AACF,SAAO,aAAa,QAAQ,GAAsB,KAAK,UAAU,EAAS,CAAC;SACrE;;AAKV,SAAgB,IAAgC;AAC9C,KAAI,OAAO,SAAW,IAAa,QAAO,EAAE;AAS5C,IAAwB;AAExB,KAAI;EACF,IAAM,IAAM,OAAO,aAAa,QAAQ,EAAqB;AAM7D,MAAI,MAAQ,MAAM;GAChB,IAAM,IAAS,CAAC,GAAyB,CAAC;AAE1C,UADA,EAAoB,EAAO,EACpB;;EAGT,IAAM,IAAS,KAAK,MAAM,EAAI;AAC9B,MAAI,CAAC,MAAM,QAAQ,EAAO,CAAE,QAAO,EAAE;EACrC,IAAM,IAAQ,EAAO,OAAO,EAAe;AAQ3C,MAAI,EAAM,WAAW,GAAG;GACtB,IAAM,IAAS,CAAC,GAAyB,CAAC;AAE1C,UADA,EAAoB,EAAO,EACpB;;EAGT,IAAM,IAAS,EAAM,IAAI,EAA4B;AAKrD,SAJI,EAAO,MAAM,GAAS,MAAU,MAAY,EAAM,GAAO,IAC3D,EAAoB,EAAO,EAGtB;SACD;AACN,SAAO,EAAE;;;AAIb,SAAgB,IAAmD;AACjE,KAAI,OAAO,SAAW,IAAa,QAAO;AAC1C,KAAI;EACF,IAAM,IAAM,OAAO,aAAa,QAAQ,EAA2B;AACnE,MAAI,CAAC,EAAK,QAAO;EACjB,IAAM,IAAS,KAAK,MAAM,EAAI;AAC9B,MACE,OAAO,KAAW,aAClB,KACA,OAAQ,EAA4B,aAAc,SAElD,QAAO;EAET,IAAM,IAAY,EAA4B;AAC9C,SAAO;GACL,WAAY,EAA4B;GACxC,OACE,OAAO,KAAa,YAAY,EAAS,SAAS,IAAI,IAAW;GACpE;SACK;AACN,SAAO;;;AAIX,SAAgB,EACd,GACM;AACF,cAAO,SAAW,KACtB,KAAI;AACF,MAAI,CAAC,GAAW;AACd,UAAO,aAAa,WAAW,EAA2B;AAC1D;;AAEF,SAAO,aAAa,QAClB,GACA,KAAK,UAAU;GACb,WAAW,EAAU;GACrB,OAAO,EAAU,SAAS;GAC3B,CAAC,CACH;SACK"}