@openhands/agent-canvas 1.0.0-rc.1 → 1.0.0-rc.3
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.
- package/README.md +2 -2
- package/README.windows.md +2 -2
- package/build/assets/{QueryClientProvider-w1cZWY41.js → QueryClientProvider-7oLZ1RtQ.js} +1 -1
- package/build/assets/{Trans-BJeYqz2A.js → Trans-rF21Jwln.js} +1 -1
- package/build/assets/acp-providers-C55k29rf.js +1 -0
- package/build/assets/active-backend-context-CQTk4wD8.js +1 -0
- package/build/assets/add-backend-modal-BdYxoUdL.js +1 -0
- package/build/assets/agent-server-client-options-Dvgeb3x4.js +1 -0
- package/build/assets/agent-server-compatibility-BANjmMTo.js +1 -0
- package/build/assets/agent-server-conversation-service.api-js3oYcdU.js +5 -0
- package/build/assets/{agent-settings-BXBaybB_.js → agent-settings-BNrffu3I.js} +1 -1
- package/build/assets/{alert-banner-NeUl1-PQ.js → alert-banner-BzU93oDh.js} +1 -1
- package/build/assets/{analytics-consent-form-modal-DxkThW4K.js → analytics-consent-form-modal-Ce-_Skcd.js} +1 -1
- package/build/assets/api-key-entry-screen-CGyS-Cna.js +1 -0
- package/build/assets/{app-settings-C-U6jONZ.js → app-settings-DmOs4oik.js} +1 -1
- package/build/assets/automation-detail-DLbCVJCw.js +1 -0
- package/build/assets/{automations-list-BLJzAd-p.js → automations-list-Ces5shz5.js} +1 -1
- package/build/assets/{back-nav-button-DgkK0Ka6.js → back-nav-button-BDM9vr_o.js} +1 -1
- package/build/assets/backend-form-modal-BUy-PzZN.js +1 -0
- package/build/assets/{backend-synced-settings-badge-BktJcGgH.js → backend-synced-settings-badge-1A63yxGs.js} +1 -1
- package/build/assets/{base-modal-DZCNv0A4.js → base-modal-Ba5WcNb6.js} +1 -1
- package/build/assets/{brand-button-LBFNic99.js → brand-button-BoiPxAGm.js} +1 -1
- package/build/assets/browser-B8bp1IqT.js +5 -0
- package/build/assets/browser-store-C5uQbbIi.js +1 -0
- package/build/assets/{browser-tab-be3QvXg9.js → browser-tab-CiBRWB4X.js} +1 -1
- package/build/assets/chat-send-button-qKFZuB9E.js +1 -0
- package/build/assets/check-BDAbW7je.js +1 -0
- package/build/assets/{checkmark-Rmpruj7q.js → checkmark-SEZPnU2I.js} +1 -1
- package/build/assets/{chevron-down-KhWYEjeW.js → chevron-down-DUxWwzTm.js} +1 -1
- package/build/assets/{chevron-left-small-6nyFCWVQ.js → chevron-left-small-x9_-ucHG.js} +1 -1
- package/build/assets/{circle-plus-check-toggle-DquBwJ_6.js → circle-plus-check-toggle-CboDp7XB.js} +1 -1
- package/build/assets/{close-D_o3d8QM.js → close-CbOsKMRg.js} +1 -1
- package/build/assets/{code-tag-DhsjDB-v.js → code-tag-C0vR_IQH.js} +1 -1
- package/build/assets/{color-themes-0biOprdo.js → color-themes-B9n7pBQl.js} +1 -1
- package/build/assets/{combobox-caret-CO7eozIY.js → combobox-caret-DwMmhrrA.js} +1 -1
- package/build/assets/{command-store-UzKGSUC2.js → command-store-BUWgE-vZ.js} +1 -1
- package/build/assets/{condenser-settings-BulzYEuW.js → condenser-settings-FCBjvqSo.js} +1 -1
- package/build/assets/{confirmation-modal-CMAtd9R0.js → confirmation-modal-BPgWqWRI.js} +1 -1
- package/build/assets/{context-menu-list-item-D0swnhFt.js → context-menu-list-item-CHWCx1Mc.js} +1 -1
- package/build/assets/conversation-CT8AvxEH.js +1 -0
- package/build/assets/conversation-panel-DqDDs4WZ.js +1 -0
- package/build/assets/conversation-service.api-y_eB_43m.js +1 -0
- package/build/assets/conversation-state-store-DpgQaK_O.js +1 -0
- package/build/assets/conversation-store-CiYGBud3.js +6 -0
- package/build/assets/{conversation-tab-empty-state-DYjKsg_c.js → conversation-tab-empty-state-Ba5hbJhn.js} +1 -1
- package/build/assets/conversation-vNuLKgz6.js +19 -0
- package/build/assets/conversation-websocket-context-Bb4XBXoc.js +3 -0
- package/build/assets/{copy-BM0RpLez.js → copy-CAxUvM-U.js} +1 -1
- package/build/assets/{custom-toast-handlers-BohXx7uh.js → custom-toast-handlers-fgD4IYsu.js} +1 -1
- package/build/assets/{declaration-DaUdB2Wg.js → declaration-IA661TBv.js} +1 -1
- package/build/assets/{device-verify-DiEJqpJb.js → device-verify-BOQz2LJQ.js} +1 -1
- package/build/assets/{dist-xtCm0O6P.js → dist-B-SKiGDl.js} +1 -1
- package/build/assets/dist-CeJSjcAI.js +1 -0
- package/build/assets/{dropdown-classes-Vqz86I0R.js → dropdown-classes-lT1LUsbO.js} +1 -1
- package/build/assets/edit-automation-modal-C-oC5tis.js +1 -0
- package/build/assets/ellipsis-button-CiLx8dqc.js +1 -0
- package/build/assets/{entry.client-BvKgdCQ9.js → entry.client-Db3BpFL_.js} +2 -2
- package/build/assets/{enum-filter-dropdown-DdFgk0EM.js → enum-filter-dropdown-DFwoVtOw.js} +1 -1
- package/build/assets/{environment-switch-overlay-CuBuZOaa.js → environment-switch-overlay-Cow6h62p.js} +1 -1
- package/build/assets/{extensions-hub-Dayqvv0n.js → extensions-hub-BxZbAtjP.js} +1 -1
- package/build/assets/extensions-navigation-BIb-vAa2.js +1 -0
- package/build/assets/{file-DwHCkWZT.js → file-BJCSojij.js} +1 -1
- package/build/assets/files-tab-DpulQlIo.js +1 -0
- package/build/assets/files-tab-store-CZAEwv3x.js +1 -0
- package/build/assets/{folder-2h1hR1Qb.js → folder-wShAU0y7.js} +1 -1
- package/build/assets/git-control-bar-branch-button-D1uam-nI.js +27 -0
- package/build/assets/{globe-qFjFNG6J.js → globe-CQ_5xwpo.js} +1 -1
- package/build/assets/home-sJAFzI6v.js +1 -0
- package/build/assets/{i18n-zDndR1Ne.js → i18n-CyvU1o95.js} +1 -1
- package/build/assets/install-server-modal-BjEzlLF5.js +1 -0
- package/build/assets/{launch-I00QV8YT.js → launch-DgtB1JTX.js} +1 -1
- package/build/assets/{lesson-plan-C18uB_56.js → lesson-plan-B607buca.js} +1 -1
- package/build/assets/{link-external-DtcdPFbw.js → link-external-DCsHkAj4.js} +1 -1
- package/build/assets/llm-client-BnqeDPua.js +1 -0
- package/build/assets/llm-settings-B8kPJ0Of.js +1 -0
- package/build/assets/llm-settings-CjUpPsvA.js +1 -0
- package/build/assets/{loading-spinner-DNwR4--Z.js → loading-spinner-CFuA0UNt.js} +1 -1
- package/build/assets/manage-backends-modal-CTA-2x4F.js +1 -0
- package/build/assets/manifest-d3e8504d.js +1 -0
- package/build/assets/{markdown-renderer-D6B-u2nM.js → markdown-renderer-oOUs3tw5.js} +1 -1
- package/build/assets/mcp-client-xEdbDxOL.js +1 -0
- package/build/assets/mcp-kJYdM8aJ.js +9 -0
- package/build/assets/messages-BFJXB6MW.js +36 -0
- package/build/assets/{modal-backdrop-BDqI1zBV.js → modal-backdrop-B1si6TUd.js} +1 -1
- package/build/assets/{modal-body-CCLCqX1J.js → modal-body-2Po2nl1e.js} +1 -1
- package/build/assets/{modal-classes-DwTdT3IK.js → modal-classes-9XTtWCtF.js} +1 -1
- package/build/assets/{modal-close-button-gQgKqUf-.js → modal-close-button-DY-rVmld.js} +1 -1
- package/build/assets/model-selector-z_QOzaRV.js +1 -0
- package/build/assets/{navigation-context-aNGUUtdq.js → navigation-context-CszaA-CJ.js} +1 -1
- package/build/assets/{navigation-link-CYkF2y3K.js → navigation-link-DXg4oo29.js} +1 -1
- package/build/assets/onboarding-CZ_7nd-M.js +1 -0
- package/build/assets/{openhands-logo-CHmtDV-t.js → openhands-logo-B-IDE1ER.js} +1 -1
- package/build/assets/{option-service.api-CGNANEcT.js → option-service.api-DHOGfYKh.js} +1 -1
- package/build/assets/organization-service.api-D79cdERN.js +1 -0
- package/build/assets/path-utils-0KWEiRs9.js +1 -0
- package/build/assets/{pencil-Dr0b2jL1.js → pencil-COslZGUC.js} +1 -1
- package/build/assets/{plan-components--aLlpASH.js → plan-components-DsiDjcDi.js} +1 -1
- package/build/assets/{planner-tab-B-5EeCEm.js → planner-tab-CNmJiZ22.js} +1 -1
- package/build/assets/plus-D8aJZRkD.js +1 -0
- package/build/assets/profiles-client-CesbAK-7.js +1 -0
- package/build/assets/{providers-DknP6O2g.js → providers-Bpq3xFRA.js} +1 -1
- package/build/assets/proxy-wIasY2Po.js +1 -0
- package/build/assets/{query-client-config-CWWGQWvw.js → query-client-config-CITeuHD7.js} +1 -1
- package/build/assets/{recommended-automations-launcher-BIul0osB.js → recommended-automations-launcher-B1kfmFTQ.js} +8 -10
- package/build/assets/root-CIZ7Rv1X.js +2 -0
- package/build/assets/root-layout-B5ijp9At.js +2 -0
- package/build/assets/{sdk-section-page-VmtJWH3A.js → sdk-section-page-DTyvCc52.js} +1 -1
- package/build/assets/{sdk-settings-schema-DFievvEK.js → sdk-settings-schema-B0KrqqPX.js} +1 -1
- package/build/assets/{search-BeVRXvX7.js → search-BqXT2Ied.js} +1 -1
- package/build/assets/secrets-service-rJqZtDmI.js +1 -0
- package/build/assets/{secrets-settings-BLMvCkKm.js → secrets-settings-BQNSDLKS.js} +1 -1
- package/build/assets/server-client-Cz8PyIbN.js +1 -0
- package/build/assets/settings-BUlJrO_h.js +1 -0
- package/build/assets/settings-client-BUlG0Gzq.js +1 -0
- package/build/assets/{settings-dropdown-input-DA_pzHWE.js → settings-dropdown-input-BUk4m23x.js} +1 -1
- package/build/assets/{settings-gear-aNebYlCy.js → settings-gear-DFmoMp-6.js} +1 -1
- package/build/assets/{settings-index-CycvkOoq.js → settings-index-DE91Eahc.js} +1 -1
- package/build/assets/{settings-input-8y5p4kze.js → settings-input-DsoZj8Dm.js} +1 -1
- package/build/assets/{settings-list-classes-Qk7zl0Wu.js → settings-list-classes-D8VAVZmi.js} +1 -1
- package/build/assets/{settings-modal-DdntdOGP.js → settings-modal-CCaPYVqW.js} +1 -1
- package/build/assets/{settings-section-header-context-B77tsYlx.js → settings-section-header-context-D61smD-W.js} +1 -1
- package/build/assets/settings-service.api-4u2RKC8k.js +1 -0
- package/build/assets/{settings-switch-ba4DuiNO.js → settings-switch-BQiAS9ga.js} +1 -1
- package/build/assets/{settings-utils-BxzHqLmZ.js → settings-utils-chxTa1vn.js} +1 -1
- package/build/assets/shared-conversation-sBPLAawM.js +1 -0
- package/build/assets/{sidebar-mobile-menu-toggle-C0mmabKj.js → sidebar-mobile-menu-toggle-BDXWzhyB.js} +1 -1
- package/build/assets/{sidebar-nav-link-BsYdDFfb.js → sidebar-nav-link-Bhlzlhp8.js} +1 -1
- package/build/assets/{skill-card-pill-row-BhUlGcYB.js → skill-card-pill-row-Bg5joQf6.js} +1 -1
- package/build/assets/{skills-TYjOUQ2d.js → skills-BDOWVle-.js} +1 -1
- package/build/assets/skills-client-Cr9F5306.js +1 -0
- package/build/assets/{skills-plugins-D0pdqgKa.js → skills-plugins-fihjo1KZ.js} +1 -1
- package/build/assets/{skills-settings-VqKTkmVl.js → skills-settings-NQnuMwZP.js} +1 -1
- package/build/assets/{styled-tooltip-TCp7svY3.js → styled-tooltip-GXy1qxsO.js} +1 -1
- package/build/assets/suspense-C9MBE_9N.js +1 -0
- package/build/assets/{switch-skeleton-cKrdaYGj.js → switch-skeleton-QpdcdxRP.js} +1 -1
- package/build/assets/{task-list-tab-BiizRsY3.js → task-list-tab-Hl9BuVfq.js} +1 -1
- package/build/assets/telemetry-j9SLrtY7.js +2 -0
- package/build/assets/{terminal-BSYITdM0.js → terminal-BUww3Ssl.js} +1 -1
- package/build/assets/{terminal-CpgZx6go.js → terminal-DRwe8--D.js} +1 -1
- package/build/assets/{toggle-switch-CUgOZqZu.js → toggle-switch-DDr-DnEc.js} +1 -1
- package/build/assets/{trash-2-CbVljPko.js → trash-2-DAKXV2Wm.js} +1 -1
- package/build/assets/{typography-Bvw0IxaN.js → typography-Cx7uw7z3.js} +1 -1
- package/build/assets/{u-check-circle-u9QiS4Fr.js → u-check-circle-CftRwky1.js} +1 -1
- package/build/assets/{u-check-circle-half-DjpjzWu3.js → u-check-circle-half-sGVk0BtL.js} +1 -1
- package/build/assets/{u-circuit-DlBlOwx9.js → u-circuit-Cg9tH5qb.js} +1 -1
- package/build/assets/{u-edit-BIYzjs3v.js → u-edit-foF02hwH.js} +1 -1
- package/build/assets/{use-active-conversation-q1wT8LI5.js → use-active-conversation-DJGoI9Mc.js} +1 -1
- package/build/assets/use-agent-settings-schema-DtusObuC.js +1 -0
- package/build/assets/{use-agent-state-CCHlVqvY.js → use-agent-state-BXgWhFh6.js} +1 -1
- package/build/assets/{use-cloud-current-user-id-BAKf91Zx.js → use-cloud-current-user-id-e1Pk7NxQ.js} +1 -1
- package/build/assets/use-config-DSzkljTq.js +1 -0
- package/build/assets/use-create-conversation-CzvaVA5A.js +1 -0
- package/build/assets/use-event-store-DSpvASbC.js +1 -0
- package/build/assets/use-get-secrets-DiLgP147.js +1 -0
- package/build/assets/use-handle-plan-click-CUZwmKIk.js +1 -0
- package/build/assets/use-is-authed-fNsj-Adj.js +1 -0
- package/build/assets/{use-launch-skill-in-chat-3ydwpi_M.js → use-launch-skill-in-chat-ClRJlIx7.js} +1 -1
- package/build/assets/use-llm-profiles-CyNVoVqm.js +1 -0
- package/build/assets/use-runtime-is-ready-CWkGQFsb.js +1 -0
- package/build/assets/{use-save-settings-rE9aA29R.js → use-save-settings-CP23emUY.js} +1 -1
- package/build/assets/use-settings-0qFqC-r6.js +1 -0
- package/build/assets/{use-settings-nav-items-C7MOWj09.js → use-settings-nav-items-C6YxUn4h.js} +1 -1
- package/build/assets/use-skills-BWHATXK-.js +1 -0
- package/build/assets/{use-task-list-YMkSzdDv.js → use-task-list-JPudBRv3.js} +1 -1
- package/build/assets/{use-tracking-DQYdZpxi.js → use-tracking-CjLZgh8X.js} +1 -1
- package/build/assets/use-unified-vscode-url-DSn2tT08.js +1 -0
- package/build/assets/use-user-conversation-CQ5IwnZk.js +1 -0
- package/build/assets/{useMutation-DDo48A8t.js → useMutation-7hG0GuPx.js} +1 -1
- package/build/assets/useQuery-JDs8UaWj.js +1 -0
- package/build/assets/{useTranslation-CEcjrme-.js → useTranslation-CbJtty1g.js} +1 -1
- package/build/assets/{utils-CdgBzLA7.js → utils-CVcuFUYj.js} +1 -1
- package/build/assets/{vendor~browser-DWk6fNtJ.js → vendor~browser-Dwwc84Zf.js} +1 -1
- package/build/assets/{vendor~browser-tab-NZdVoI2Z.js → vendor~browser-tab-Bhohe29F.js} +1 -1
- package/build/assets/{vendor~conversation-panel~conversation-gp03cWZW.js → vendor~conversation-panel~conversation-DYHL7QoY.js} +1 -1
- package/build/assets/{vendor~conversation-panel~conversation~index-BZ5C6Xpz.js → vendor~conversation-panel~conversation~index-Be58Romv.js} +1 -1
- package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~oli4dvxu-BodGsxSf.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~oli4dvxu-CBEOG8NF.js} +1 -1
- package/build/assets/{vendor~files-tab-Buz36Y-q.js → vendor~files-tab-B447_Zns.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation-DG0H5SkJ.js → vendor~home~conversation-panel~conversation-DZ-F7J6T.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CXivI4Ym.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-B4oeCCli.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-9Il_wz8U.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CQQAW8hY.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-1pTajrpX.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DFsI4CLy.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Ceeqkj0k.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DcvlNgbn.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-B7I1ZxCx.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-ojk_J4bB.js} +1 -1
- package/build/assets/{vendor~launch-DXL78kBf.js → vendor~launch-BdXJCmwc.js} +1 -1
- package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-CfAc3nMS.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-DToubnIU.js} +1 -1
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation-DkwcKRtv.js → vendor~root-layout~home~conversation-panel~conversation-Cg0nXqVs.js} +1 -1
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-DSqEbr0N.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-Dr8Ly0at.js} +1 -1
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-BC9XTECT.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-DveauQfg.js} +1 -1
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~k776hupu-D0XUSHNN.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~jpfhx3ls-zdl_Rltz.js} +2 -2
- package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~jxrvuot9-BaCzvZac.js +48 -0
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-CcFtthyg.js → vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-BwbkftxT.js} +1 -1
- package/build/assets/{vendor~root-layout~home~mcp~automations-list-cNHi83v_.js → vendor~root-layout~home~mcp~automations-list-CtdIEhjQ.js} +1 -1
- package/build/assets/{vendor~root-layout~home~mcp~automations-list-BnIlGhjl.js → vendor~root-layout~home~mcp~automations-list-DGOI7kQz.js} +1 -1
- package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BGWUbqUq.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-CbuXadI-.js} +1 -1
- package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BuCSnjsW.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-iPAfRWNQ.js} +2 -2
- package/build/assets/{verification-settings-CIqtxWat.js → verification-settings-Cm02KmeI.js} +1 -1
- package/build/assets/{vscode-tab-DEt72yJX.js → vscode-tab-AF70Yke0.js} +1 -1
- package/build/assets/{waiting-for-runtime-message-DSjJfeoj.js → waiting-for-runtime-message-Bg27u9JB.js} +1 -1
- package/build/assets/x-8AbJWTbX.js +1 -0
- package/build/assets/{x-mark-DsJ9tDD0.js → x-mark-Cn-YJVbN.js} +1 -1
- package/build/index.html +4 -4
- package/build/locales/ar/openhands.json +2 -1
- package/build/locales/ca/openhands.json +2 -1
- package/build/locales/de/openhands.json +2 -1
- package/build/locales/en/openhands.json +2 -1
- package/build/locales/es/openhands.json +2 -1
- package/build/locales/fr/openhands.json +2 -1
- package/build/locales/it/openhands.json +2 -1
- package/build/locales/ja/openhands.json +2 -1
- package/build/locales/ko-KR/openhands.json +2 -1
- package/build/locales/no/openhands.json +2 -1
- package/build/locales/pt/openhands.json +2 -1
- package/build/locales/tr/openhands.json +2 -1
- package/build/locales/uk/openhands.json +2 -1
- package/build/locales/zh-CN/openhands.json +2 -1
- package/build/locales/zh-TW/openhands.json +2 -1
- package/config/defaults.json +1 -1
- package/dist/api/agent-server-adapter.cjs +3 -3
- package/dist/api/agent-server-adapter.cjs.map +1 -1
- package/dist/api/agent-server-adapter.js +94 -86
- package/dist/api/agent-server-adapter.js.map +1 -1
- package/dist/api/app-preferences-store.cjs.map +1 -1
- package/dist/api/app-preferences-store.d.ts +6 -2
- package/dist/api/app-preferences-store.js.map +1 -1
- package/dist/api/cloud/proxy.cjs +1 -1
- package/dist/api/cloud/proxy.cjs.map +1 -1
- package/dist/api/cloud/proxy.d.ts +18 -6
- package/dist/api/cloud/proxy.js +1 -1
- package/dist/api/cloud/proxy.js.map +1 -1
- package/dist/api/conversation-metadata-store.cjs.map +1 -1
- package/dist/api/conversation-metadata-store.d.ts +8 -0
- package/dist/api/conversation-metadata-store.js.map +1 -1
- package/dist/api/conversation-service/agent-server-conversation-service.types.d.ts +9 -0
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js +9 -9
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js.map +1 -1
- package/dist/components/conversation-events/chat/event-message.cjs +1 -1
- package/dist/components/conversation-events/chat/event-message.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-message.js +80 -71
- package/dist/components/conversation-events/chat/event-message.js.map +1 -1
- package/dist/components/features/automations/recommended-automations-launcher.d.ts +7 -1
- package/dist/components/features/chat/switch-profile-button.cjs +1 -1
- package/dist/components/features/chat/switch-profile-button.cjs.map +1 -1
- package/dist/components/features/chat/switch-profile-button.js +5 -5
- package/dist/components/features/chat/switch-profile-button.js.map +1 -1
- package/dist/components/features/conversation-panel/skills-modal.cjs +1 -1
- package/dist/components/features/conversation-panel/skills-modal.cjs.map +1 -1
- package/dist/components/features/conversation-panel/skills-modal.js +34 -37
- package/dist/components/features/conversation-panel/skills-modal.js.map +1 -1
- package/dist/components/features/home/workspace-selection-form.d.ts +1 -0
- package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +8 -0
- package/dist/components/features/settings/llm-profiles/profile-actions-menu.cjs +1 -1
- package/dist/components/features/settings/llm-profiles/profile-actions-menu.js +1 -0
- package/dist/components/features/sidebar/sidebar.cjs +1 -1
- package/dist/components/features/sidebar/sidebar.js +6 -6
- package/dist/components/features/skills/extensions-navigation.cjs +1 -1
- package/dist/components/features/skills/extensions-navigation.cjs.map +1 -1
- package/dist/components/features/skills/extensions-navigation.d.ts +1 -9
- package/dist/components/features/skills/extensions-navigation.js +31 -50
- package/dist/components/features/skills/extensions-navigation.js.map +1 -1
- package/dist/constants/acp-providers.cjs +1 -1
- package/dist/constants/acp-providers.js +1 -1
- package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
- package/dist/hooks/mutation/use-create-conversation.cjs.map +1 -1
- package/dist/hooks/mutation/use-create-conversation.js +26 -14
- package/dist/hooks/mutation/use-create-conversation.js.map +1 -1
- package/dist/hooks/mutation/use-switch-llm-profile-and-log.cjs +1 -1
- package/dist/hooks/mutation/use-switch-llm-profile-and-log.cjs.map +1 -1
- package/dist/hooks/mutation/use-switch-llm-profile-and-log.js +26 -15
- package/dist/hooks/mutation/use-switch-llm-profile-and-log.js.map +1 -1
- package/dist/i18n/declaration.cjs +1 -1
- package/dist/i18n/declaration.cjs.map +1 -1
- package/dist/i18n/declaration.d.ts +1 -0
- package/dist/i18n/declaration.js +1 -1
- package/dist/i18n/declaration.js.map +1 -1
- package/dist/i18n/translation.cjs +1 -1
- package/dist/i18n/translation.cjs.map +1 -1
- package/dist/i18n/translation.js +32 -15
- package/dist/i18n/translation.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +10 -10
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/index.js +1 -1
- package/dist/locales/ar/openhands.json +2 -1
- package/dist/locales/ca/openhands.json +2 -1
- package/dist/locales/de/openhands.json +2 -1
- package/dist/locales/en/openhands.json +2 -1
- package/dist/locales/es/openhands.json +2 -1
- package/dist/locales/fr/openhands.json +2 -1
- package/dist/locales/it/openhands.json +2 -1
- package/dist/locales/ja/openhands.json +2 -1
- package/dist/locales/ko-KR/openhands.json +2 -1
- package/dist/locales/no/openhands.json +2 -1
- package/dist/locales/pt/openhands.json +2 -1
- package/dist/locales/tr/openhands.json +2 -1
- package/dist/locales/uk/openhands.json +2 -1
- package/dist/locales/zh-CN/openhands.json +2 -1
- package/dist/locales/zh-TW/openhands.json +2 -1
- package/dist/node_modules/@openhands/typescript-client/dist/models/acp.cjs +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/models/acp.cjs.map +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/models/acp.js +10 -1
- package/dist/node_modules/@openhands/typescript-client/dist/models/acp.js.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.cjs.map +1 -1
- package/dist/package.js +2 -2
- package/dist/package.js.map +1 -1
- package/dist/routes/llm-settings.cjs +1 -1
- package/dist/routes/llm-settings.cjs.map +1 -1
- package/dist/routes/llm-settings.js +21 -21
- package/dist/routes/llm-settings.js.map +1 -1
- package/dist/routes/mcp.cjs +1 -1
- package/dist/routes/mcp.cjs.map +1 -1
- package/dist/routes/mcp.d.ts +0 -1
- package/dist/routes/mcp.js +0 -1
- package/dist/routes/mcp.js.map +1 -1
- package/dist/types/agent-server/core/events/index.d.ts +1 -0
- package/dist/types/agent-server/core/events/streaming-delta-event.d.ts +7 -0
- package/dist/types/agent-server/core/openhands-event.d.ts +2 -2
- package/dist/types/agent-server/type-guards.cjs +1 -1
- package/dist/types/agent-server/type-guards.cjs.map +1 -1
- package/dist/types/agent-server/type-guards.d.ts +2 -0
- package/dist/types/agent-server/type-guards.js +3 -3
- package/dist/types/agent-server/type-guards.js.map +1 -1
- package/dist/utils/handle-event-for-ui.cjs +1 -1
- package/dist/utils/handle-event-for-ui.cjs.map +1 -1
- package/dist/utils/handle-event-for-ui.js +69 -13
- package/dist/utils/handle-event-for-ui.js.map +1 -1
- package/dist/utils/mcp-config.cjs +1 -1
- package/dist/utils/mcp-config.cjs.map +1 -1
- package/dist/utils/mcp-config.js +27 -19
- package/dist/utils/mcp-config.js.map +1 -1
- package/dist/utils/mcp-marketplace-utils.cjs +1 -1
- package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
- package/dist/utils/mcp-marketplace-utils.js +38 -18
- package/dist/utils/mcp-marketplace-utils.js.map +1 -1
- package/dist/utils/normalize-display-model.cjs +1 -1
- package/dist/utils/normalize-display-model.cjs.map +1 -1
- package/dist/utils/normalize-display-model.js +8 -7
- package/dist/utils/normalize-display-model.js.map +1 -1
- package/dist/utils/openhands-llm.cjs +1 -1
- package/dist/utils/openhands-llm.cjs.map +1 -1
- package/dist/utils/openhands-llm.d.ts +13 -0
- package/dist/utils/openhands-llm.js +9 -3
- package/dist/utils/openhands-llm.js.map +1 -1
- package/package.json +2 -2
- package/scripts/check-sdk-version-sync.mjs +6 -6
- package/scripts/dev-safe.mjs +71 -120
- package/scripts/dev-with-automation.mjs +58 -4
- package/scripts/runtime-services-info.mjs +199 -0
- package/scripts/static-server.mjs +42 -4
- package/build/assets/acp-providers-DJr8DlNG.js +0 -1
- package/build/assets/acp-route-guard-A__sWgbc.js +0 -1
- package/build/assets/active-backend-context-I2w666XY.js +0 -1
- package/build/assets/add-backend-modal-BDBDBXsJ.js +0 -1
- package/build/assets/agent-server-client-options-9agOSarV.js +0 -1
- package/build/assets/agent-server-compatibility-B7QStIcH.js +0 -1
- package/build/assets/agent-server-conversation-service.api-Cagoqq1V.js +0 -5
- package/build/assets/api-key-entry-screen-ByXA4hXH.js +0 -1
- package/build/assets/automation-detail-Dbmgt974.js +0 -1
- package/build/assets/backend-form-modal-CeB983Sj.js +0 -1
- package/build/assets/browser-D08Sp3ZY.js +0 -5
- package/build/assets/browser-store-JRrcGdlk.js +0 -1
- package/build/assets/chat-send-button-5qz0zj6R.js +0 -1
- package/build/assets/check-CZhEL6rP.js +0 -1
- package/build/assets/conversation-BrjF2-Ky.js +0 -1
- package/build/assets/conversation-HgR_TTPE.js +0 -19
- package/build/assets/conversation-panel-BlRcO5AC.js +0 -1
- package/build/assets/conversation-service.api-B_Pdmwsa.js +0 -1
- package/build/assets/conversation-state-store-D-w0uurj.js +0 -1
- package/build/assets/conversation-store-CC-isCnP.js +0 -1
- package/build/assets/conversation-websocket-context-G95yfL81.js +0 -3
- package/build/assets/dist-Bl-1K5Tv.js +0 -1
- package/build/assets/edit-automation-modal-CNZgSSiH.js +0 -1
- package/build/assets/extensions-navigation-yFLAU06N.js +0 -1
- package/build/assets/files-tab-CMredyYX.js +0 -1
- package/build/assets/files-tab-store-DLU28g8C.js +0 -1
- package/build/assets/git-control-bar-branch-button-D8blTNXh.js +0 -27
- package/build/assets/home-CWw845Rz.js +0 -1
- package/build/assets/install-server-modal-D8Q0xZcN.js +0 -1
- package/build/assets/llm-client-BqyLKgUN.js +0 -1
- package/build/assets/llm-settings-CAnFYAEG.js +0 -1
- package/build/assets/llm-settings-DotqpmCF.js +0 -1
- package/build/assets/manage-backends-modal-Ceo_SOcf.js +0 -1
- package/build/assets/manifest-61ec2d68.js +0 -1
- package/build/assets/mcp-EvrLVTla.js +0 -9
- package/build/assets/messages-Bz9TWjlh.js +0 -36
- package/build/assets/middleware-CfatjPYZ.js +0 -6
- package/build/assets/model-selector-CZOi4V7r.js +0 -1
- package/build/assets/onboarding-CPCKYvFh.js +0 -1
- package/build/assets/organization-service.api-Dn74hBTH.js +0 -1
- package/build/assets/path-utils-BjxzIGLp.js +0 -1
- package/build/assets/plus-DT-M0FA1.js +0 -1
- package/build/assets/profiles-client-BrqNmaDV.js +0 -1
- package/build/assets/proxy-sRh0WKI7.js +0 -1
- package/build/assets/root-CklXEh3W.js +0 -2
- package/build/assets/root-layout-BCA_X8XL.js +0 -2
- package/build/assets/secrets-service-DVtlLWY8.js +0 -1
- package/build/assets/server-client-DYv_GHPl.js +0 -1
- package/build/assets/settings-CXvJUx_j.js +0 -1
- package/build/assets/settings-service.api-DxIEtvx6.js +0 -1
- package/build/assets/shared-conversation-x41nZQi7.js +0 -1
- package/build/assets/sidebar-store-DnQAJAE5.js +0 -1
- package/build/assets/telemetry-fQFd-8V3.js +0 -2
- package/build/assets/use-agent-settings-schema-Yxf7KGyG.js +0 -1
- package/build/assets/use-config-DwfigQ_Y.js +0 -1
- package/build/assets/use-create-conversation-BEzddjXn.js +0 -1
- package/build/assets/use-event-store-Cxqc45Sw.js +0 -1
- package/build/assets/use-get-secrets-BlO1BfUo.js +0 -1
- package/build/assets/use-handle-plan-click-Bfl0zIBr.js +0 -1
- package/build/assets/use-is-authed-hHndEep7.js +0 -1
- package/build/assets/use-llm-profiles-E-jjZMZw.js +0 -1
- package/build/assets/use-runtime-is-ready-DBWzvAmc.js +0 -1
- package/build/assets/use-settings-BPTbE7lg.js +0 -1
- package/build/assets/use-skills-QhoaIYGF.js +0 -1
- package/build/assets/use-unified-vscode-url-DFtNIC1Q.js +0 -1
- package/build/assets/use-user-conversation-BRAseenw.js +0 -1
- package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~dp08i1qy-D8soyAAx.js +0 -48
- package/build/assets/vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~jaomi49z-Cw89stA6.js +0 -1
- package/build/assets/x-JOBEVLW0.js +0 -1
- package/dist/components/features/conversation-panel/skills-runtime-waiting-state.cjs +0 -2
- package/dist/components/features/conversation-panel/skills-runtime-waiting-state.cjs.map +0 -1
- package/dist/components/features/conversation-panel/skills-runtime-waiting-state.d.ts +0 -1
- package/dist/components/features/conversation-panel/skills-runtime-waiting-state.js +0 -21
- package/dist/components/features/conversation-panel/skills-runtime-waiting-state.js.map +0 -1
- package/dist/utils/acp-route-guard.cjs +0 -1
- package/dist/utils/acp-route-guard.d.ts +0 -26
- package/dist/utils/acp-route-guard.js +0 -4
- /package/build/assets/{agent-server-ui-style-scope-BwIZYdC1.js → agent-server-ui-style-scope-Bhc5vpWO.js} +0 -0
- /package/build/assets/{automation-DJ_3GeXD.js → automation-BzmydDjr.js} +0 -0
- /package/build/assets/{common-DqjLSBOt.js → common-Cfviy7yT.js} +0 -0
- /package/build/assets/{context-CEQZwATj.js → context-BBqptpXX.js} +0 -0
- /package/build/assets/{conversation-local-storage-YmOVXxxW.js → conversation-local-storage-D5Tre_GA.js} +0 -0
- /package/build/assets/{createLucideIcon-Ddu8jDOQ.js → createLucideIcon-C9OEnwvX.js} +0 -0
- /package/build/assets/{environment-switch-store-CiurvTtK.js → environment-switch-store-BpKxp6Xq.js} +0 -0
- /package/build/assets/{git-status-mapper-DnL9OC8_.js → git-status-mapper-C3rfzUex.js} +0 -0
- /package/build/assets/{handle-capture-consent-3XrjZ8wi.js → handle-capture-consent-DDnXMC9Y.js} +0 -0
- /package/build/assets/{health-store-B5f0S2FY.js → health-store-d-d2ssv4.js} +0 -0
- /package/build/assets/{iconBase-BVhFI-0E.js → iconBase-1A_WRQ4E.js} +0 -0
- /package/build/assets/{map-provider-C3Z5Dx2J.js → map-provider-XSFHmdDs.js} +0 -0
- /package/build/assets/{objectWithoutPropertiesLoose-DSQKyRhw.js → objectWithoutPropertiesLoose-B-IA9dU9.js} +0 -0
- /package/build/assets/{query-keys-tAsQcc_9.js → query-keys-CQaji0wJ.js} +0 -0
- /package/build/assets/{react-Dy05vyj5.js → react-CuAHzoGi.js} +0 -0
- /package/build/assets/{retrieve-axios-error-message-BY-yIkIq.js → retrieve-axios-error-message-DbnSBc_1.js} +0 -0
- /package/build/assets/{sdk-settings-field-metadata-C6KMD-jZ.js → sdk-settings-field-metadata-CETNItbo.js} +0 -0
- /package/build/assets/{settings-DGY6n4J2.js → settings-BgL2HUsU.js} +0 -0
- /package/build/assets/{settings-like-page-layout-classes-DNg2vKSM.js → settings-like-page-layout-classes-DENKlZpD.js} +0 -0
- /package/build/assets/{use-breakpoint-DpxHDmuH.js → use-breakpoint-Dt2knceC.js} +0 -0
- /package/build/assets/{use-click-outside-element-DhxCUyWl.js → use-click-outside-element-Bu2A3s59.js} +0 -0
- /package/build/assets/{v4-khGvL7i2.js → v4-BygpdDmc.js} +0 -0
- /package/build/assets/{vendor~browser-BDNLFng6.js → vendor~browser-BYEwwJqV.js} +0 -0
- /package/build/assets/{vendor~conversation-panel~conversation~alert-banner-D_hRW_zc.js → vendor~conversation-panel~conversation~alert-banner-BnHToS5O.js} +0 -0
- /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~j8sdb9mk-OFpe9fX_.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~j8sdb9mk-oCzr0-9g.js} +0 -0
- /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~lpdshwee-BPuuVEqr.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~lpdshwee-CTmh4bwd.js} +0 -0
- /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~skills-settings~m~o9nrx3fm-D44TR8hL.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~skills-settings~m~o9nrx3fm-87v-LliW.js} +0 -0
- /package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dr3Ow7Ms.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-D8cqyq4k.js} +0 -0
- /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~i4kjfqhl-B1TKKuuH.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ntycl9e1-DspdqGPW.js} +0 -0
- /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~e9ykmtgh-Fa-nXZ4M.js → vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~e9ykmtgh-DMH1jSwL.js} +0 -0
- /package/build/assets/{vendor~terminal-0ObOedYm.js → vendor~terminal-aeP3PnKf.js} +0 -0
- /package/build/assets/{vscode-url-helper-BMq8JBhB.js → vscode-url-helper-DOCkV_-G.js} +0 -0
package/dist/package.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@openhands/agent-canvas\",\n \"version\": \"1.0.0-rc.
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@openhands/agent-canvas\",\n \"version\": \"1.0.0-rc.3\",\n \"description\": \"Agent Canvas UI for OpenHands - run AI coding agents with a visual interface\",\n \"license\": \"MIT\",\n \"private\": false,\n \"type\": \"module\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/OpenHands/agent-canvas\"\n },\n \"homepage\": \"https://github.com/OpenHands/agent-canvas#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/OpenHands/agent-canvas/issues\"\n },\n \"bin\": {\n \"agent-canvas\": \"bin/agent-canvas.mjs\"\n },\n \"engines\": {\n \"node\": \">=22.12.0\"\n },\n \"dependencies\": {\n \"@heroui/react\": \"2.8.10\",\n \"@microlink/react-json-view\": \"1.31.20\",\n \"@monaco-editor/react\": \"4.7.0\",\n \"@openhands/extensions\": \"github:OpenHands/extensions#8a6690071e0e9229ae70b30ff0352aff9e6fca21\",\n \"@openhands/typescript-client\": \"1.24.3\",\n \"@react-router/node\": \"7.14.2\",\n \"@react-router/serve\": \"7.14.2\",\n \"@tailwindcss/vite\": \"4.2.4\",\n \"@tanstack/react-query\": \"5.100.9\",\n \"@types/shell-quote\": \"^1.7.5\",\n \"@uidotdev/usehooks\": \"2.4.1\",\n \"@xterm/addon-fit\": \"0.11.0\",\n \"@xterm/xterm\": \"6.0.0\",\n \"axios\": \"1.16.0\",\n \"class-variance-authority\": \"0.7.1\",\n \"clsx\": \"2.1.1\",\n \"downshift\": \"9.3.2\",\n \"framer-motion\": \"12.38.0\",\n \"i18next\": \"26.0.8\",\n \"i18next-browser-languagedetector\": \"8.2.1\",\n \"i18next-http-backend\": \"4.0.0\",\n \"isbot\": \"5.1.39\",\n \"lucide-react\": \"1.14.0\",\n \"monaco-editor\": \"0.55.1\",\n \"posthog-js\": \"1.372.6\",\n \"react\": \"19.2.5\",\n \"react-dom\": \"19.2.5\",\n \"react-hot-toast\": \"2.6.0\",\n \"react-i18next\": \"17.0.6\",\n \"react-icons\": \"5.6.0\",\n \"react-markdown\": \"10.1.0\",\n \"react-router\": \"7.14.2\",\n \"react-syntax-highlighter\": \"16.1.1\",\n \"rehype-raw\": \"7.0.0\",\n \"rehype-sanitize\": \"6.0.0\",\n \"remark-breaks\": \"4.0.0\",\n \"remark-gfm\": \"4.0.1\",\n \"shell-quote\": \"^1.8.3\",\n \"sirv-cli\": \"3.0.1\",\n \"socket.io-client\": \"4.8.3\",\n \"tailwind-merge\": \"3.5.0\",\n \"tailwind-scrollbar\": \"4.0.2\",\n \"unist-util-visit\": \"5.1.0\",\n \"uuid\": \"14.0.0\",\n \"vite\": \"8.0.10\",\n \"zustand\": \"5.0.12\"\n },\n \"scripts\": {\n \"dev\": \"node --env-file-if-exists=.env scripts/dev-with-automation.mjs\",\n \"dev:static\": \"node --env-file-if-exists=.env scripts/dev-static.mjs\",\n \"dev:extra-backend\": \"node --env-file-if-exists=.env scripts/dev-extra-backend.mjs\",\n \"dev:minimal\": \"node --env-file-if-exists=.env scripts/dev-safe.mjs\",\n \"dev:frontend\": \"npm run make-i18n && cross-env VITE_MOCK_API=false react-router dev\",\n \"dev:mock\": \"npm run make-i18n && cross-env VITE_MOCK_API=true react-router dev\",\n \"build\": \"npm run build:app\",\n \"build:mock\": \"npm run make-i18n && cross-env VITE_MOCK_API=true react-router build\",\n \"start\": \"npx sirv-cli build/ --single\",\n \"test\": \"npm run make-i18n && vitest run\",\n \"test:e2e\": \"playwright test --pass-with-no-tests\",\n \"test:e2e:live\": \"node --env-file-if-exists=.env tests/e2e/live/scripts/run-live-e2e.mjs\",\n \"test:e2e:mock-llm\": \"playwright test --config=playwright.mock-llm.config.ts\",\n \"test:e2e:mock-llm:docker\": \"playwright test --config=playwright.mock-llm-docker.config.ts\",\n \"test:e2e:snapshots\": \"playwright test tests/e2e/snapshots --project=chromium --retries=0\",\n \"test:e2e:snapshots:update\": \"playwright test tests/e2e/snapshots --project=chromium --update-snapshots\",\n \"test:coverage\": \"npm run make-i18n && vitest run --coverage\",\n \"dev_wsl\": \"VITE_WATCH_USE_POLLING=true vite\",\n \"preview\": \"vite preview\",\n \"make-i18n\": \"node scripts/make-i18n-translations.cjs\",\n \"prelint\": \"npm run make-i18n\",\n \"lint\": \"npm run typecheck && eslint src && prettier --check src/**/*.{ts,tsx}\",\n \"lint:fix\": \"eslint src --fix && prettier --write src/**/*.{ts,tsx}\",\n \"prepare\": \"husky\",\n \"typecheck\": \"react-router typegen && tsc\",\n \"typecheck:staged\": \"react-router typegen && npx tsc --noEmit --skipLibCheck\",\n \"check-translation-completeness\": \"node scripts/check-translation-completeness.cjs\",\n \"build:app\": \"npm run make-i18n && react-router build\",\n \"build:lib\": \"npm run make-i18n && react-router typegen && cross-env BUILD_LIB=true VITE_APP_ENV=production vite build && tsc -p tsconfig.lib.json\",\n \"build:docker\": \"node scripts/docker-build.mjs\"\n },\n \"lint-staged\": {\n \"src/**/*.{ts,tsx,js}\": [\n \"eslint --fix\",\n \"prettier --write\"\n ],\n \"src/**/*.{ts,tsx}\": [\n \"bash -c 'npm run typecheck:staged'\"\n ],\n \"src/**/*\": [\n \"npm run check-translation-completeness\"\n ]\n },\n \"devDependencies\": {\n \"@eslint/eslintrc\": \"3.3.1\",\n \"@eslint/js\": \"9.39.4\",\n \"@mswjs/socket.io-binding\": \"0.2.0\",\n \"@playwright/test\": \"1.59.1\",\n \"@react-router/dev\": \"7.14.2\",\n \"@tailwindcss/typography\": \"0.5.19\",\n \"@tanstack/eslint-plugin-query\": \"5.100.9\",\n \"@testing-library/dom\": \"10.4.1\",\n \"@testing-library/jest-dom\": \"6.9.1\",\n \"@testing-library/react\": \"16.3.2\",\n \"@testing-library/user-event\": \"14.6.1\",\n \"@types/mdast\": \"4.0.4\",\n \"@types/node\": \"25.6.0\",\n \"@types/react\": \"19.2.14\",\n \"@types/react-dom\": \"19.2.3\",\n \"@types/react-syntax-highlighter\": \"15.5.13\",\n \"@typescript-eslint/eslint-plugin\": \"8.59.2\",\n \"@typescript-eslint/parser\": \"8.59.2\",\n \"@vercel/react-router\": \"1.3.0\",\n \"@vitest/coverage-v8\": \"4.1.5\",\n \"cross-env\": \"10.1.0\",\n \"eslint\": \"9.39.4\",\n \"eslint-config-prettier\": \"10.1.8\",\n \"eslint-import-resolver-typescript\": \"4.4.4\",\n \"eslint-plugin-i18next\": \"6.1.4\",\n \"eslint-plugin-import-x\": \"4.16.2\",\n \"eslint-plugin-jsx-a11y\": \"6.10.2\",\n \"eslint-plugin-prettier\": \"5.5.5\",\n \"eslint-plugin-react\": \"7.37.5\",\n \"eslint-plugin-react-hooks\": \"7.1.1\",\n \"eslint-plugin-unused-imports\": \"4.4.1\",\n \"globals\": \"16.5.0\",\n \"husky\": \"9.1.7\",\n \"jsdom\": \"29.1.1\",\n \"lint-staged\": \"16.4.0\",\n \"msw\": \"2.14.2\",\n \"postcss-prefix-selector\": \"2.1.1\",\n \"prettier\": \"3.8.3\",\n \"tailwindcss\": \"4.2.4\",\n \"typescript\": \"6.0.3\",\n \"vite-plugin-svgr\": \"5.2.0\",\n \"vitest\": \"4.1.5\"\n },\n \"packageManager\": \"npm@10.5.0\",\n \"volta\": {\n \"node\": \"22.12.0\"\n },\n \"msw\": {\n \"workerDirectory\": [\n \"public\"\n ]\n },\n \"overrides\": {\n \"dompurify\": \"3.3.2\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"files\": [\n \"dist\",\n \"bin\",\n \"build\",\n \"config\",\n \"scripts\",\n \"tools\"\n ],\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"./browser\": {\n \"types\": \"./dist/components/browser/index.d.ts\",\n \"import\": \"./dist/components/browser/index.js\",\n \"require\": \"./dist/components/browser/index.cjs\"\n },\n \"./conversation\": {\n \"types\": \"./dist/components/conversation/index.d.ts\",\n \"import\": \"./dist/components/conversation/index.js\",\n \"require\": \"./dist/components/conversation/index.cjs\"\n },\n \"./files\": {\n \"types\": \"./dist/components/files/index.d.ts\",\n \"import\": \"./dist/components/files/index.js\",\n \"require\": \"./dist/components/files/index.cjs\"\n },\n \"./settings\": {\n \"types\": \"./dist/components/settings/index.d.ts\",\n \"import\": \"./dist/components/settings/index.js\",\n \"require\": \"./dist/components/settings/index.cjs\"\n },\n \"./sidebar\": {\n \"types\": \"./dist/components/sidebar/index.d.ts\",\n \"import\": \"./dist/components/sidebar/index.js\",\n \"require\": \"./dist/components/sidebar/index.cjs\"\n },\n \"./terminal\": {\n \"types\": \"./dist/components/terminal/index.d.ts\",\n \"import\": \"./dist/components/terminal/index.js\",\n \"require\": \"./dist/components/terminal/index.cjs\"\n },\n \"./i18n\": {\n \"types\": \"./dist/i18n/index.d.ts\",\n \"import\": \"./dist/i18n/index.js\",\n \"require\": \"./dist/i18n/index.cjs\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"peerDependencies\": {\n \"react\": \"19.2.5\",\n \"react-dom\": \"19.2.5\",\n \"react-router\": \"7.14.2\"\n }\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../i18n/declaration.cjs`);require(`../contexts/active-backend-context.cjs`);const r=require(`../services/settings.cjs`),i=require(`../hooks/query/use-settings.cjs`),a=require(`../components/features/settings/settings-input.cjs`),o=require(`../utils/extract-model-and-provider.cjs`),s=require(`../ui/help-link.cjs`),c=require(`../components/shared/modals/settings/model-selector.cjs`),
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../i18n/declaration.cjs`);require(`../contexts/active-backend-context.cjs`);const r=require(`../services/settings.cjs`),i=require(`../hooks/query/use-settings.cjs`),a=require(`../components/features/settings/settings-input.cjs`),o=require(`../utils/extract-model-and-provider.cjs`),s=require(`../ui/help-link.cjs`),c=require(`../utils/openhands-llm.cjs`),l=require(`../components/shared/modals/settings/model-selector.cjs`),u=require(`../hooks/query/use-agent-settings-schema.cjs`),d=require(`../components/features/settings/key-status-icon.cjs`),f=require(`../utils/sdk-settings-schema.cjs`),p=require(`../components/features/settings/sdk-settings/sdk-section-page.cjs`);require(`../components/features/settings/llm-profiles/index.cjs`);let m=require(`react`);m=e.__toESM(m,1);let h=require(`react/jsx-runtime`);var g=new Set([`llm.model`,`llm.api_key`,`llm.base_url`]),_=(e,t)=>!e||!t?null:`${e}/${t}`,v=(e,t)=>e?.sections.flatMap(e=>e.fields).find(e=>e.key===t)?.default??null,y={openai:new Set([`https://api.openai.com`,`https://api.openai.com/v1`]),openhands:new Set([c.OPENHANDS_LLM_PROXY_BASE_URL,`https://llm-proxy.app.all-hands.dev/v1`]),litellm_proxy:new Set([c.OPENHANDS_LLM_PROXY_BASE_URL,`https://llm-proxy.app.all-hands.dev/v1`])},b=e=>{try{let t=new URL(e),n=t.pathname.replace(/\/+$/,``)||``;return`${t.origin}${n}`}catch{return e.trim().replace(/\/+$/,``)}},x=(e,t)=>{let n=b(t),{provider:r}=o.extractModelAndProvider(e);if(r){let e=y[r];if(e)return e.has(n)}return Object.values(y).some(e=>e?.has(n))};function S({testId:e}){let{t:r}=t.useTranslation(`openhands`);return(0,h.jsx)(s.HelpLink,{testId:e,text:r(n.I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_TEXT),linkText:r(n.I18nKey.SETTINGS$NAV_API_KEYS),href:`https://app.all-hands.dev/settings/api-keys`,suffix:` ${r(n.I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_SUFFIX)}`})}function C({scope:e=`personal`,onSaveSuccess:o,initialValueOverrides:y,embedded:b,hideSaveButton:C,suppressSuccessToast:w,onSaveControlChange:T}){let{t:E}=t.useTranslation(`openhands`),{data:D}=i.useSettings(e),{data:O}=u.useAgentSettingsSchema(D?.agent_settings_schema),k=String(r.DEFAULT_SETTINGS.agent_settings?.llm?.model??``),A=m.default.useCallback((e,t)=>{let n=f.inferInitialView(e,t);if(n!==`basic`)return n;let r=e.llm_model??``,i=e.llm_base_url?.trim()??``;return i.length>0&&!x(r,i)?`all`:`basic`},[]);return(0,h.jsx)(p.SdkSectionPage,{scope:e,sectionKeys:[`llm`],excludeKeys:g,header:m.default.useCallback(({values:e,isDisabled:t,view:r,onChange:i})=>{let o=typeof e[`llm.model`]==`string`?e[`llm.model`]:``,c=typeof e[`llm.base_url`]==`string`?e[`llm.base_url`]:``,u=o.startsWith(`openhands/`),f=typeof e[`llm.api_key`]==`string`?e[`llm.api_key`]:``,p=b?f.length>0:!!D?.llm_api_key_set,m=(e,r)=>(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(a.SettingsInput,{testId:e,label:E(n.I18nKey.SETTINGS_FORM$API_KEY),type:`password`,className:`w-full`,value:f,placeholder:p?`<hidden>`:``,onChange:e=>i(`llm.api_key`,e),isDisabled:t,startContent:p?(0,h.jsx)(d.KeyStatusIcon,{isSet:p}):void 0}),(0,h.jsx)(s.HelpLink,{testId:r,text:E(n.I18nKey.SETTINGS$DONT_KNOW_API_KEY),linkText:E(n.I18nKey.SETTINGS$CLICK_FOR_INSTRUCTIONS),href:`https://docs.openhands.dev/usage/local-setup#getting-an-api-key`})]});return(0,h.jsx)(`div`,{className:`flex flex-col gap-6`,children:r===`basic`?(0,h.jsxs)(`div`,{className:`flex flex-col gap-6`,"data-testid":`llm-settings-form-basic`,children:[(0,h.jsx)(l.ModelSelector,{currentModel:o||void 0,currentBaseUrl:c||void 0,onChange:(e,t)=>{let n=_(e,t);n&&i(`llm.model`,n)},wrapperClassName:`!flex-col !gap-6`,isDisabled:t}),u?(0,h.jsx)(S,{testId:`openhands-api-key-help`}):null,m(`llm-api-key-input`,`llm-api-key-help-anchor`)]}):(0,h.jsxs)(`div`,{className:`flex flex-col gap-6`,"data-testid":`llm-settings-form-advanced`,children:[(0,h.jsx)(a.SettingsInput,{testId:`llm-custom-model-input`,label:E(n.I18nKey.SETTINGS$CUSTOM_MODEL),type:`text`,className:`w-full`,value:o,placeholder:k,onChange:e=>i(`llm.model`,e),isDisabled:t}),u?(0,h.jsx)(S,{testId:`openhands-api-key-help-2`}):null,(0,h.jsx)(a.SettingsInput,{testId:`base-url-input`,label:E(n.I18nKey.SETTINGS$BASE_URL),type:`text`,className:`w-full`,value:c,placeholder:`https://api.openai.com`,onChange:e=>i(`llm.base_url`,e),isDisabled:t}),m(`llm-api-key-input`,`llm-api-key-help-anchor-advanced`)]})})},[k,b,D?.llm_api_key_set,E]),buildPayload:m.default.useCallback((e,t)=>{let n=structuredClone(e),r=n.llm??{};return t.view===`basic`&&(r.base_url=c.isOpenHandsProxyModel(r.model??t.values[`llm.model`],r.base_url??t.values[`llm.base_url`])?c.OPENHANDS_LLM_PROXY_BASE_URL:v(O,`llm.base_url`),n.llm=r),{agent_settings_diff:n}},[O]),getInitialView:A,forceShowAdvancedView:!0,allowAllView:!0,onSaveSuccess:o,initialValueOverrides:y,embedded:b,hideSaveButton:C,suppressSuccessToast:w,onSaveControlChange:T,testId:`llm-settings-screen`})}exports.LlmSettingsScreen=C;
|
|
2
2
|
//# sourceMappingURL=llm-settings.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-settings.cjs","names":[],"sources":["../../src/routes/llm-settings.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ModelSelector } from \"#/components/shared/modals/settings/model-selector\";\nimport { useAgentSettingsSchema } from \"#/hooks/query/use-agent-settings-schema\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { SettingsInput } from \"#/components/features/settings/settings-input\";\nimport { HelpLink } from \"#/ui/help-link\";\nimport { KeyStatusIcon } from \"#/components/features/settings/key-status-icon\";\nimport {\n SdkSectionHeaderProps,\n SdkSectionPage,\n SdkSectionSaveControl,\n} from \"#/components/features/settings/sdk-settings/sdk-section-page\";\nimport { LlmSettingsLocalView } from \"#/components/features/settings/llm-profiles\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { Settings, SettingsSchema, SettingsScope } from \"#/types/settings\";\nimport { extractModelAndProvider } from \"#/utils/extract-model-and-provider\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport {\n inferInitialView,\n type SettingsFormValues,\n type SettingsView,\n} from \"#/utils/sdk-settings-schema\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport {\n OPENHANDS_LLM_PROXY_BASE_URL,\n isOpenHandsProviderModel,\n} from \"#/utils/openhands-llm\";\n\nconst LLM_EXCLUDED_KEYS = new Set([\"llm.model\", \"llm.api_key\", \"llm.base_url\"]);\n\nconst buildModelId = (provider: string | null, model: string | null) => {\n if (!provider || !model) return null;\n return `${provider}/${model}`;\n};\n\nconst getSchemaFieldDefaultValue = (\n schema: SettingsSchema | null | undefined,\n fieldKey: string,\n) =>\n schema?.sections\n .flatMap((section) => section.fields)\n .find((field) => field.key === fieldKey)?.default ?? null;\n\nconst KNOWN_PROVIDER_DEFAULT_BASE_URLS: Partial<Record<string, Set<string>>> = {\n openai: new Set([\"https://api.openai.com\", \"https://api.openai.com/v1\"]),\n openhands: new Set([\n OPENHANDS_LLM_PROXY_BASE_URL,\n \"https://llm-proxy.app.all-hands.dev/v1\",\n ]),\n litellm_proxy: new Set([\n OPENHANDS_LLM_PROXY_BASE_URL,\n \"https://llm-proxy.app.all-hands.dev/v1\",\n ]),\n};\n\nconst normalizeBaseUrl = (baseUrl: string) => {\n try {\n const parsedUrl = new URL(baseUrl);\n const normalizedPath = parsedUrl.pathname.replace(/\\/+$/, \"\") || \"\";\n return `${parsedUrl.origin}${normalizedPath}`;\n } catch {\n return baseUrl.trim().replace(/\\/+$/, \"\");\n }\n};\n\nconst isProviderDefaultBaseUrl = (model: string, baseUrl: string) => {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl);\n const { provider } = extractModelAndProvider(model);\n\n if (provider) {\n const knownDefaults = KNOWN_PROVIDER_DEFAULT_BASE_URLS[provider];\n if (knownDefaults) {\n return knownDefaults.has(normalizedBaseUrl);\n }\n }\n\n return Object.values(KNOWN_PROVIDER_DEFAULT_BASE_URLS).some((knownDefaults) =>\n knownDefaults?.has(normalizedBaseUrl),\n );\n};\n\ninterface OpenHandsApiKeyHelpProps {\n testId: string;\n}\n\nfunction OpenHandsApiKeyHelp({ testId }: OpenHandsApiKeyHelpProps) {\n const { t } = useTranslation(\"openhands\");\n\n return (\n <HelpLink\n testId={testId}\n text={t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_TEXT)}\n linkText={t(I18nKey.SETTINGS$NAV_API_KEYS)}\n href=\"https://app.all-hands.dev/settings/api-keys\"\n suffix={` ${t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_SUFFIX)}`}\n />\n );\n}\n\nexport function LlmSettingsScreen({\n scope = \"personal\",\n onSaveSuccess,\n initialValueOverrides,\n embedded,\n hideSaveButton,\n suppressSuccessToast,\n onSaveControlChange,\n}: {\n scope?: SettingsScope;\n /** Optional hook fired after a successful save (e.g. advance an onboarding step). */\n onSaveSuccess?: () => void;\n /** Forwarded to {@link SdkSectionPage}. */\n initialValueOverrides?: SettingsFormValues;\n /** Forwarded to {@link SdkSectionPage}. */\n embedded?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n hideSaveButton?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n suppressSuccessToast?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n onSaveControlChange?: (control: SdkSectionSaveControl) => void;\n}) {\n const { t } = useTranslation(\"openhands\");\n\n const { data: settings } = useSettings(scope);\n const { data: schema } = useAgentSettingsSchema(\n settings?.agent_settings_schema,\n );\n\n const defaultModel = String(\n (DEFAULT_SETTINGS.agent_settings?.llm as Record<string, unknown>)?.model ??\n \"\",\n );\n\n const getInitialView = React.useCallback(\n (\n currentSettings: Settings,\n filteredSchema: SettingsSchema,\n ): SettingsView => {\n const schemaView = inferInitialView(currentSettings, filteredSchema);\n if (schemaView !== \"basic\") {\n return schemaView;\n }\n\n const currentModel = currentSettings.llm_model ?? \"\";\n const trimmedBaseUrl = currentSettings.llm_base_url?.trim() ?? \"\";\n const hasCustomBaseUrl =\n trimmedBaseUrl.length > 0 &&\n !isProviderDefaultBaseUrl(currentModel, trimmedBaseUrl);\n\n return hasCustomBaseUrl ? \"all\" : \"basic\";\n },\n [],\n );\n\n const buildHeader = React.useCallback(\n ({ values, isDisabled, view, onChange }: SdkSectionHeaderProps) => {\n const modelValue =\n typeof values[\"llm.model\"] === \"string\" ? values[\"llm.model\"] : \"\";\n const baseUrlValue =\n typeof values[\"llm.base_url\"] === \"string\"\n ? values[\"llm.base_url\"]\n : \"\";\n const showOpenHandsApiKeyHelp = modelValue.startsWith(\"openhands/\");\n\n const apiKeyValue =\n typeof values[\"llm.api_key\"] === \"string\" ? values[\"llm.api_key\"] : \"\";\n // For embedded profile forms (create/edit) the global\n // `llm_api_key_set` flag is misleading: a brand-new profile would show a\n // \"key set\" indicator just because some other profile has a key. Reflect\n // the form's own key state instead so create mode starts visibly unset.\n const apiKeyIsSet = embedded\n ? apiKeyValue.length > 0\n : Boolean(settings?.llm_api_key_set);\n\n const renderApiKeyInput = (testId: string, helpTestId: string) => (\n <>\n <SettingsInput\n testId={testId}\n label={t(I18nKey.SETTINGS_FORM$API_KEY)}\n type=\"password\"\n className=\"w-full\"\n value={apiKeyValue}\n placeholder={apiKeyIsSet ? \"<hidden>\" : \"\"}\n onChange={(value) => onChange(\"llm.api_key\", value)}\n isDisabled={isDisabled}\n startContent={\n apiKeyIsSet ? <KeyStatusIcon isSet={apiKeyIsSet} /> : undefined\n }\n />\n\n <HelpLink\n testId={helpTestId}\n text={t(I18nKey.SETTINGS$DONT_KNOW_API_KEY)}\n linkText={t(I18nKey.SETTINGS$CLICK_FOR_INSTRUCTIONS)}\n href=\"https://docs.openhands.dev/usage/local-setup#getting-an-api-key\"\n />\n </>\n );\n\n return (\n <div className=\"flex flex-col gap-6\">\n {view === \"basic\" ? (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-basic\"\n >\n <ModelSelector\n currentModel={modelValue || undefined}\n currentBaseUrl={baseUrlValue || undefined}\n onChange={(provider, model) => {\n const nextModel = buildModelId(provider, model);\n if (nextModel) {\n onChange(\"llm.model\", nextModel);\n }\n }}\n wrapperClassName=\"!flex-col !gap-6\"\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help\" />\n ) : null}\n\n {renderApiKeyInput(\n \"llm-api-key-input\",\n \"llm-api-key-help-anchor\",\n )}\n </div>\n ) : (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-advanced\"\n >\n <SettingsInput\n testId=\"llm-custom-model-input\"\n label={t(I18nKey.SETTINGS$CUSTOM_MODEL)}\n type=\"text\"\n className=\"w-full\"\n value={modelValue}\n placeholder={defaultModel}\n onChange={(value) => onChange(\"llm.model\", value)}\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help-2\" />\n ) : null}\n\n <SettingsInput\n testId=\"base-url-input\"\n label={t(I18nKey.SETTINGS$BASE_URL)}\n type=\"text\"\n className=\"w-full\"\n value={baseUrlValue}\n placeholder=\"https://api.openai.com\"\n onChange={(value) => onChange(\"llm.base_url\", value)}\n isDisabled={isDisabled}\n />\n\n {renderApiKeyInput(\n \"llm-api-key-input\",\n \"llm-api-key-help-anchor-advanced\",\n )}\n </div>\n )}\n </div>\n );\n },\n [defaultModel, embedded, settings?.llm_api_key_set, t],\n );\n\n const buildPayload = React.useCallback(\n (\n basePayload: Record<string, unknown>,\n context: {\n values: Record<string, string | boolean>;\n view: SettingsView;\n },\n ) => {\n // basePayload is a nested dict (e.g. {llm: {model: \"gpt-4\"}})\n const agentSettings = structuredClone(basePayload);\n\n const llm = (agentSettings.llm ?? {}) as Record<string, unknown>;\n\n if (context.view === \"basic\") {\n const model = llm.model ?? context.values[\"llm.model\"];\n llm.base_url = isOpenHandsProviderModel(model)\n ? OPENHANDS_LLM_PROXY_BASE_URL\n : getSchemaFieldDefaultValue(schema, \"llm.base_url\");\n agentSettings.llm = llm;\n }\n\n return { agent_settings_diff: agentSettings };\n },\n [schema],\n );\n\n return (\n <SdkSectionPage\n scope={scope}\n sectionKeys={[\"llm\"]}\n excludeKeys={LLM_EXCLUDED_KEYS}\n header={buildHeader}\n buildPayload={buildPayload}\n getInitialView={getInitialView}\n forceShowAdvancedView\n allowAllView\n onSaveSuccess={onSaveSuccess}\n initialValueOverrides={initialValueOverrides}\n embedded={embedded}\n hideSaveButton={hideSaveButton}\n suppressSuccessToast={suppressSuccessToast}\n onSaveControlChange={onSaveControlChange}\n testId=\"llm-settings-screen\"\n />\n );\n}\n\n/**\n * Default export for the route renders different views based on backend type:\n * - Local backends: LlmSettingsLocalView with profile management\n * - Cloud backends: Standard LlmSettingsScreen (profiles are not supported)\n *\n * The LlmSettingsScreen component is also exported for embedded use cases\n * (e.g., onboarding, profile editing forms).\n *\n * Note: This is a route file, only the router should import the default export.\n * Other consumers should use the named export `LlmSettingsScreen` for embedded\n * use cases.\n */\nexport default function LlmSettingsRoute() {\n const { backend } = useActiveBackend();\n const isCloud = backend.kind === \"cloud\";\n\n // Cloud backends use the standard LLM settings form (no profiles support)\n if (isCloud) {\n return <LlmSettingsScreen />;\n }\n\n // Local backends use the profile management view\n return <LlmSettingsLocalView />;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"llm-settings.cjs","names":[],"sources":["../../src/routes/llm-settings.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ModelSelector } from \"#/components/shared/modals/settings/model-selector\";\nimport { useAgentSettingsSchema } from \"#/hooks/query/use-agent-settings-schema\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { SettingsInput } from \"#/components/features/settings/settings-input\";\nimport { HelpLink } from \"#/ui/help-link\";\nimport { KeyStatusIcon } from \"#/components/features/settings/key-status-icon\";\nimport {\n SdkSectionHeaderProps,\n SdkSectionPage,\n SdkSectionSaveControl,\n} from \"#/components/features/settings/sdk-settings/sdk-section-page\";\nimport { LlmSettingsLocalView } from \"#/components/features/settings/llm-profiles\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { Settings, SettingsSchema, SettingsScope } from \"#/types/settings\";\nimport { extractModelAndProvider } from \"#/utils/extract-model-and-provider\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport {\n inferInitialView,\n type SettingsFormValues,\n type SettingsView,\n} from \"#/utils/sdk-settings-schema\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport {\n OPENHANDS_LLM_PROXY_BASE_URL,\n isOpenHandsProxyModel,\n} from \"#/utils/openhands-llm\";\n\nconst LLM_EXCLUDED_KEYS = new Set([\"llm.model\", \"llm.api_key\", \"llm.base_url\"]);\n\nconst buildModelId = (provider: string | null, model: string | null) => {\n if (!provider || !model) return null;\n return `${provider}/${model}`;\n};\n\nconst getSchemaFieldDefaultValue = (\n schema: SettingsSchema | null | undefined,\n fieldKey: string,\n) =>\n schema?.sections\n .flatMap((section) => section.fields)\n .find((field) => field.key === fieldKey)?.default ?? null;\n\nconst KNOWN_PROVIDER_DEFAULT_BASE_URLS: Partial<Record<string, Set<string>>> = {\n openai: new Set([\"https://api.openai.com\", \"https://api.openai.com/v1\"]),\n openhands: new Set([\n OPENHANDS_LLM_PROXY_BASE_URL,\n \"https://llm-proxy.app.all-hands.dev/v1\",\n ]),\n litellm_proxy: new Set([\n OPENHANDS_LLM_PROXY_BASE_URL,\n \"https://llm-proxy.app.all-hands.dev/v1\",\n ]),\n};\n\nconst normalizeBaseUrl = (baseUrl: string) => {\n try {\n const parsedUrl = new URL(baseUrl);\n const normalizedPath = parsedUrl.pathname.replace(/\\/+$/, \"\") || \"\";\n return `${parsedUrl.origin}${normalizedPath}`;\n } catch {\n return baseUrl.trim().replace(/\\/+$/, \"\");\n }\n};\n\nconst isProviderDefaultBaseUrl = (model: string, baseUrl: string) => {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl);\n const { provider } = extractModelAndProvider(model);\n\n if (provider) {\n const knownDefaults = KNOWN_PROVIDER_DEFAULT_BASE_URLS[provider];\n if (knownDefaults) {\n return knownDefaults.has(normalizedBaseUrl);\n }\n }\n\n return Object.values(KNOWN_PROVIDER_DEFAULT_BASE_URLS).some((knownDefaults) =>\n knownDefaults?.has(normalizedBaseUrl),\n );\n};\n\ninterface OpenHandsApiKeyHelpProps {\n testId: string;\n}\n\nfunction OpenHandsApiKeyHelp({ testId }: OpenHandsApiKeyHelpProps) {\n const { t } = useTranslation(\"openhands\");\n\n return (\n <HelpLink\n testId={testId}\n text={t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_TEXT)}\n linkText={t(I18nKey.SETTINGS$NAV_API_KEYS)}\n href=\"https://app.all-hands.dev/settings/api-keys\"\n suffix={` ${t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_SUFFIX)}`}\n />\n );\n}\n\nexport function LlmSettingsScreen({\n scope = \"personal\",\n onSaveSuccess,\n initialValueOverrides,\n embedded,\n hideSaveButton,\n suppressSuccessToast,\n onSaveControlChange,\n}: {\n scope?: SettingsScope;\n /** Optional hook fired after a successful save (e.g. advance an onboarding step). */\n onSaveSuccess?: () => void;\n /** Forwarded to {@link SdkSectionPage}. */\n initialValueOverrides?: SettingsFormValues;\n /** Forwarded to {@link SdkSectionPage}. */\n embedded?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n hideSaveButton?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n suppressSuccessToast?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n onSaveControlChange?: (control: SdkSectionSaveControl) => void;\n}) {\n const { t } = useTranslation(\"openhands\");\n\n const { data: settings } = useSettings(scope);\n const { data: schema } = useAgentSettingsSchema(\n settings?.agent_settings_schema,\n );\n\n const defaultModel = String(\n (DEFAULT_SETTINGS.agent_settings?.llm as Record<string, unknown>)?.model ??\n \"\",\n );\n\n const getInitialView = React.useCallback(\n (\n currentSettings: Settings,\n filteredSchema: SettingsSchema,\n ): SettingsView => {\n const schemaView = inferInitialView(currentSettings, filteredSchema);\n if (schemaView !== \"basic\") {\n return schemaView;\n }\n\n const currentModel = currentSettings.llm_model ?? \"\";\n const trimmedBaseUrl = currentSettings.llm_base_url?.trim() ?? \"\";\n const hasCustomBaseUrl =\n trimmedBaseUrl.length > 0 &&\n !isProviderDefaultBaseUrl(currentModel, trimmedBaseUrl);\n\n return hasCustomBaseUrl ? \"all\" : \"basic\";\n },\n [],\n );\n\n const buildHeader = React.useCallback(\n ({ values, isDisabled, view, onChange }: SdkSectionHeaderProps) => {\n const modelValue =\n typeof values[\"llm.model\"] === \"string\" ? values[\"llm.model\"] : \"\";\n const baseUrlValue =\n typeof values[\"llm.base_url\"] === \"string\"\n ? values[\"llm.base_url\"]\n : \"\";\n const showOpenHandsApiKeyHelp = modelValue.startsWith(\"openhands/\");\n\n const apiKeyValue =\n typeof values[\"llm.api_key\"] === \"string\" ? values[\"llm.api_key\"] : \"\";\n // For embedded profile forms (create/edit) the global\n // `llm_api_key_set` flag is misleading: a brand-new profile would show a\n // \"key set\" indicator just because some other profile has a key. Reflect\n // the form's own key state instead so create mode starts visibly unset.\n const apiKeyIsSet = embedded\n ? apiKeyValue.length > 0\n : Boolean(settings?.llm_api_key_set);\n\n const renderApiKeyInput = (testId: string, helpTestId: string) => (\n <>\n <SettingsInput\n testId={testId}\n label={t(I18nKey.SETTINGS_FORM$API_KEY)}\n type=\"password\"\n className=\"w-full\"\n value={apiKeyValue}\n placeholder={apiKeyIsSet ? \"<hidden>\" : \"\"}\n onChange={(value) => onChange(\"llm.api_key\", value)}\n isDisabled={isDisabled}\n startContent={\n apiKeyIsSet ? <KeyStatusIcon isSet={apiKeyIsSet} /> : undefined\n }\n />\n\n <HelpLink\n testId={helpTestId}\n text={t(I18nKey.SETTINGS$DONT_KNOW_API_KEY)}\n linkText={t(I18nKey.SETTINGS$CLICK_FOR_INSTRUCTIONS)}\n href=\"https://docs.openhands.dev/usage/local-setup#getting-an-api-key\"\n />\n </>\n );\n\n return (\n <div className=\"flex flex-col gap-6\">\n {view === \"basic\" ? (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-basic\"\n >\n <ModelSelector\n currentModel={modelValue || undefined}\n currentBaseUrl={baseUrlValue || undefined}\n onChange={(provider, model) => {\n const nextModel = buildModelId(provider, model);\n if (nextModel) {\n onChange(\"llm.model\", nextModel);\n }\n }}\n wrapperClassName=\"!flex-col !gap-6\"\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help\" />\n ) : null}\n\n {renderApiKeyInput(\n \"llm-api-key-input\",\n \"llm-api-key-help-anchor\",\n )}\n </div>\n ) : (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-advanced\"\n >\n <SettingsInput\n testId=\"llm-custom-model-input\"\n label={t(I18nKey.SETTINGS$CUSTOM_MODEL)}\n type=\"text\"\n className=\"w-full\"\n value={modelValue}\n placeholder={defaultModel}\n onChange={(value) => onChange(\"llm.model\", value)}\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help-2\" />\n ) : null}\n\n <SettingsInput\n testId=\"base-url-input\"\n label={t(I18nKey.SETTINGS$BASE_URL)}\n type=\"text\"\n className=\"w-full\"\n value={baseUrlValue}\n placeholder=\"https://api.openai.com\"\n onChange={(value) => onChange(\"llm.base_url\", value)}\n isDisabled={isDisabled}\n />\n\n {renderApiKeyInput(\n \"llm-api-key-input\",\n \"llm-api-key-help-anchor-advanced\",\n )}\n </div>\n )}\n </div>\n );\n },\n [defaultModel, embedded, settings?.llm_api_key_set, t],\n );\n\n const buildPayload = React.useCallback(\n (\n basePayload: Record<string, unknown>,\n context: {\n values: Record<string, string | boolean>;\n view: SettingsView;\n },\n ) => {\n // basePayload is a nested dict (e.g. {llm: {model: \"gpt-4\"}})\n const agentSettings = structuredClone(basePayload);\n\n const llm = (agentSettings.llm ?? {}) as Record<string, unknown>;\n\n if (context.view === \"basic\") {\n const model = llm.model ?? context.values[\"llm.model\"];\n const baseUrl = llm.base_url ?? context.values[\"llm.base_url\"];\n llm.base_url = isOpenHandsProxyModel(model, baseUrl)\n ? OPENHANDS_LLM_PROXY_BASE_URL\n : getSchemaFieldDefaultValue(schema, \"llm.base_url\");\n agentSettings.llm = llm;\n }\n\n return { agent_settings_diff: agentSettings };\n },\n [schema],\n );\n\n return (\n <SdkSectionPage\n scope={scope}\n sectionKeys={[\"llm\"]}\n excludeKeys={LLM_EXCLUDED_KEYS}\n header={buildHeader}\n buildPayload={buildPayload}\n getInitialView={getInitialView}\n forceShowAdvancedView\n allowAllView\n onSaveSuccess={onSaveSuccess}\n initialValueOverrides={initialValueOverrides}\n embedded={embedded}\n hideSaveButton={hideSaveButton}\n suppressSuccessToast={suppressSuccessToast}\n onSaveControlChange={onSaveControlChange}\n testId=\"llm-settings-screen\"\n />\n );\n}\n\n/**\n * Default export for the route renders different views based on backend type:\n * - Local backends: LlmSettingsLocalView with profile management\n * - Cloud backends: Standard LlmSettingsScreen (profiles are not supported)\n *\n * The LlmSettingsScreen component is also exported for embedded use cases\n * (e.g., onboarding, profile editing forms).\n *\n * Note: This is a route file, only the router should import the default export.\n * Other consumers should use the named export `LlmSettingsScreen` for embedded\n * use cases.\n */\nexport default function LlmSettingsRoute() {\n const { backend } = useActiveBackend();\n const isCloud = backend.kind === \"cloud\";\n\n // Cloud backends use the standard LLM settings form (no profiles support)\n if (isCloud) {\n return <LlmSettingsScreen />;\n }\n\n // Local backends use the profile management view\n return <LlmSettingsLocalView />;\n}\n"],"mappings":"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"}
|
|
@@ -6,12 +6,12 @@ import { useSettings as r } from "../hooks/query/use-settings.js";
|
|
|
6
6
|
import { SettingsInput as i } from "../components/features/settings/settings-input.js";
|
|
7
7
|
import { extractModelAndProvider as a } from "../utils/extract-model-and-provider.js";
|
|
8
8
|
import { HelpLink as o } from "../ui/help-link.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
9
|
+
import { OPENHANDS_LLM_PROXY_BASE_URL as s, isOpenHandsProxyModel as c } from "../utils/openhands-llm.js";
|
|
10
|
+
import { ModelSelector as l } from "../components/shared/modals/settings/model-selector.js";
|
|
11
|
+
import { useAgentSettingsSchema as u } from "../hooks/query/use-agent-settings-schema.js";
|
|
12
|
+
import { KeyStatusIcon as d } from "../components/features/settings/key-status-icon.js";
|
|
13
|
+
import { inferInitialView as f } from "../utils/sdk-settings-schema.js";
|
|
14
|
+
import { SdkSectionPage as p } from "../components/features/settings/sdk-settings/sdk-section-page.js";
|
|
15
15
|
import "../components/features/settings/llm-profiles/index.js";
|
|
16
16
|
import m from "react";
|
|
17
17
|
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
@@ -22,8 +22,8 @@ var v = new Set([
|
|
|
22
22
|
"llm.base_url"
|
|
23
23
|
]), y = (e, t) => !e || !t ? null : `${e}/${t}`, b = (e, t) => e?.sections.flatMap((e) => e.fields).find((e) => e.key === t)?.default ?? null, x = {
|
|
24
24
|
openai: new Set(["https://api.openai.com", "https://api.openai.com/v1"]),
|
|
25
|
-
openhands: new Set([
|
|
26
|
-
litellm_proxy: new Set([
|
|
25
|
+
openhands: new Set([s, "https://llm-proxy.app.all-hands.dev/v1"]),
|
|
26
|
+
litellm_proxy: new Set([s, "https://llm-proxy.app.all-hands.dev/v1"])
|
|
27
27
|
}, S = (e) => {
|
|
28
28
|
try {
|
|
29
29
|
let t = new URL(e), n = t.pathname.replace(/\/+$/, "") || "";
|
|
@@ -50,18 +50,18 @@ function w({ testId: n }) {
|
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
function T({ scope: a = "personal", onSaveSuccess: x, initialValueOverrides: S, embedded: T, hideSaveButton: E, suppressSuccessToast: D, onSaveControlChange: O }) {
|
|
53
|
-
let { t: k } = e("openhands"), { data: A } = r(a), { data: j } =
|
|
54
|
-
let n =
|
|
53
|
+
let { t: k } = e("openhands"), { data: A } = r(a), { data: j } = u(A?.agent_settings_schema), M = String(n.agent_settings?.llm?.model ?? ""), N = m.useCallback((e, t) => {
|
|
54
|
+
let n = f(e, t);
|
|
55
55
|
if (n !== "basic") return n;
|
|
56
56
|
let r = e.llm_model ?? "", i = e.llm_base_url?.trim() ?? "";
|
|
57
57
|
return i.length > 0 && !C(r, i) ? "all" : "basic";
|
|
58
58
|
}, []);
|
|
59
|
-
return /* @__PURE__ */ g(
|
|
59
|
+
return /* @__PURE__ */ g(p, {
|
|
60
60
|
scope: a,
|
|
61
61
|
sectionKeys: ["llm"],
|
|
62
62
|
excludeKeys: v,
|
|
63
63
|
header: m.useCallback(({ values: e, isDisabled: n, view: r, onChange: a }) => {
|
|
64
|
-
let
|
|
64
|
+
let s = typeof e["llm.model"] == "string" ? e["llm.model"] : "", c = typeof e["llm.base_url"] == "string" ? e["llm.base_url"] : "", u = s.startsWith("openhands/"), f = typeof e["llm.api_key"] == "string" ? e["llm.api_key"] : "", p = T ? f.length > 0 : !!A?.llm_api_key_set, m = (e, r) => /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(i, {
|
|
65
65
|
testId: e,
|
|
66
66
|
label: k(t.SETTINGS_FORM$API_KEY),
|
|
67
67
|
type: "password",
|
|
@@ -70,7 +70,7 @@ function T({ scope: a = "personal", onSaveSuccess: x, initialValueOverrides: S,
|
|
|
70
70
|
placeholder: p ? "<hidden>" : "",
|
|
71
71
|
onChange: (e) => a("llm.api_key", e),
|
|
72
72
|
isDisabled: n,
|
|
73
|
-
startContent: p ? /* @__PURE__ */ g(
|
|
73
|
+
startContent: p ? /* @__PURE__ */ g(d, { isSet: p }) : void 0
|
|
74
74
|
}), /* @__PURE__ */ g(o, {
|
|
75
75
|
testId: r,
|
|
76
76
|
text: k(t.SETTINGS$DONT_KNOW_API_KEY),
|
|
@@ -83,9 +83,9 @@ function T({ scope: a = "personal", onSaveSuccess: x, initialValueOverrides: S,
|
|
|
83
83
|
className: "flex flex-col gap-6",
|
|
84
84
|
"data-testid": "llm-settings-form-basic",
|
|
85
85
|
children: [
|
|
86
|
-
/* @__PURE__ */ g(
|
|
87
|
-
currentModel:
|
|
88
|
-
currentBaseUrl:
|
|
86
|
+
/* @__PURE__ */ g(l, {
|
|
87
|
+
currentModel: s || void 0,
|
|
88
|
+
currentBaseUrl: c || void 0,
|
|
89
89
|
onChange: (e, t) => {
|
|
90
90
|
let n = y(e, t);
|
|
91
91
|
n && a("llm.model", n);
|
|
@@ -93,7 +93,7 @@ function T({ scope: a = "personal", onSaveSuccess: x, initialValueOverrides: S,
|
|
|
93
93
|
wrapperClassName: "!flex-col !gap-6",
|
|
94
94
|
isDisabled: n
|
|
95
95
|
}),
|
|
96
|
-
|
|
96
|
+
u ? /* @__PURE__ */ g(w, { testId: "openhands-api-key-help" }) : null,
|
|
97
97
|
m("llm-api-key-input", "llm-api-key-help-anchor")
|
|
98
98
|
]
|
|
99
99
|
}) : /* @__PURE__ */ _("div", {
|
|
@@ -105,18 +105,18 @@ function T({ scope: a = "personal", onSaveSuccess: x, initialValueOverrides: S,
|
|
|
105
105
|
label: k(t.SETTINGS$CUSTOM_MODEL),
|
|
106
106
|
type: "text",
|
|
107
107
|
className: "w-full",
|
|
108
|
-
value:
|
|
108
|
+
value: s,
|
|
109
109
|
placeholder: M,
|
|
110
110
|
onChange: (e) => a("llm.model", e),
|
|
111
111
|
isDisabled: n
|
|
112
112
|
}),
|
|
113
|
-
|
|
113
|
+
u ? /* @__PURE__ */ g(w, { testId: "openhands-api-key-help-2" }) : null,
|
|
114
114
|
/* @__PURE__ */ g(i, {
|
|
115
115
|
testId: "base-url-input",
|
|
116
116
|
label: k(t.SETTINGS$BASE_URL),
|
|
117
117
|
type: "text",
|
|
118
118
|
className: "w-full",
|
|
119
|
-
value:
|
|
119
|
+
value: c,
|
|
120
120
|
placeholder: "https://api.openai.com",
|
|
121
121
|
onChange: (e) => a("llm.base_url", e),
|
|
122
122
|
isDisabled: n
|
|
@@ -133,7 +133,7 @@ function T({ scope: a = "personal", onSaveSuccess: x, initialValueOverrides: S,
|
|
|
133
133
|
]),
|
|
134
134
|
buildPayload: m.useCallback((e, t) => {
|
|
135
135
|
let n = structuredClone(e), r = n.llm ?? {};
|
|
136
|
-
return t.view === "basic" && (r.base_url =
|
|
136
|
+
return t.view === "basic" && (r.base_url = c(r.model ?? t.values["llm.model"], r.base_url ?? t.values["llm.base_url"]) ? s : b(j, "llm.base_url"), n.llm = r), { agent_settings_diff: n };
|
|
137
137
|
}, [j]),
|
|
138
138
|
getInitialView: N,
|
|
139
139
|
forceShowAdvancedView: !0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-settings.js","names":[],"sources":["../../src/routes/llm-settings.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ModelSelector } from \"#/components/shared/modals/settings/model-selector\";\nimport { useAgentSettingsSchema } from \"#/hooks/query/use-agent-settings-schema\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { SettingsInput } from \"#/components/features/settings/settings-input\";\nimport { HelpLink } from \"#/ui/help-link\";\nimport { KeyStatusIcon } from \"#/components/features/settings/key-status-icon\";\nimport {\n SdkSectionHeaderProps,\n SdkSectionPage,\n SdkSectionSaveControl,\n} from \"#/components/features/settings/sdk-settings/sdk-section-page\";\nimport { LlmSettingsLocalView } from \"#/components/features/settings/llm-profiles\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { Settings, SettingsSchema, SettingsScope } from \"#/types/settings\";\nimport { extractModelAndProvider } from \"#/utils/extract-model-and-provider\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport {\n inferInitialView,\n type SettingsFormValues,\n type SettingsView,\n} from \"#/utils/sdk-settings-schema\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport {\n OPENHANDS_LLM_PROXY_BASE_URL,\n isOpenHandsProviderModel,\n} from \"#/utils/openhands-llm\";\n\nconst LLM_EXCLUDED_KEYS = new Set([\"llm.model\", \"llm.api_key\", \"llm.base_url\"]);\n\nconst buildModelId = (provider: string | null, model: string | null) => {\n if (!provider || !model) return null;\n return `${provider}/${model}`;\n};\n\nconst getSchemaFieldDefaultValue = (\n schema: SettingsSchema | null | undefined,\n fieldKey: string,\n) =>\n schema?.sections\n .flatMap((section) => section.fields)\n .find((field) => field.key === fieldKey)?.default ?? null;\n\nconst KNOWN_PROVIDER_DEFAULT_BASE_URLS: Partial<Record<string, Set<string>>> = {\n openai: new Set([\"https://api.openai.com\", \"https://api.openai.com/v1\"]),\n openhands: new Set([\n OPENHANDS_LLM_PROXY_BASE_URL,\n \"https://llm-proxy.app.all-hands.dev/v1\",\n ]),\n litellm_proxy: new Set([\n OPENHANDS_LLM_PROXY_BASE_URL,\n \"https://llm-proxy.app.all-hands.dev/v1\",\n ]),\n};\n\nconst normalizeBaseUrl = (baseUrl: string) => {\n try {\n const parsedUrl = new URL(baseUrl);\n const normalizedPath = parsedUrl.pathname.replace(/\\/+$/, \"\") || \"\";\n return `${parsedUrl.origin}${normalizedPath}`;\n } catch {\n return baseUrl.trim().replace(/\\/+$/, \"\");\n }\n};\n\nconst isProviderDefaultBaseUrl = (model: string, baseUrl: string) => {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl);\n const { provider } = extractModelAndProvider(model);\n\n if (provider) {\n const knownDefaults = KNOWN_PROVIDER_DEFAULT_BASE_URLS[provider];\n if (knownDefaults) {\n return knownDefaults.has(normalizedBaseUrl);\n }\n }\n\n return Object.values(KNOWN_PROVIDER_DEFAULT_BASE_URLS).some((knownDefaults) =>\n knownDefaults?.has(normalizedBaseUrl),\n );\n};\n\ninterface OpenHandsApiKeyHelpProps {\n testId: string;\n}\n\nfunction OpenHandsApiKeyHelp({ testId }: OpenHandsApiKeyHelpProps) {\n const { t } = useTranslation(\"openhands\");\n\n return (\n <HelpLink\n testId={testId}\n text={t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_TEXT)}\n linkText={t(I18nKey.SETTINGS$NAV_API_KEYS)}\n href=\"https://app.all-hands.dev/settings/api-keys\"\n suffix={` ${t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_SUFFIX)}`}\n />\n );\n}\n\nexport function LlmSettingsScreen({\n scope = \"personal\",\n onSaveSuccess,\n initialValueOverrides,\n embedded,\n hideSaveButton,\n suppressSuccessToast,\n onSaveControlChange,\n}: {\n scope?: SettingsScope;\n /** Optional hook fired after a successful save (e.g. advance an onboarding step). */\n onSaveSuccess?: () => void;\n /** Forwarded to {@link SdkSectionPage}. */\n initialValueOverrides?: SettingsFormValues;\n /** Forwarded to {@link SdkSectionPage}. */\n embedded?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n hideSaveButton?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n suppressSuccessToast?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n onSaveControlChange?: (control: SdkSectionSaveControl) => void;\n}) {\n const { t } = useTranslation(\"openhands\");\n\n const { data: settings } = useSettings(scope);\n const { data: schema } = useAgentSettingsSchema(\n settings?.agent_settings_schema,\n );\n\n const defaultModel = String(\n (DEFAULT_SETTINGS.agent_settings?.llm as Record<string, unknown>)?.model ??\n \"\",\n );\n\n const getInitialView = React.useCallback(\n (\n currentSettings: Settings,\n filteredSchema: SettingsSchema,\n ): SettingsView => {\n const schemaView = inferInitialView(currentSettings, filteredSchema);\n if (schemaView !== \"basic\") {\n return schemaView;\n }\n\n const currentModel = currentSettings.llm_model ?? \"\";\n const trimmedBaseUrl = currentSettings.llm_base_url?.trim() ?? \"\";\n const hasCustomBaseUrl =\n trimmedBaseUrl.length > 0 &&\n !isProviderDefaultBaseUrl(currentModel, trimmedBaseUrl);\n\n return hasCustomBaseUrl ? \"all\" : \"basic\";\n },\n [],\n );\n\n const buildHeader = React.useCallback(\n ({ values, isDisabled, view, onChange }: SdkSectionHeaderProps) => {\n const modelValue =\n typeof values[\"llm.model\"] === \"string\" ? values[\"llm.model\"] : \"\";\n const baseUrlValue =\n typeof values[\"llm.base_url\"] === \"string\"\n ? values[\"llm.base_url\"]\n : \"\";\n const showOpenHandsApiKeyHelp = modelValue.startsWith(\"openhands/\");\n\n const apiKeyValue =\n typeof values[\"llm.api_key\"] === \"string\" ? values[\"llm.api_key\"] : \"\";\n // For embedded profile forms (create/edit) the global\n // `llm_api_key_set` flag is misleading: a brand-new profile would show a\n // \"key set\" indicator just because some other profile has a key. Reflect\n // the form's own key state instead so create mode starts visibly unset.\n const apiKeyIsSet = embedded\n ? apiKeyValue.length > 0\n : Boolean(settings?.llm_api_key_set);\n\n const renderApiKeyInput = (testId: string, helpTestId: string) => (\n <>\n <SettingsInput\n testId={testId}\n label={t(I18nKey.SETTINGS_FORM$API_KEY)}\n type=\"password\"\n className=\"w-full\"\n value={apiKeyValue}\n placeholder={apiKeyIsSet ? \"<hidden>\" : \"\"}\n onChange={(value) => onChange(\"llm.api_key\", value)}\n isDisabled={isDisabled}\n startContent={\n apiKeyIsSet ? <KeyStatusIcon isSet={apiKeyIsSet} /> : undefined\n }\n />\n\n <HelpLink\n testId={helpTestId}\n text={t(I18nKey.SETTINGS$DONT_KNOW_API_KEY)}\n linkText={t(I18nKey.SETTINGS$CLICK_FOR_INSTRUCTIONS)}\n href=\"https://docs.openhands.dev/usage/local-setup#getting-an-api-key\"\n />\n </>\n );\n\n return (\n <div className=\"flex flex-col gap-6\">\n {view === \"basic\" ? (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-basic\"\n >\n <ModelSelector\n currentModel={modelValue || undefined}\n currentBaseUrl={baseUrlValue || undefined}\n onChange={(provider, model) => {\n const nextModel = buildModelId(provider, model);\n if (nextModel) {\n onChange(\"llm.model\", nextModel);\n }\n }}\n wrapperClassName=\"!flex-col !gap-6\"\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help\" />\n ) : null}\n\n {renderApiKeyInput(\n \"llm-api-key-input\",\n \"llm-api-key-help-anchor\",\n )}\n </div>\n ) : (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-advanced\"\n >\n <SettingsInput\n testId=\"llm-custom-model-input\"\n label={t(I18nKey.SETTINGS$CUSTOM_MODEL)}\n type=\"text\"\n className=\"w-full\"\n value={modelValue}\n placeholder={defaultModel}\n onChange={(value) => onChange(\"llm.model\", value)}\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help-2\" />\n ) : null}\n\n <SettingsInput\n testId=\"base-url-input\"\n label={t(I18nKey.SETTINGS$BASE_URL)}\n type=\"text\"\n className=\"w-full\"\n value={baseUrlValue}\n placeholder=\"https://api.openai.com\"\n onChange={(value) => onChange(\"llm.base_url\", value)}\n isDisabled={isDisabled}\n />\n\n {renderApiKeyInput(\n \"llm-api-key-input\",\n \"llm-api-key-help-anchor-advanced\",\n )}\n </div>\n )}\n </div>\n );\n },\n [defaultModel, embedded, settings?.llm_api_key_set, t],\n );\n\n const buildPayload = React.useCallback(\n (\n basePayload: Record<string, unknown>,\n context: {\n values: Record<string, string | boolean>;\n view: SettingsView;\n },\n ) => {\n // basePayload is a nested dict (e.g. {llm: {model: \"gpt-4\"}})\n const agentSettings = structuredClone(basePayload);\n\n const llm = (agentSettings.llm ?? {}) as Record<string, unknown>;\n\n if (context.view === \"basic\") {\n const model = llm.model ?? context.values[\"llm.model\"];\n llm.base_url = isOpenHandsProviderModel(model)\n ? OPENHANDS_LLM_PROXY_BASE_URL\n : getSchemaFieldDefaultValue(schema, \"llm.base_url\");\n agentSettings.llm = llm;\n }\n\n return { agent_settings_diff: agentSettings };\n },\n [schema],\n );\n\n return (\n <SdkSectionPage\n scope={scope}\n sectionKeys={[\"llm\"]}\n excludeKeys={LLM_EXCLUDED_KEYS}\n header={buildHeader}\n buildPayload={buildPayload}\n getInitialView={getInitialView}\n forceShowAdvancedView\n allowAllView\n onSaveSuccess={onSaveSuccess}\n initialValueOverrides={initialValueOverrides}\n embedded={embedded}\n hideSaveButton={hideSaveButton}\n suppressSuccessToast={suppressSuccessToast}\n onSaveControlChange={onSaveControlChange}\n testId=\"llm-settings-screen\"\n />\n );\n}\n\n/**\n * Default export for the route renders different views based on backend type:\n * - Local backends: LlmSettingsLocalView with profile management\n * - Cloud backends: Standard LlmSettingsScreen (profiles are not supported)\n *\n * The LlmSettingsScreen component is also exported for embedded use cases\n * (e.g., onboarding, profile editing forms).\n *\n * Note: This is a route file, only the router should import the default export.\n * Other consumers should use the named export `LlmSettingsScreen` for embedded\n * use cases.\n */\nexport default function LlmSettingsRoute() {\n const { backend } = useActiveBackend();\n const isCloud = backend.kind === \"cloud\";\n\n // Cloud backends use the standard LLM settings form (no profiles support)\n if (isCloud) {\n return <LlmSettingsScreen />;\n }\n\n // Local backends use the profile management view\n return <LlmSettingsLocalView />;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"llm-settings.js","names":[],"sources":["../../src/routes/llm-settings.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ModelSelector } from \"#/components/shared/modals/settings/model-selector\";\nimport { useAgentSettingsSchema } from \"#/hooks/query/use-agent-settings-schema\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { SettingsInput } from \"#/components/features/settings/settings-input\";\nimport { HelpLink } from \"#/ui/help-link\";\nimport { KeyStatusIcon } from \"#/components/features/settings/key-status-icon\";\nimport {\n SdkSectionHeaderProps,\n SdkSectionPage,\n SdkSectionSaveControl,\n} from \"#/components/features/settings/sdk-settings/sdk-section-page\";\nimport { LlmSettingsLocalView } from \"#/components/features/settings/llm-profiles\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { Settings, SettingsSchema, SettingsScope } from \"#/types/settings\";\nimport { extractModelAndProvider } from \"#/utils/extract-model-and-provider\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport {\n inferInitialView,\n type SettingsFormValues,\n type SettingsView,\n} from \"#/utils/sdk-settings-schema\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport {\n OPENHANDS_LLM_PROXY_BASE_URL,\n isOpenHandsProxyModel,\n} from \"#/utils/openhands-llm\";\n\nconst LLM_EXCLUDED_KEYS = new Set([\"llm.model\", \"llm.api_key\", \"llm.base_url\"]);\n\nconst buildModelId = (provider: string | null, model: string | null) => {\n if (!provider || !model) return null;\n return `${provider}/${model}`;\n};\n\nconst getSchemaFieldDefaultValue = (\n schema: SettingsSchema | null | undefined,\n fieldKey: string,\n) =>\n schema?.sections\n .flatMap((section) => section.fields)\n .find((field) => field.key === fieldKey)?.default ?? null;\n\nconst KNOWN_PROVIDER_DEFAULT_BASE_URLS: Partial<Record<string, Set<string>>> = {\n openai: new Set([\"https://api.openai.com\", \"https://api.openai.com/v1\"]),\n openhands: new Set([\n OPENHANDS_LLM_PROXY_BASE_URL,\n \"https://llm-proxy.app.all-hands.dev/v1\",\n ]),\n litellm_proxy: new Set([\n OPENHANDS_LLM_PROXY_BASE_URL,\n \"https://llm-proxy.app.all-hands.dev/v1\",\n ]),\n};\n\nconst normalizeBaseUrl = (baseUrl: string) => {\n try {\n const parsedUrl = new URL(baseUrl);\n const normalizedPath = parsedUrl.pathname.replace(/\\/+$/, \"\") || \"\";\n return `${parsedUrl.origin}${normalizedPath}`;\n } catch {\n return baseUrl.trim().replace(/\\/+$/, \"\");\n }\n};\n\nconst isProviderDefaultBaseUrl = (model: string, baseUrl: string) => {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl);\n const { provider } = extractModelAndProvider(model);\n\n if (provider) {\n const knownDefaults = KNOWN_PROVIDER_DEFAULT_BASE_URLS[provider];\n if (knownDefaults) {\n return knownDefaults.has(normalizedBaseUrl);\n }\n }\n\n return Object.values(KNOWN_PROVIDER_DEFAULT_BASE_URLS).some((knownDefaults) =>\n knownDefaults?.has(normalizedBaseUrl),\n );\n};\n\ninterface OpenHandsApiKeyHelpProps {\n testId: string;\n}\n\nfunction OpenHandsApiKeyHelp({ testId }: OpenHandsApiKeyHelpProps) {\n const { t } = useTranslation(\"openhands\");\n\n return (\n <HelpLink\n testId={testId}\n text={t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_TEXT)}\n linkText={t(I18nKey.SETTINGS$NAV_API_KEYS)}\n href=\"https://app.all-hands.dev/settings/api-keys\"\n suffix={` ${t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_SUFFIX)}`}\n />\n );\n}\n\nexport function LlmSettingsScreen({\n scope = \"personal\",\n onSaveSuccess,\n initialValueOverrides,\n embedded,\n hideSaveButton,\n suppressSuccessToast,\n onSaveControlChange,\n}: {\n scope?: SettingsScope;\n /** Optional hook fired after a successful save (e.g. advance an onboarding step). */\n onSaveSuccess?: () => void;\n /** Forwarded to {@link SdkSectionPage}. */\n initialValueOverrides?: SettingsFormValues;\n /** Forwarded to {@link SdkSectionPage}. */\n embedded?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n hideSaveButton?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n suppressSuccessToast?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n onSaveControlChange?: (control: SdkSectionSaveControl) => void;\n}) {\n const { t } = useTranslation(\"openhands\");\n\n const { data: settings } = useSettings(scope);\n const { data: schema } = useAgentSettingsSchema(\n settings?.agent_settings_schema,\n );\n\n const defaultModel = String(\n (DEFAULT_SETTINGS.agent_settings?.llm as Record<string, unknown>)?.model ??\n \"\",\n );\n\n const getInitialView = React.useCallback(\n (\n currentSettings: Settings,\n filteredSchema: SettingsSchema,\n ): SettingsView => {\n const schemaView = inferInitialView(currentSettings, filteredSchema);\n if (schemaView !== \"basic\") {\n return schemaView;\n }\n\n const currentModel = currentSettings.llm_model ?? \"\";\n const trimmedBaseUrl = currentSettings.llm_base_url?.trim() ?? \"\";\n const hasCustomBaseUrl =\n trimmedBaseUrl.length > 0 &&\n !isProviderDefaultBaseUrl(currentModel, trimmedBaseUrl);\n\n return hasCustomBaseUrl ? \"all\" : \"basic\";\n },\n [],\n );\n\n const buildHeader = React.useCallback(\n ({ values, isDisabled, view, onChange }: SdkSectionHeaderProps) => {\n const modelValue =\n typeof values[\"llm.model\"] === \"string\" ? values[\"llm.model\"] : \"\";\n const baseUrlValue =\n typeof values[\"llm.base_url\"] === \"string\"\n ? values[\"llm.base_url\"]\n : \"\";\n const showOpenHandsApiKeyHelp = modelValue.startsWith(\"openhands/\");\n\n const apiKeyValue =\n typeof values[\"llm.api_key\"] === \"string\" ? values[\"llm.api_key\"] : \"\";\n // For embedded profile forms (create/edit) the global\n // `llm_api_key_set` flag is misleading: a brand-new profile would show a\n // \"key set\" indicator just because some other profile has a key. Reflect\n // the form's own key state instead so create mode starts visibly unset.\n const apiKeyIsSet = embedded\n ? apiKeyValue.length > 0\n : Boolean(settings?.llm_api_key_set);\n\n const renderApiKeyInput = (testId: string, helpTestId: string) => (\n <>\n <SettingsInput\n testId={testId}\n label={t(I18nKey.SETTINGS_FORM$API_KEY)}\n type=\"password\"\n className=\"w-full\"\n value={apiKeyValue}\n placeholder={apiKeyIsSet ? \"<hidden>\" : \"\"}\n onChange={(value) => onChange(\"llm.api_key\", value)}\n isDisabled={isDisabled}\n startContent={\n apiKeyIsSet ? <KeyStatusIcon isSet={apiKeyIsSet} /> : undefined\n }\n />\n\n <HelpLink\n testId={helpTestId}\n text={t(I18nKey.SETTINGS$DONT_KNOW_API_KEY)}\n linkText={t(I18nKey.SETTINGS$CLICK_FOR_INSTRUCTIONS)}\n href=\"https://docs.openhands.dev/usage/local-setup#getting-an-api-key\"\n />\n </>\n );\n\n return (\n <div className=\"flex flex-col gap-6\">\n {view === \"basic\" ? (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-basic\"\n >\n <ModelSelector\n currentModel={modelValue || undefined}\n currentBaseUrl={baseUrlValue || undefined}\n onChange={(provider, model) => {\n const nextModel = buildModelId(provider, model);\n if (nextModel) {\n onChange(\"llm.model\", nextModel);\n }\n }}\n wrapperClassName=\"!flex-col !gap-6\"\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help\" />\n ) : null}\n\n {renderApiKeyInput(\n \"llm-api-key-input\",\n \"llm-api-key-help-anchor\",\n )}\n </div>\n ) : (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-advanced\"\n >\n <SettingsInput\n testId=\"llm-custom-model-input\"\n label={t(I18nKey.SETTINGS$CUSTOM_MODEL)}\n type=\"text\"\n className=\"w-full\"\n value={modelValue}\n placeholder={defaultModel}\n onChange={(value) => onChange(\"llm.model\", value)}\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help-2\" />\n ) : null}\n\n <SettingsInput\n testId=\"base-url-input\"\n label={t(I18nKey.SETTINGS$BASE_URL)}\n type=\"text\"\n className=\"w-full\"\n value={baseUrlValue}\n placeholder=\"https://api.openai.com\"\n onChange={(value) => onChange(\"llm.base_url\", value)}\n isDisabled={isDisabled}\n />\n\n {renderApiKeyInput(\n \"llm-api-key-input\",\n \"llm-api-key-help-anchor-advanced\",\n )}\n </div>\n )}\n </div>\n );\n },\n [defaultModel, embedded, settings?.llm_api_key_set, t],\n );\n\n const buildPayload = React.useCallback(\n (\n basePayload: Record<string, unknown>,\n context: {\n values: Record<string, string | boolean>;\n view: SettingsView;\n },\n ) => {\n // basePayload is a nested dict (e.g. {llm: {model: \"gpt-4\"}})\n const agentSettings = structuredClone(basePayload);\n\n const llm = (agentSettings.llm ?? {}) as Record<string, unknown>;\n\n if (context.view === \"basic\") {\n const model = llm.model ?? context.values[\"llm.model\"];\n const baseUrl = llm.base_url ?? context.values[\"llm.base_url\"];\n llm.base_url = isOpenHandsProxyModel(model, baseUrl)\n ? OPENHANDS_LLM_PROXY_BASE_URL\n : getSchemaFieldDefaultValue(schema, \"llm.base_url\");\n agentSettings.llm = llm;\n }\n\n return { agent_settings_diff: agentSettings };\n },\n [schema],\n );\n\n return (\n <SdkSectionPage\n scope={scope}\n sectionKeys={[\"llm\"]}\n excludeKeys={LLM_EXCLUDED_KEYS}\n header={buildHeader}\n buildPayload={buildPayload}\n getInitialView={getInitialView}\n forceShowAdvancedView\n allowAllView\n onSaveSuccess={onSaveSuccess}\n initialValueOverrides={initialValueOverrides}\n embedded={embedded}\n hideSaveButton={hideSaveButton}\n suppressSuccessToast={suppressSuccessToast}\n onSaveControlChange={onSaveControlChange}\n testId=\"llm-settings-screen\"\n />\n );\n}\n\n/**\n * Default export for the route renders different views based on backend type:\n * - Local backends: LlmSettingsLocalView with profile management\n * - Cloud backends: Standard LlmSettingsScreen (profiles are not supported)\n *\n * The LlmSettingsScreen component is also exported for embedded use cases\n * (e.g., onboarding, profile editing forms).\n *\n * Note: This is a route file, only the router should import the default export.\n * Other consumers should use the named export `LlmSettingsScreen` for embedded\n * use cases.\n */\nexport default function LlmSettingsRoute() {\n const { backend } = useActiveBackend();\n const isCloud = backend.kind === \"cloud\";\n\n // Cloud backends use the standard LLM settings form (no profiles support)\n if (isCloud) {\n return <LlmSettingsScreen />;\n }\n\n // Local backends use the profile management view\n return <LlmSettingsLocalView />;\n}\n"],"mappings":"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"}
|
package/dist/routes/mcp.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../i18n/declaration.cjs`),r=require(`../contexts/active-backend-context.cjs`),i=require(`../utils/custom-toast-handlers.cjs`),a=require(`../utils/mcp-config.cjs`),o=require(`../hooks/query/use-settings.cjs`),s=require(`../components/features/settings/brand-button.cjs`),c=require(`../utils/settings-like-page-layout-classes.cjs`),l=require(`../utils/retrieve-axios-error-message.cjs`),u=require(`../components/features/skills/extensions-navigation.cjs`),d=require(`../components/shared/modals/confirmation-modal.cjs`),f=require(`../hooks/mutation/use-delete-mcp-server.cjs`)
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../i18n/declaration.cjs`),r=require(`../contexts/active-backend-context.cjs`),i=require(`../utils/custom-toast-handlers.cjs`),a=require(`../utils/mcp-config.cjs`),o=require(`../hooks/query/use-settings.cjs`),s=require(`../components/features/settings/brand-button.cjs`),c=require(`../utils/settings-like-page-layout-classes.cjs`),l=require(`../utils/retrieve-axios-error-message.cjs`),u=require(`../components/features/skills/extensions-navigation.cjs`),d=require(`../components/shared/modals/confirmation-modal.cjs`),f=require(`../hooks/mutation/use-delete-mcp-server.cjs`),p=require(`../utils/mcp-marketplace-utils.cjs`),m=require(`../node_modules/@openhands/extensions/integrations/index.cjs`),h=require(`../utils/mcp-installed-servers.cjs`),g=require(`../components/features/mcp-page/installed-servers-section.cjs`),_=require(`../components/features/mcp-page/marketplace-section.cjs`),v=require(`../components/features/mcp-page/install-server-modal.cjs`),y=require(`../components/features/mcp-page/custom-server-editor.cjs`),b=require(`../components/features/mcp-page/mcp-toolbar.cjs`);require(`../components/features/mcp-page/index.cjs`);let x=require(`react`);x=e.__toESM(x,1);let S=require(`react/jsx-runtime`);function C(){let{t:e}=t.useTranslation(`openhands`),{data:C,isLoading:w}=o.useSettings(),{mutate:T,isPending:E}=f.useDeleteMcpServer(),D=r.useActiveBackend().backend.kind,[O,k]=x.default.useState(null),[A,j]=x.default.useState(null),[M,N]=x.default.useState(null),[P,F]=x.default.useState(``),[I,L]=x.default.useState(`all`),R=h.flattenMcpConfig(a.parseMcpConfig(C?.agent_settings?.mcp_config)),z=p.getMcpMarketplaceCatalog(m.default),B=R.filter(e=>p.installedServerMatchesQuery(e,p.findCatalogEntryForServer(e,z),P)),V=e=>{k(e)};return w||!C?(0,S.jsxs)(`div`,{"data-testid":`mcp-page`,className:`flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10`,children:[(0,S.jsx)(u.ExtensionsNavigation,{}),(0,S.jsx)(`div`,{className:`flex h-full flex-1 items-center justify-center px-4 md:px-0`,children:(0,S.jsx)(`div`,{className:`h-8 w-8 rounded-full border-2 border-[var(--oh-border)] border-t-white animate-spin`})})]}):(0,S.jsxs)(`div`,{"data-testid":`mcp-page`,className:`flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10`,children:[(0,S.jsx)(u.ExtensionsNavigation,{}),(0,S.jsxs)(`main`,{className:c.settingsLikeMainScrollClassName,children:[(0,S.jsxs)(`div`,{className:`mx-auto flex w-full min-w-0 max-w-[800px] flex-col gap-6`,children:[(0,S.jsx)(`div`,{className:`min-w-0`,children:(0,S.jsxs)(`div`,{className:`flex items-start justify-between gap-4`,children:[(0,S.jsxs)(`div`,{className:`space-y-1`,children:[(0,S.jsx)(`h2`,{className:`text-xl font-medium leading-6 text-foreground`,children:e(n.I18nKey.SETTINGS$MCP_TITLE)}),(0,S.jsx)(`div`,{className:`max-w-2xl text-sm text-tertiary-light`,children:e(n.I18nKey.MCP$PAGE_DESCRIPTION)})]}),(0,S.jsx)(s.BrandButton,{type:`button`,variant:`secondary`,testId:`mcp-add-custom-server`,className:`flex-shrink-0 whitespace-nowrap`,onClick:()=>j({id:``,type:`sse`}),children:e(n.I18nKey.MCP$ADD_CUSTOM)})]})}),(0,S.jsx)(b.McpToolbar,{search:P,onSearchChange:F,sectionFilter:I,onSectionFilterChange:L}),I===`library`?null:(0,S.jsxs)(`section`,{className:`flex flex-col gap-3`,children:[(0,S.jsx)(`h2`,{className:`text-base font-semibold text-foreground`,children:e(n.I18nKey.MCP$INSTALLED_TITLE)}),(0,S.jsx)(g.InstalledServersSection,{servers:B,hasAnyInstalled:R.length>0,query:P,onEdit:e=>{j(e)},onDelete:e=>{let t=R.find(t=>t.id===e);t&&N(t)}})]}),I===`installed`?null:(0,S.jsx)(_.MarketplaceSection,{backendKind:D,onSelect:V,onAdd:V,query:P})]}),O&&(0,S.jsx)(v.InstallServerModal,{entry:O,onClose:()=>k(null)}),A&&(0,S.jsx)(y.CustomServerEditor,{server:A,existingServers:R,onClose:()=>j(null)}),M&&(0,S.jsx)(d.ConfirmationModal,{text:e(n.I18nKey.SETTINGS$MCP_CONFIRM_DELETE),onCancel:()=>N(null),onConfirm:()=>{M&&T(M,{onSuccess:()=>{i.displaySuccessToast(e(n.I18nKey.MCP$REMOVE_SUCCESS)),N(null)},onError:t=>{i.displayErrorToast(l.retrieveAxiosErrorMessage(t)||e(n.I18nKey.ERROR$GENERIC)),N(null)}})},isConfirming:E})]})]})}exports.default=C;
|
|
2
2
|
//# sourceMappingURL=mcp.cjs.map
|
package/dist/routes/mcp.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.cjs","names":[],"sources":["../../src/routes/mcp.tsx"],"sourcesContent":["import React from \"react\";\nimport { AxiosError } from \"axios\";\nimport { ExtensionsNavigation } from \"#/components/features/skills/extensions-navigation\";\nimport { useTranslation } from \"react-i18next\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { ConfirmationModal } from \"#/components/shared/modals/confirmation-modal\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { useDeleteMcpServer } from \"#/hooks/mutation/use-delete-mcp-server\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { parseMcpConfig } from \"#/utils/mcp-config\";\nimport { redirectIfAcpActive } from \"#/utils/acp-route-guard\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { settingsLikeMainScrollClassName } from \"#/utils/settings-like-page-layout-classes\";\nimport {\n findCatalogEntryForServer,\n getMcpMarketplaceCatalog,\n installedServerMatchesQuery,\n} from \"#/utils/mcp-marketplace-utils\";\nimport {\n INTEGRATION_CATALOG as MCP_MARKETPLACE,\n type IntegrationCatalogEntry as MarketplaceEntry,\n} from \"@openhands/extensions/integrations\";\nimport { MCPServerConfig } from \"#/types/mcp-server\";\nimport { flattenMcpConfig } from \"#/utils/mcp-installed-servers\";\nimport {\n InstalledServersSection,\n McpToolbar,\n MarketplaceSection,\n InstallServerModal,\n CustomServerEditor,\n type McpSectionFilter,\n} from \"#/components/features/mcp-page\";\n\n// ACP guard: the ACP sub-agent owns its own MCP server configuration —\n// the SDK explicitly rejects `mcp_config` on ACPAgent init, and\n// `agent-server-adapter` already strips it from start payloads. The\n// Settings → Agent page is where the user configures the ACP server, so\n// bouncing there is consistent with how `/settings` and\n// `/settings/condenser` already behave under ACP.\n//\n// Declared with no parameters (rather than typed as Route.ClientLoaderArgs)\n// so the lib build doesn't pull generated React Router types out of rootDir.\nexport const clientLoader = async () => redirectIfAcpActive();\n\nexport default function MCPPage() {\n const { t } = useTranslation(\"openhands\");\n const { data: settings, isLoading } = useSettings();\n const { mutate: deleteMcpServer, isPending: isDeleting } =\n useDeleteMcpServer();\n const activeBackend = useActiveBackend();\n const backendKind = activeBackend.backend.kind;\n\n const [installEntry, setInstallEntry] =\n React.useState<MarketplaceEntry | null>(null);\n const [editingServer, setEditingServer] =\n React.useState<MCPServerConfig | null>(null);\n const [serverToDelete, setServerToDelete] =\n React.useState<MCPServerConfig | null>(null);\n const [searchQuery, setSearchQuery] = React.useState(\"\");\n const [sectionFilter, setSectionFilter] =\n React.useState<McpSectionFilter>(\"all\");\n\n const mcpConfig = parseMcpConfig(settings?.agent_settings?.mcp_config);\n const allServers = flattenMcpConfig(mcpConfig);\n const mcpMarketplace = getMcpMarketplaceCatalog(MCP_MARKETPLACE);\n\n // Filter installed servers by the search query. We pair each server\n // with its catalog entry (if any) so the search can match friendly\n // names like \"Slack\" against a stdio server whose own `.name` is\n // just \"slack\".\n const filteredInstalledServers = allServers.filter((server) =>\n installedServerMatchesQuery(\n server,\n findCatalogEntryForServer(server, mcpMarketplace),\n searchQuery,\n ),\n );\n\n const handleMarketplaceInstall = (entry: MarketplaceEntry) => {\n setInstallEntry(entry);\n };\n\n const handleEdit = (server: MCPServerConfig) => {\n setEditingServer(server);\n };\n\n const handleDeleteClick = (serverId: string) => {\n const target = allServers.find((s) => s.id === serverId);\n if (target) setServerToDelete(target);\n };\n\n const handleConfirmDelete = () => {\n if (!serverToDelete) return;\n // Pass the full server config — useDeleteMcpServer re-resolves its\n // position against the fresh settings at mutation time, so a\n // background refresh between this click and confirm cannot point\n // us at the wrong index.\n deleteMcpServer(serverToDelete, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$REMOVE_SUCCESS));\n setServerToDelete(null);\n },\n onError: (err) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n displayErrorToast(message || t(I18nKey.ERROR$GENERIC));\n setServerToDelete(null);\n },\n });\n };\n\n if (isLoading || !settings) {\n return (\n <div\n data-testid=\"mcp-page\"\n className=\"flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10\"\n >\n <ExtensionsNavigation />\n <div className=\"flex h-full flex-1 items-center justify-center px-4 md:px-0\">\n <div className=\"h-8 w-8 rounded-full border-2 border-[var(--oh-border)] border-t-white animate-spin\" />\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-testid=\"mcp-page\"\n className=\"flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10\"\n >\n <ExtensionsNavigation />\n <main className={settingsLikeMainScrollClassName}>\n <div className=\"mx-auto flex w-full min-w-0 max-w-[800px] flex-col gap-6\">\n <div className=\"min-w-0\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"space-y-1\">\n <h2 className=\"text-xl font-medium leading-6 text-foreground\">\n {t(I18nKey.SETTINGS$MCP_TITLE)}\n </h2>\n <div className=\"max-w-2xl text-sm text-tertiary-light\">\n {t(I18nKey.MCP$PAGE_DESCRIPTION)}\n </div>\n </div>\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n testId=\"mcp-add-custom-server\"\n className=\"flex-shrink-0 whitespace-nowrap\"\n onClick={() => setEditingServer({ id: \"\", type: \"sse\" })}\n >\n {t(I18nKey.MCP$ADD_CUSTOM)}\n </BrandButton>\n </div>\n </div>\n\n <McpToolbar\n search={searchQuery}\n onSearchChange={setSearchQuery}\n sectionFilter={sectionFilter}\n onSectionFilterChange={setSectionFilter}\n />\n\n {sectionFilter !== \"library\" ? (\n <section className=\"flex flex-col gap-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(I18nKey.MCP$INSTALLED_TITLE)}\n </h2>\n <InstalledServersSection\n servers={filteredInstalledServers}\n hasAnyInstalled={allServers.length > 0}\n query={searchQuery}\n onEdit={handleEdit}\n onDelete={handleDeleteClick}\n />\n </section>\n ) : null}\n\n {sectionFilter !== \"installed\" ? (\n <MarketplaceSection\n backendKind={backendKind}\n onSelect={handleMarketplaceInstall}\n onAdd={handleMarketplaceInstall}\n query={searchQuery}\n />\n ) : null}\n </div>\n\n {installEntry && (\n <InstallServerModal\n entry={installEntry}\n onClose={() => setInstallEntry(null)}\n />\n )}\n\n {/* Custom (or non-marketplace) server editor. The empty-id\n sentinel (`{ id: \"\", type: \"sse\" }`) means \"add new\". */}\n {editingServer && (\n <CustomServerEditor\n server={editingServer}\n existingServers={allServers}\n onClose={() => setEditingServer(null)}\n />\n )}\n\n {serverToDelete && (\n <ConfirmationModal\n text={t(I18nKey.SETTINGS$MCP_CONFIRM_DELETE)}\n onCancel={() => setServerToDelete(null)}\n onConfirm={handleConfirmDelete}\n isConfirming={isDeleting}\n />\n )}\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp.cjs","names":[],"sources":["../../src/routes/mcp.tsx"],"sourcesContent":["import React from \"react\";\nimport { AxiosError } from \"axios\";\nimport { ExtensionsNavigation } from \"#/components/features/skills/extensions-navigation\";\nimport { useTranslation } from \"react-i18next\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { ConfirmationModal } from \"#/components/shared/modals/confirmation-modal\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { useDeleteMcpServer } from \"#/hooks/mutation/use-delete-mcp-server\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { parseMcpConfig } from \"#/utils/mcp-config\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { settingsLikeMainScrollClassName } from \"#/utils/settings-like-page-layout-classes\";\nimport {\n findCatalogEntryForServer,\n getMcpMarketplaceCatalog,\n installedServerMatchesQuery,\n} from \"#/utils/mcp-marketplace-utils\";\nimport {\n INTEGRATION_CATALOG as MCP_MARKETPLACE,\n type IntegrationCatalogEntry as MarketplaceEntry,\n} from \"@openhands/extensions/integrations\";\nimport { MCPServerConfig } from \"#/types/mcp-server\";\nimport { flattenMcpConfig } from \"#/utils/mcp-installed-servers\";\nimport {\n InstalledServersSection,\n McpToolbar,\n MarketplaceSection,\n InstallServerModal,\n CustomServerEditor,\n type McpSectionFilter,\n} from \"#/components/features/mcp-page\";\n\n// No ACP guard here (unlike `/settings` and `/settings/condenser`): MCP\n// servers configured via `agent_settings.mcp_config` are now forwarded to\n// the ACP subprocess at session creation, so this page is meaningful for\n// both OpenHands and ACP agents. The same editor and `mcp_config` storage\n// drive both kinds.\n\nexport default function MCPPage() {\n const { t } = useTranslation(\"openhands\");\n const { data: settings, isLoading } = useSettings();\n const { mutate: deleteMcpServer, isPending: isDeleting } =\n useDeleteMcpServer();\n const activeBackend = useActiveBackend();\n const backendKind = activeBackend.backend.kind;\n\n const [installEntry, setInstallEntry] =\n React.useState<MarketplaceEntry | null>(null);\n const [editingServer, setEditingServer] =\n React.useState<MCPServerConfig | null>(null);\n const [serverToDelete, setServerToDelete] =\n React.useState<MCPServerConfig | null>(null);\n const [searchQuery, setSearchQuery] = React.useState(\"\");\n const [sectionFilter, setSectionFilter] =\n React.useState<McpSectionFilter>(\"all\");\n\n const mcpConfig = parseMcpConfig(settings?.agent_settings?.mcp_config);\n const allServers = flattenMcpConfig(mcpConfig);\n const mcpMarketplace = getMcpMarketplaceCatalog(MCP_MARKETPLACE);\n\n // Filter installed servers by the search query. We pair each server\n // with its catalog entry (if any) so the search can match friendly\n // names like \"Slack\" against a stdio server whose own `.name` is\n // just \"slack\".\n const filteredInstalledServers = allServers.filter((server) =>\n installedServerMatchesQuery(\n server,\n findCatalogEntryForServer(server, mcpMarketplace),\n searchQuery,\n ),\n );\n\n const handleMarketplaceInstall = (entry: MarketplaceEntry) => {\n setInstallEntry(entry);\n };\n\n const handleEdit = (server: MCPServerConfig) => {\n setEditingServer(server);\n };\n\n const handleDeleteClick = (serverId: string) => {\n const target = allServers.find((s) => s.id === serverId);\n if (target) setServerToDelete(target);\n };\n\n const handleConfirmDelete = () => {\n if (!serverToDelete) return;\n // Pass the full server config — useDeleteMcpServer re-resolves its\n // position against the fresh settings at mutation time, so a\n // background refresh between this click and confirm cannot point\n // us at the wrong index.\n deleteMcpServer(serverToDelete, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$REMOVE_SUCCESS));\n setServerToDelete(null);\n },\n onError: (err) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n displayErrorToast(message || t(I18nKey.ERROR$GENERIC));\n setServerToDelete(null);\n },\n });\n };\n\n if (isLoading || !settings) {\n return (\n <div\n data-testid=\"mcp-page\"\n className=\"flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10\"\n >\n <ExtensionsNavigation />\n <div className=\"flex h-full flex-1 items-center justify-center px-4 md:px-0\">\n <div className=\"h-8 w-8 rounded-full border-2 border-[var(--oh-border)] border-t-white animate-spin\" />\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-testid=\"mcp-page\"\n className=\"flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10\"\n >\n <ExtensionsNavigation />\n <main className={settingsLikeMainScrollClassName}>\n <div className=\"mx-auto flex w-full min-w-0 max-w-[800px] flex-col gap-6\">\n <div className=\"min-w-0\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"space-y-1\">\n <h2 className=\"text-xl font-medium leading-6 text-foreground\">\n {t(I18nKey.SETTINGS$MCP_TITLE)}\n </h2>\n <div className=\"max-w-2xl text-sm text-tertiary-light\">\n {t(I18nKey.MCP$PAGE_DESCRIPTION)}\n </div>\n </div>\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n testId=\"mcp-add-custom-server\"\n className=\"flex-shrink-0 whitespace-nowrap\"\n onClick={() => setEditingServer({ id: \"\", type: \"sse\" })}\n >\n {t(I18nKey.MCP$ADD_CUSTOM)}\n </BrandButton>\n </div>\n </div>\n\n <McpToolbar\n search={searchQuery}\n onSearchChange={setSearchQuery}\n sectionFilter={sectionFilter}\n onSectionFilterChange={setSectionFilter}\n />\n\n {sectionFilter !== \"library\" ? (\n <section className=\"flex flex-col gap-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(I18nKey.MCP$INSTALLED_TITLE)}\n </h2>\n <InstalledServersSection\n servers={filteredInstalledServers}\n hasAnyInstalled={allServers.length > 0}\n query={searchQuery}\n onEdit={handleEdit}\n onDelete={handleDeleteClick}\n />\n </section>\n ) : null}\n\n {sectionFilter !== \"installed\" ? (\n <MarketplaceSection\n backendKind={backendKind}\n onSelect={handleMarketplaceInstall}\n onAdd={handleMarketplaceInstall}\n query={searchQuery}\n />\n ) : null}\n </div>\n\n {installEntry && (\n <InstallServerModal\n entry={installEntry}\n onClose={() => setInstallEntry(null)}\n />\n )}\n\n {/* Custom (or non-marketplace) server editor. The empty-id\n sentinel (`{ id: \"\", type: \"sse\" }`) means \"add new\". */}\n {editingServer && (\n <CustomServerEditor\n server={editingServer}\n existingServers={allServers}\n onClose={() => setEditingServer(null)}\n />\n )}\n\n {serverToDelete && (\n <ConfirmationModal\n text={t(I18nKey.SETTINGS$MCP_CONFIRM_DELETE)}\n onCancel={() => setServerToDelete(null)}\n onConfirm={handleConfirmDelete}\n isConfirming={isDeleting}\n />\n )}\n </main>\n </div>\n );\n}\n"],"mappings":"w0CA2CA,SAAwB,GAAU,CAChC,GAAM,CAAE,KAAM,EAAA,eAAe,YAAY,CACnC,CAAE,KAAM,EAAU,aAAc,EAAA,aAAa,CAC7C,CAAE,OAAQ,EAAiB,UAAW,GAC1C,EAAA,oBAAoB,CAEhB,EADgB,EAAA,kBACF,CAAc,QAAQ,KAEpC,CAAC,EAAc,GACnB,EAAA,QAAM,SAAkC,KAAK,CACzC,CAAC,EAAe,GACpB,EAAA,QAAM,SAAiC,KAAK,CACxC,CAAC,EAAgB,GACrB,EAAA,QAAM,SAAiC,KAAK,CACxC,CAAC,EAAa,GAAkB,EAAA,QAAM,SAAS,GAAG,CAClD,CAAC,EAAe,GACpB,EAAA,QAAM,SAA2B,MAAM,CAGnC,EAAa,EAAA,iBADD,EAAA,eAAe,GAAU,gBAAgB,WACvB,CAAU,CACxC,EAAiB,EAAA,yBAAyB,EAAA,QAAgB,CAM1D,EAA2B,EAAW,OAAQ,GAClD,EAAA,4BACE,EACA,EAAA,0BAA0B,EAAQ,EAAe,CACjD,EACD,CACF,CAEK,EAA4B,GAA4B,CAC5D,EAAgB,EAAM,EA6CxB,OAdI,GAAa,CAAC,GAEd,EAAA,EAAA,MAAC,MAAD,CACE,cAAY,WACZ,UAAU,iEAFZ,EAIE,EAAA,EAAA,KAAC,EAAA,qBAAD,EAAwB,CAAA,EACxB,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,wEACb,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,sFAAwF,CAAA,CACnG,CAAA,CACF,IAKR,EAAA,EAAA,MAAC,MAAD,CACE,cAAY,WACZ,UAAU,iEAFZ,EAIE,EAAA,EAAA,KAAC,EAAA,qBAAD,EAAwB,CAAA,EACxB,EAAA,EAAA,MAAC,OAAD,CAAM,UAAW,EAAA,yCAAjB,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oEAAf,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,oBACb,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kDAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qBAAf,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yDACX,EAAE,EAAA,QAAQ,mBAAmB,CAC3B,CAAA,EACL,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,iDACZ,EAAE,EAAA,QAAQ,qBAAqB,CAC5B,CAAA,CACF,IACN,EAAA,EAAA,KAAC,EAAA,YAAD,CACE,KAAK,SACL,QAAQ,YACR,OAAO,wBACP,UAAU,kCACV,YAAe,EAAiB,CAAE,GAAI,GAAI,KAAM,MAAO,CAAC,UAEvD,EAAE,EAAA,QAAQ,eAAe,CACd,CAAA,CACV,GACF,CAAA,EAEN,EAAA,EAAA,KAAC,EAAA,WAAD,CACE,OAAQ,EACR,eAAgB,EACD,gBACf,sBAAuB,EACvB,CAAA,CAED,IAAkB,UAaf,MAZF,EAAA,EAAA,MAAC,UAAD,CAAS,UAAU,+BAAnB,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,mDACX,EAAE,EAAA,QAAQ,oBAAoB,CAC5B,CAAA,EACL,EAAA,EAAA,KAAC,EAAA,wBAAD,CACE,QAAS,EACT,gBAAiB,EAAW,OAAS,EACrC,MAAO,EACP,OAxFM,GAA4B,CAC9C,EAAiB,EAAO,EAwFZ,SArFa,GAAqB,CAC9C,IAAM,EAAS,EAAW,KAAM,GAAM,EAAE,KAAO,EAAS,CACpD,GAAQ,EAAkB,EAAO,EAoFzB,CAAA,CACM,GAGX,IAAkB,YAOf,MANF,EAAA,EAAA,KAAC,EAAA,mBAAD,CACe,cACb,SAAU,EACV,MAAO,EACP,MAAO,EACP,CAAA,CAEA,GAEL,IACC,EAAA,EAAA,KAAC,EAAA,mBAAD,CACE,MAAO,EACP,YAAe,EAAgB,KAAK,CACpC,CAAA,CAKH,IACC,EAAA,EAAA,KAAC,EAAA,mBAAD,CACE,OAAQ,EACR,gBAAiB,EACjB,YAAe,EAAiB,KAAK,CACrC,CAAA,CAGH,IACC,EAAA,EAAA,KAAC,EAAA,kBAAD,CACE,KAAM,EAAE,EAAA,QAAQ,4BAA4B,CAC5C,aAAgB,EAAkB,KAAK,CACvC,cApHwB,CAC3B,GAKL,EAAgB,EAAgB,CAC9B,cAAiB,CACf,EAAA,oBAAoB,EAAE,EAAA,QAAQ,mBAAmB,CAAC,CAClD,EAAkB,KAAK,EAEzB,QAAU,GAAQ,CAEhB,EAAA,kBADgB,EAAA,0BAA0B,EACxB,EAAW,EAAE,EAAA,QAAQ,cAAc,CAAC,CACtD,EAAkB,KAAK,EAE1B,CAAC,EAqGM,aAAc,EACd,CAAA,CAEC,GACH"}
|
package/dist/routes/mcp.d.ts
CHANGED
package/dist/routes/mcp.js
CHANGED
|
@@ -10,7 +10,6 @@ import { retrieveAxiosErrorMessage as l } from "../utils/retrieve-axios-error-me
|
|
|
10
10
|
import { ExtensionsNavigation as u } from "../components/features/skills/extensions-navigation.js";
|
|
11
11
|
import { ConfirmationModal as d } from "../components/shared/modals/confirmation-modal.js";
|
|
12
12
|
import { useDeleteMcpServer as f } from "../hooks/mutation/use-delete-mcp-server.js";
|
|
13
|
-
import "../utils/acp-route-guard.js";
|
|
14
13
|
import { findCatalogEntryForServer as p, getMcpMarketplaceCatalog as m, installedServerMatchesQuery as h } from "../utils/mcp-marketplace-utils.js";
|
|
15
14
|
import g from "../node_modules/@openhands/extensions/integrations/index.js";
|
|
16
15
|
import { flattenMcpConfig as _ } from "../utils/mcp-installed-servers.js";
|
package/dist/routes/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","names":[],"sources":["../../src/routes/mcp.tsx"],"sourcesContent":["import React from \"react\";\nimport { AxiosError } from \"axios\";\nimport { ExtensionsNavigation } from \"#/components/features/skills/extensions-navigation\";\nimport { useTranslation } from \"react-i18next\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { ConfirmationModal } from \"#/components/shared/modals/confirmation-modal\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { useDeleteMcpServer } from \"#/hooks/mutation/use-delete-mcp-server\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { parseMcpConfig } from \"#/utils/mcp-config\";\nimport { redirectIfAcpActive } from \"#/utils/acp-route-guard\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { settingsLikeMainScrollClassName } from \"#/utils/settings-like-page-layout-classes\";\nimport {\n findCatalogEntryForServer,\n getMcpMarketplaceCatalog,\n installedServerMatchesQuery,\n} from \"#/utils/mcp-marketplace-utils\";\nimport {\n INTEGRATION_CATALOG as MCP_MARKETPLACE,\n type IntegrationCatalogEntry as MarketplaceEntry,\n} from \"@openhands/extensions/integrations\";\nimport { MCPServerConfig } from \"#/types/mcp-server\";\nimport { flattenMcpConfig } from \"#/utils/mcp-installed-servers\";\nimport {\n InstalledServersSection,\n McpToolbar,\n MarketplaceSection,\n InstallServerModal,\n CustomServerEditor,\n type McpSectionFilter,\n} from \"#/components/features/mcp-page\";\n\n// ACP guard: the ACP sub-agent owns its own MCP server configuration —\n// the SDK explicitly rejects `mcp_config` on ACPAgent init, and\n// `agent-server-adapter` already strips it from start payloads. The\n// Settings → Agent page is where the user configures the ACP server, so\n// bouncing there is consistent with how `/settings` and\n// `/settings/condenser` already behave under ACP.\n//\n// Declared with no parameters (rather than typed as Route.ClientLoaderArgs)\n// so the lib build doesn't pull generated React Router types out of rootDir.\nexport const clientLoader = async () => redirectIfAcpActive();\n\nexport default function MCPPage() {\n const { t } = useTranslation(\"openhands\");\n const { data: settings, isLoading } = useSettings();\n const { mutate: deleteMcpServer, isPending: isDeleting } =\n useDeleteMcpServer();\n const activeBackend = useActiveBackend();\n const backendKind = activeBackend.backend.kind;\n\n const [installEntry, setInstallEntry] =\n React.useState<MarketplaceEntry | null>(null);\n const [editingServer, setEditingServer] =\n React.useState<MCPServerConfig | null>(null);\n const [serverToDelete, setServerToDelete] =\n React.useState<MCPServerConfig | null>(null);\n const [searchQuery, setSearchQuery] = React.useState(\"\");\n const [sectionFilter, setSectionFilter] =\n React.useState<McpSectionFilter>(\"all\");\n\n const mcpConfig = parseMcpConfig(settings?.agent_settings?.mcp_config);\n const allServers = flattenMcpConfig(mcpConfig);\n const mcpMarketplace = getMcpMarketplaceCatalog(MCP_MARKETPLACE);\n\n // Filter installed servers by the search query. We pair each server\n // with its catalog entry (if any) so the search can match friendly\n // names like \"Slack\" against a stdio server whose own `.name` is\n // just \"slack\".\n const filteredInstalledServers = allServers.filter((server) =>\n installedServerMatchesQuery(\n server,\n findCatalogEntryForServer(server, mcpMarketplace),\n searchQuery,\n ),\n );\n\n const handleMarketplaceInstall = (entry: MarketplaceEntry) => {\n setInstallEntry(entry);\n };\n\n const handleEdit = (server: MCPServerConfig) => {\n setEditingServer(server);\n };\n\n const handleDeleteClick = (serverId: string) => {\n const target = allServers.find((s) => s.id === serverId);\n if (target) setServerToDelete(target);\n };\n\n const handleConfirmDelete = () => {\n if (!serverToDelete) return;\n // Pass the full server config — useDeleteMcpServer re-resolves its\n // position against the fresh settings at mutation time, so a\n // background refresh between this click and confirm cannot point\n // us at the wrong index.\n deleteMcpServer(serverToDelete, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$REMOVE_SUCCESS));\n setServerToDelete(null);\n },\n onError: (err) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n displayErrorToast(message || t(I18nKey.ERROR$GENERIC));\n setServerToDelete(null);\n },\n });\n };\n\n if (isLoading || !settings) {\n return (\n <div\n data-testid=\"mcp-page\"\n className=\"flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10\"\n >\n <ExtensionsNavigation />\n <div className=\"flex h-full flex-1 items-center justify-center px-4 md:px-0\">\n <div className=\"h-8 w-8 rounded-full border-2 border-[var(--oh-border)] border-t-white animate-spin\" />\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-testid=\"mcp-page\"\n className=\"flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10\"\n >\n <ExtensionsNavigation />\n <main className={settingsLikeMainScrollClassName}>\n <div className=\"mx-auto flex w-full min-w-0 max-w-[800px] flex-col gap-6\">\n <div className=\"min-w-0\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"space-y-1\">\n <h2 className=\"text-xl font-medium leading-6 text-foreground\">\n {t(I18nKey.SETTINGS$MCP_TITLE)}\n </h2>\n <div className=\"max-w-2xl text-sm text-tertiary-light\">\n {t(I18nKey.MCP$PAGE_DESCRIPTION)}\n </div>\n </div>\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n testId=\"mcp-add-custom-server\"\n className=\"flex-shrink-0 whitespace-nowrap\"\n onClick={() => setEditingServer({ id: \"\", type: \"sse\" })}\n >\n {t(I18nKey.MCP$ADD_CUSTOM)}\n </BrandButton>\n </div>\n </div>\n\n <McpToolbar\n search={searchQuery}\n onSearchChange={setSearchQuery}\n sectionFilter={sectionFilter}\n onSectionFilterChange={setSectionFilter}\n />\n\n {sectionFilter !== \"library\" ? (\n <section className=\"flex flex-col gap-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(I18nKey.MCP$INSTALLED_TITLE)}\n </h2>\n <InstalledServersSection\n servers={filteredInstalledServers}\n hasAnyInstalled={allServers.length > 0}\n query={searchQuery}\n onEdit={handleEdit}\n onDelete={handleDeleteClick}\n />\n </section>\n ) : null}\n\n {sectionFilter !== \"installed\" ? (\n <MarketplaceSection\n backendKind={backendKind}\n onSelect={handleMarketplaceInstall}\n onAdd={handleMarketplaceInstall}\n query={searchQuery}\n />\n ) : null}\n </div>\n\n {installEntry && (\n <InstallServerModal\n entry={installEntry}\n onClose={() => setInstallEntry(null)}\n />\n )}\n\n {/* Custom (or non-marketplace) server editor. The empty-id\n sentinel (`{ id: \"\", type: \"sse\" }`) means \"add new\". */}\n {editingServer && (\n <CustomServerEditor\n server={editingServer}\n existingServers={allServers}\n onClose={() => setEditingServer(null)}\n />\n )}\n\n {serverToDelete && (\n <ConfirmationModal\n text={t(I18nKey.SETTINGS$MCP_CONFIRM_DELETE)}\n onCancel={() => setServerToDelete(null)}\n onConfirm={handleConfirmDelete}\n isConfirming={isDeleting}\n />\n )}\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp.js","names":[],"sources":["../../src/routes/mcp.tsx"],"sourcesContent":["import React from \"react\";\nimport { AxiosError } from \"axios\";\nimport { ExtensionsNavigation } from \"#/components/features/skills/extensions-navigation\";\nimport { useTranslation } from \"react-i18next\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { ConfirmationModal } from \"#/components/shared/modals/confirmation-modal\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { useDeleteMcpServer } from \"#/hooks/mutation/use-delete-mcp-server\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { parseMcpConfig } from \"#/utils/mcp-config\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { retrieveAxiosErrorMessage } from \"#/utils/retrieve-axios-error-message\";\nimport { settingsLikeMainScrollClassName } from \"#/utils/settings-like-page-layout-classes\";\nimport {\n findCatalogEntryForServer,\n getMcpMarketplaceCatalog,\n installedServerMatchesQuery,\n} from \"#/utils/mcp-marketplace-utils\";\nimport {\n INTEGRATION_CATALOG as MCP_MARKETPLACE,\n type IntegrationCatalogEntry as MarketplaceEntry,\n} from \"@openhands/extensions/integrations\";\nimport { MCPServerConfig } from \"#/types/mcp-server\";\nimport { flattenMcpConfig } from \"#/utils/mcp-installed-servers\";\nimport {\n InstalledServersSection,\n McpToolbar,\n MarketplaceSection,\n InstallServerModal,\n CustomServerEditor,\n type McpSectionFilter,\n} from \"#/components/features/mcp-page\";\n\n// No ACP guard here (unlike `/settings` and `/settings/condenser`): MCP\n// servers configured via `agent_settings.mcp_config` are now forwarded to\n// the ACP subprocess at session creation, so this page is meaningful for\n// both OpenHands and ACP agents. The same editor and `mcp_config` storage\n// drive both kinds.\n\nexport default function MCPPage() {\n const { t } = useTranslation(\"openhands\");\n const { data: settings, isLoading } = useSettings();\n const { mutate: deleteMcpServer, isPending: isDeleting } =\n useDeleteMcpServer();\n const activeBackend = useActiveBackend();\n const backendKind = activeBackend.backend.kind;\n\n const [installEntry, setInstallEntry] =\n React.useState<MarketplaceEntry | null>(null);\n const [editingServer, setEditingServer] =\n React.useState<MCPServerConfig | null>(null);\n const [serverToDelete, setServerToDelete] =\n React.useState<MCPServerConfig | null>(null);\n const [searchQuery, setSearchQuery] = React.useState(\"\");\n const [sectionFilter, setSectionFilter] =\n React.useState<McpSectionFilter>(\"all\");\n\n const mcpConfig = parseMcpConfig(settings?.agent_settings?.mcp_config);\n const allServers = flattenMcpConfig(mcpConfig);\n const mcpMarketplace = getMcpMarketplaceCatalog(MCP_MARKETPLACE);\n\n // Filter installed servers by the search query. We pair each server\n // with its catalog entry (if any) so the search can match friendly\n // names like \"Slack\" against a stdio server whose own `.name` is\n // just \"slack\".\n const filteredInstalledServers = allServers.filter((server) =>\n installedServerMatchesQuery(\n server,\n findCatalogEntryForServer(server, mcpMarketplace),\n searchQuery,\n ),\n );\n\n const handleMarketplaceInstall = (entry: MarketplaceEntry) => {\n setInstallEntry(entry);\n };\n\n const handleEdit = (server: MCPServerConfig) => {\n setEditingServer(server);\n };\n\n const handleDeleteClick = (serverId: string) => {\n const target = allServers.find((s) => s.id === serverId);\n if (target) setServerToDelete(target);\n };\n\n const handleConfirmDelete = () => {\n if (!serverToDelete) return;\n // Pass the full server config — useDeleteMcpServer re-resolves its\n // position against the fresh settings at mutation time, so a\n // background refresh between this click and confirm cannot point\n // us at the wrong index.\n deleteMcpServer(serverToDelete, {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.MCP$REMOVE_SUCCESS));\n setServerToDelete(null);\n },\n onError: (err) => {\n const message = retrieveAxiosErrorMessage(err as AxiosError);\n displayErrorToast(message || t(I18nKey.ERROR$GENERIC));\n setServerToDelete(null);\n },\n });\n };\n\n if (isLoading || !settings) {\n return (\n <div\n data-testid=\"mcp-page\"\n className=\"flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10\"\n >\n <ExtensionsNavigation />\n <div className=\"flex h-full flex-1 items-center justify-center px-4 md:px-0\">\n <div className=\"h-8 w-8 rounded-full border-2 border-[var(--oh-border)] border-t-white animate-spin\" />\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-testid=\"mcp-page\"\n className=\"flex h-full gap-4 md:gap-6 md:pl-8 lg:gap-10 lg:pl-10\"\n >\n <ExtensionsNavigation />\n <main className={settingsLikeMainScrollClassName}>\n <div className=\"mx-auto flex w-full min-w-0 max-w-[800px] flex-col gap-6\">\n <div className=\"min-w-0\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"space-y-1\">\n <h2 className=\"text-xl font-medium leading-6 text-foreground\">\n {t(I18nKey.SETTINGS$MCP_TITLE)}\n </h2>\n <div className=\"max-w-2xl text-sm text-tertiary-light\">\n {t(I18nKey.MCP$PAGE_DESCRIPTION)}\n </div>\n </div>\n <BrandButton\n type=\"button\"\n variant=\"secondary\"\n testId=\"mcp-add-custom-server\"\n className=\"flex-shrink-0 whitespace-nowrap\"\n onClick={() => setEditingServer({ id: \"\", type: \"sse\" })}\n >\n {t(I18nKey.MCP$ADD_CUSTOM)}\n </BrandButton>\n </div>\n </div>\n\n <McpToolbar\n search={searchQuery}\n onSearchChange={setSearchQuery}\n sectionFilter={sectionFilter}\n onSectionFilterChange={setSectionFilter}\n />\n\n {sectionFilter !== \"library\" ? (\n <section className=\"flex flex-col gap-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(I18nKey.MCP$INSTALLED_TITLE)}\n </h2>\n <InstalledServersSection\n servers={filteredInstalledServers}\n hasAnyInstalled={allServers.length > 0}\n query={searchQuery}\n onEdit={handleEdit}\n onDelete={handleDeleteClick}\n />\n </section>\n ) : null}\n\n {sectionFilter !== \"installed\" ? (\n <MarketplaceSection\n backendKind={backendKind}\n onSelect={handleMarketplaceInstall}\n onAdd={handleMarketplaceInstall}\n query={searchQuery}\n />\n ) : null}\n </div>\n\n {installEntry && (\n <InstallServerModal\n entry={installEntry}\n onClose={() => setInstallEntry(null)}\n />\n )}\n\n {/* Custom (or non-marketplace) server editor. The empty-id\n sentinel (`{ id: \"\", type: \"sse\" }`) means \"add new\". */}\n {editingServer && (\n <CustomServerEditor\n server={editingServer}\n existingServers={allServers}\n onClose={() => setEditingServer(null)}\n />\n )}\n\n {serverToDelete && (\n <ConfirmationModal\n text={t(I18nKey.SETTINGS$MCP_CONFIRM_DELETE)}\n onCancel={() => setServerToDelete(null)}\n onConfirm={handleConfirmDelete}\n isConfirming={isDeleting}\n />\n )}\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ACPToolCallEvent, ActionEvent, MessageEvent, ObservationEvent, UserRejectObservation, AgentErrorEvent, SystemPromptEvent, CondensationEvent, CondensationRequestEvent, CondensationSummaryEvent, ConversationStateUpdateEvent, ConversationErrorEvent, HookExecutionEvent, PauseEvent, ServerErrorEvent } from "./events/index";
|
|
1
|
+
import { ACPToolCallEvent, ActionEvent, MessageEvent, ObservationEvent, UserRejectObservation, AgentErrorEvent, SystemPromptEvent, CondensationEvent, CondensationRequestEvent, CondensationSummaryEvent, ConversationStateUpdateEvent, ConversationErrorEvent, HookExecutionEvent, PauseEvent, ServerErrorEvent, StreamingDeltaEvent } from "./events/index";
|
|
2
2
|
/**
|
|
3
3
|
* Union type representing all possible OpenHands events.
|
|
4
4
|
* This includes all main event types that can occur in the system.
|
|
5
5
|
*/
|
|
6
|
-
export type OpenHandsEvent = ActionEvent | MessageEvent | ObservationEvent | UserRejectObservation | AgentErrorEvent | SystemPromptEvent | ACPToolCallEvent | HookExecutionEvent | CondensationEvent | CondensationRequestEvent | CondensationSummaryEvent | ConversationStateUpdateEvent | ConversationErrorEvent | PauseEvent | ServerErrorEvent;
|
|
6
|
+
export type OpenHandsEvent = ActionEvent | MessageEvent | ObservationEvent | UserRejectObservation | AgentErrorEvent | SystemPromptEvent | ACPToolCallEvent | HookExecutionEvent | CondensationEvent | CondensationRequestEvent | CondensationSummaryEvent | ConversationStateUpdateEvent | ConversationErrorEvent | PauseEvent | ServerErrorEvent | StreamingDeltaEvent;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../_virtual/_rolldown/runtime.cjs`);function e(e){return typeof e==`object`&&!!e&&`id`in e&&`timestamp`in e&&`source`in e&&typeof e.id==`string`&&e.id.length>0&&typeof e.timestamp==`string`&&e.timestamp.length>0&&typeof e.source==`string`&&(e.source===`agent`||e.source===`user`||e.source===`environment`||e.source===`hook`)}var t=e=>e.source===`environment`&&`action_id`in e&&`observation`in e&&e.observation!==null&&typeof e.observation==`object`&&`kind`in e.observation,n=e=>e.source===`agent`&&`tool_name`in e&&`tool_call_id`in e&&`error`in e&&typeof e.tool_name==`string`&&typeof e.tool_call_id==`string`&&typeof e.error==`string`,r=e=>`llm_message`in e&&typeof e.llm_message==`object`&&e.llm_message!==null&&`role`in e.llm_message&&`content`in e.llm_message,i=e=>r(e)&&e.llm_message.role===`user`,a=e=>e.source===`agent`&&`action`in e&&e.action!==null&&typeof e.action==`object`&&`kind`in e.action&&`tool_name`in e&&`tool_call_id`in e&&typeof e.tool_name==`string`&&typeof e.tool_call_id==`string`,o=e=>a(e)&&(e.action.kind===`ExecuteBashAction`||e.action.kind===`TerminalAction`),s=e=>t(e)&&(e.observation.kind===`ExecuteBashObservation`||e.observation.kind===`TerminalObservation`),c=e=>t(e)&&e.observation.kind===`PlanningFileEditorObservation`,l=e=>t(e)&&e.observation.kind===`BrowserObservation`,u=e=>t(e)&&e.observation.kind===`SwitchLLMObservation`,d=e=>a(e)&&e.action.kind===`BrowserNavigateAction`,f=e=>a(e)&&e.tool_name===`canvas_ui`,p=e=>e.source===`agent`&&`system_prompt`in e&&`tools`in e&&typeof e.system_prompt==`object`&&Array.isArray(e.tools),m=e=>`kind`in e&&e.kind===`ConversationStateUpdateEvent`,h=e=>e.key===`full_state`,g=e=>e.key===`execution_status`,_=e=>e.key===`stats`,v=e=>`kind`in e&&e.kind===`ConversationErrorEvent`,y=e=>`kind`in e&&e.kind===`ServerErrorEvent`,b=e=>v(e)||y(e),x=e=>`kind`in e&&e.kind===`HookExecutionEvent`,S=e=>`kind`in e&&e.kind===`ACPToolCallEvent`;function
|
|
1
|
+
require(`../../_virtual/_rolldown/runtime.cjs`);function e(e){return typeof e==`object`&&!!e&&`id`in e&&`timestamp`in e&&`source`in e&&typeof e.id==`string`&&e.id.length>0&&typeof e.timestamp==`string`&&e.timestamp.length>0&&typeof e.source==`string`&&(e.source===`agent`||e.source===`user`||e.source===`environment`||e.source===`hook`)}var t=e=>e.source===`environment`&&`action_id`in e&&`observation`in e&&e.observation!==null&&typeof e.observation==`object`&&`kind`in e.observation,n=e=>e.source===`agent`&&`tool_name`in e&&`tool_call_id`in e&&`error`in e&&typeof e.tool_name==`string`&&typeof e.tool_call_id==`string`&&typeof e.error==`string`,r=e=>`llm_message`in e&&typeof e.llm_message==`object`&&e.llm_message!==null&&`role`in e.llm_message&&`content`in e.llm_message,i=e=>r(e)&&e.llm_message.role===`user`,a=e=>e.source===`agent`&&`action`in e&&e.action!==null&&typeof e.action==`object`&&`kind`in e.action&&`tool_name`in e&&`tool_call_id`in e&&typeof e.tool_name==`string`&&typeof e.tool_call_id==`string`,o=e=>a(e)&&(e.action.kind===`ExecuteBashAction`||e.action.kind===`TerminalAction`),s=e=>t(e)&&(e.observation.kind===`ExecuteBashObservation`||e.observation.kind===`TerminalObservation`),c=e=>t(e)&&e.observation.kind===`PlanningFileEditorObservation`,l=e=>t(e)&&e.observation.kind===`BrowserObservation`,u=e=>t(e)&&e.observation.kind===`SwitchLLMObservation`,d=e=>a(e)&&e.action.kind===`BrowserNavigateAction`,f=e=>a(e)&&e.tool_name===`canvas_ui`,p=e=>e.source===`agent`&&`system_prompt`in e&&`tools`in e&&typeof e.system_prompt==`object`&&Array.isArray(e.tools),m=e=>`kind`in e&&e.kind===`ConversationStateUpdateEvent`,h=e=>e.key===`full_state`,g=e=>e.key===`execution_status`,_=e=>e.key===`stats`,v=e=>`kind`in e&&e.kind===`ConversationErrorEvent`,y=e=>`kind`in e&&e.kind===`ServerErrorEvent`,b=e=>v(e)||y(e),x=e=>`kind`in e&&e.kind===`HookExecutionEvent`,S=e=>`kind`in e&&e.kind===`ACPToolCallEvent`,C=e=>`kind`in e&&e.kind===`StreamingDeltaEvent`;function w(t){return e(t)}exports.isACPToolCallEvent=S,exports.isActionEvent=a,exports.isAgentErrorEvent=n,exports.isAgentServerEvent=w,exports.isAgentStatusConversationStateUpdateEvent=g,exports.isBrowserNavigateActionEvent=d,exports.isBrowserObservationEvent=l,exports.isCanvasUIActionEvent=f,exports.isConversationStateUpdateEvent=m,exports.isDisplayableErrorEvent=b,exports.isExecuteBashActionEvent=o,exports.isExecuteBashObservationEvent=s,exports.isFullStateConversationStateUpdateEvent=h,exports.isHookExecutionEvent=x,exports.isMessageEvent=r,exports.isObservationEvent=t,exports.isPlanningFileEditorObservationEvent=c,exports.isStatsConversationStateUpdateEvent=_,exports.isStreamingDeltaEvent=C,exports.isSwitchLLMObservationEvent=u,exports.isSystemPromptEvent=p,exports.isUserMessageEvent=i;
|
|
2
2
|
//# sourceMappingURL=type-guards.cjs.map
|