@openhands/agent-canvas 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.windows.md +2 -2
- package/build/assets/{Trans-DP5rcauH.js → Trans-B7lYw4uD.js} +1 -1
- package/build/assets/{acp-providers-BHKZvEm5.js → acp-providers-WUD4tfSq.js} +1 -1
- package/build/assets/active-backend-context-mY7Gff5c.js +2 -0
- package/build/assets/{agent-context-settings-CTaN-Iw9.js → agent-context-settings-BTUmWEzL.js} +1 -1
- package/build/assets/{agent-profiles-settings-CR4VTqv_.js → agent-profiles-settings-Ilqid_R5.js} +1 -1
- package/build/assets/agent-server-conversation-service.api-C_7YnjzP.js +41 -0
- package/build/assets/{agent-settings-BIOsUglV.js → agent-settings-BGTyoT6X.js} +2 -2
- package/build/assets/agent-settings-CxfT4ksO.js +1 -0
- package/build/assets/{alert-banner-C1nzoXBC.js → alert-banner-eRyIwDyU.js} +1 -1
- package/build/assets/api-key-entry-screen-JOx3SH1-.js +1 -0
- package/build/assets/app-settings-D53QZy5p.js +1 -0
- package/build/assets/automation-detail-nEflClmc.js +2 -0
- package/build/assets/{automation-export-Bu-r-EzL.js → automation-export-BO_kfqC8.js} +1 -1
- package/build/assets/{automations-list-CYw7_6FR.js → automations-list-BYncC7gv.js} +1 -1
- package/build/assets/{back-nav-button-Be_L157B.js → back-nav-button-C02hGa8t.js} +1 -1
- package/build/assets/backend-form-modal-CKrBF80P.js +1 -0
- package/build/assets/{backend-synced-settings-badge-BNpEylNP.js → backend-synced-settings-badge-CQ9xtNWm.js} +1 -1
- package/build/assets/{badge-DwgrwohM.js → badge-CzXbf7tR.js} +1 -1
- package/build/assets/{base-modal-CSHhJTSr.js → base-modal-DkVyzFMt.js} +1 -1
- package/build/assets/{brand-button-CQO8oqVo.js → brand-button-BUlDL-mz.js} +1 -1
- package/build/assets/{browser-BUDJnBg4.js → browser-3QjrEr1D.js} +1 -1
- package/build/assets/{browser-tab-CEApjoEV.js → browser-tab-BijpUbdt.js} +1 -1
- package/build/assets/{chat-send-button-D4614wUz.js → chat-send-button-BKsVjhtA.js} +1 -1
- package/build/assets/check-CrxQfJZ2.js +1 -0
- package/build/assets/chevron-down-D_LsyTA7.js +1 -0
- package/build/assets/circle-check-DZn9HkLE.js +1 -0
- package/build/assets/{circle-plus-check-toggle-E3Ecb_gg.js → circle-plus-check-toggle-DdjUiOo1.js} +1 -1
- package/build/assets/cloud-funnel-analytics-NlmDoeo2.js +1 -0
- package/build/assets/{combobox-caret-C8_eGdp2.js → combobox-caret-Dl9UtvD6.js} +1 -1
- package/build/assets/{command-menu-N_BriUy8.js → command-menu-CvLPs7dg.js} +1 -1
- package/build/assets/{condenser-settings-Czi8cKxV.js → condenser-settings-C4Y9CzQV.js} +1 -1
- package/build/assets/{confirmation-modal-gZtbojCm.js → confirmation-modal-CRBJSllP.js} +1 -1
- package/build/assets/{context-menu-list-item-BTHGFhIx.js → context-menu-list-item-FGaafXGh.js} +1 -1
- package/build/assets/conversation-D_ORtpKC.js +1 -0
- package/build/assets/conversation-Dpc5nmGi.js +90 -0
- package/build/assets/conversation-panel-D_ORtpKC.js +1 -0
- package/build/assets/conversation-state-store-CGRQemtx.js +1 -0
- package/build/assets/{conversation-tab-empty-state-BCMHkGQk.js → conversation-tab-empty-state-CDjvNgpG.js} +1 -1
- package/build/assets/{copy-B8Y5ufoR.js → copy-Drwu97Gx.js} +1 -1
- package/build/assets/{copy-to-clipboard-button-C4l3CMSd.js → copy-to-clipboard-button-NbN1clVr.js} +1 -1
- package/build/assets/{device-verify-C_M7Z8xs.js → device-verify-CDm_WrVu.js} +1 -1
- package/build/assets/{dropdown-classes-DQlj-MpS.js → dropdown-classes-CqA2BTCL.js} +1 -1
- package/build/assets/{entry.client-DETaouwO.js → entry.client-BO2KQj0y.js} +2 -2
- package/build/assets/{enum-filter-dropdown-paB-lFSP.js → enum-filter-dropdown-DSqPZnsD.js} +1 -1
- package/build/assets/{environment-switch-overlay-D0FgCMqs.js → environment-switch-overlay-mGl2pqqx.js} +1 -1
- package/build/assets/extensions-hub-Br3Lgynp.js +1 -0
- package/build/assets/extensions-navigation-BQmstKDZ.js +1 -0
- package/build/assets/{external-link-Cnnd6ikC.js → external-link-D9B2QMPf.js} +1 -1
- package/build/assets/files-tab-4c9HFSLo.js +1 -0
- package/build/assets/{form-control-classes-CSPevi7c.js → form-control-classes-CtVAktAy.js} +1 -1
- package/build/assets/{format-time-delta-DuhE50B-.js → format-time-delta-DICE64sz.js} +1 -1
- package/build/assets/git-provider-icon-0PqmIS_S.js +1 -0
- package/build/assets/{highlighted-source-view-suUKd5if.js → highlighted-source-view-KMsA9c4a.js} +1 -1
- package/build/assets/home-D9JsyEmE.js +1 -0
- package/build/assets/{home-sN90Xcxp.js → home-TFhnmZLx.js} +1 -1
- package/build/assets/index-home-D9JsyEmE.js +1 -0
- package/build/assets/install-server-modal-BgGRnqA0.js +1 -0
- package/build/assets/{launch-Ccnhte5b.js → launch-DjkKw55l.js} +1 -1
- package/build/assets/{list-todo-_BXQzOiB.js → list-todo-BpAf7aOT.js} +1 -1
- package/build/assets/{llm-not-configured-banner-DTWzISnk.js → llm-not-configured-banner-Y4W9-sar.js} +4 -4
- package/build/assets/llm-settings-D8zNkIlP.js +1 -0
- package/build/assets/llm-settings-nPwv7hKu.js +1 -0
- package/build/assets/{llm-subscription-service-W5BprsNA.js → llm-subscription-service-COTgZc1C.js} +1 -1
- package/build/assets/loader-circle-CGD6-Vak.js +1 -0
- package/build/assets/{loading-spinner-CcpnCr_d.js → loading-spinner-DRFyh6Iy.js} +1 -1
- package/build/assets/manage-backends-modal-DiRWE3tJ.js +1 -0
- package/build/assets/manifest-1cf33a7b.js +1 -0
- package/build/assets/markdown-renderer-U40HF0Ak.js +8 -0
- package/build/assets/mcp-xDMaHzOw.js +9 -0
- package/build/assets/{messages-DINDkPNK.js → messages-CdNfZPgd.js} +2 -2
- package/build/assets/{modal-body-DQhiw-AR.js → modal-body-C3bGicn4.js} +1 -1
- package/build/assets/{modal-classes-YaEEa5eZ.js → modal-classes-DFFO6BMU.js} +1 -1
- package/build/assets/{modal-close-button-turIYn0t.js → modal-close-button-vxkAIRyZ.js} +1 -1
- package/build/assets/{onboarding-BegDrOKn.js → onboarding-DWuFieEA.js} +1 -1
- package/build/assets/onboarding-modal-C8m72Dpb.js +1 -0
- package/build/assets/organization-service.api-CQ73CB-S.js +1 -0
- package/build/assets/{pencil-8_lvWDoZ.js → pencil-DzW2GB2M.js} +1 -1
- package/build/assets/{planner-tab-w6HT0N_A.js → planner-tab-YG0bGxDY.js} +1 -1
- package/build/assets/plugins-management-service-ijhH8OSh.js +1 -0
- package/build/assets/plus-CTGLo3aa.js +1 -0
- package/build/assets/profiles-service.api-C32pPm70.js +1 -0
- package/build/assets/providers-DuSn9s2i.js +1 -0
- package/build/assets/proxy-BiyDhMBd.js +1 -0
- package/build/assets/{puzzle-DXWdfaa7.js → puzzle-DlqT0yvr.js} +1 -1
- package/build/assets/{recommended-automations-launcher-BF2mtAKw.js → recommended-automations-launcher-Dru1yN46.js} +2 -2
- package/build/assets/{refresh-cw-D7CYLy-d.js → refresh-cw-Br5254f4.js} +1 -1
- package/build/assets/root-DOLLQteU.js +2 -0
- package/build/assets/root-layout-xmylWoew.js +3 -0
- package/build/assets/schema-field-htfNouTG.js +1 -0
- package/build/assets/{sdk-section-page-BW-i45yr.js → sdk-section-page-D0K1dBQY.js} +1 -1
- package/build/assets/search-BrpWXWUi.js +1 -0
- package/build/assets/secrets-service-Dbv5Zb8G.js +1 -0
- package/build/assets/{secrets-settings-D7U1ErCH.js → secrets-settings-Da4SSaX9.js} +1 -1
- package/build/assets/{segmented-toggle-DbbD3Tve.js → segmented-toggle-CZKOdIly.js} +1 -1
- package/build/assets/settings-F4a09IxY.js +1 -0
- package/build/assets/{settings-dropdown-input-Dx6vlkgf.js → settings-dropdown-input-jOiv5Yrk.js} +1 -1
- package/build/assets/{settings-index-COAdxm2Q.js → settings-index-CyJBf5NA.js} +1 -1
- package/build/assets/{settings-input-BjW_zyTN.js → settings-input-BEaTCem1.js} +1 -1
- package/build/assets/{settings-list-classes-gQdBahdt.js → settings-list-classes-VqMzkYYt.js} +1 -1
- package/build/assets/{settings-modal-CFnbQ8_4.js → settings-modal-C-S1NpbC.js} +1 -1
- package/build/assets/settings-service.api-CTJRrvMu.js +1 -0
- package/build/assets/{settings-switch-UNSf8K8I.js → settings-switch-B405oU_v.js} +1 -1
- package/build/assets/{shared-conversation-CnMxy7p_.js → shared-conversation-BvWtTwES.js} +1 -1
- package/build/assets/{sidebar-layout-DXHtbEj1.js → sidebar-layout-BGMuyC86.js} +1 -1
- package/build/assets/{sidebar-mobile-menu-toggle-U78hQRJ1.js → sidebar-mobile-menu-toggle-xiKoUfGa.js} +1 -1
- package/build/assets/{sidebar-nav-link-pvUivfK2.js → sidebar-nav-link-DA4_ct9G.js} +1 -1
- package/build/assets/{sidebar-store-DwrYKwYr.js → sidebar-store-FRJ3rADB.js} +1 -1
- package/build/assets/{skill-card-pill-row-D7XCz5uk.js → skill-card-pill-row-BMdhr_sd.js} +1 -1
- package/build/assets/{skill-icon-badge-HqBWZ2Eq.js → skill-icon-badge-CGUkgcVH.js} +1 -1
- package/build/assets/{skills-plugins-BPhaNpfx.js → skills-plugins-C5eJnyft.js} +1 -1
- package/build/assets/{skills-settings-CcwMtnnJ.js → skills-settings-pUxMr6WZ.js} +1 -1
- package/build/assets/{styled-tooltip-DsjDnR_W.js → styled-tooltip-B3kIdVyH.js} +1 -1
- package/build/assets/{task-list-tab-BmaTwjaS.js → task-list-tab-B3k4W1cK.js} +1 -1
- package/build/assets/{terminal-BRcoba5B.js → terminal-CBI_41Zc.js} +1 -1
- package/build/assets/{toggle-switch-BptY3XRG.js → toggle-switch-DYRr9CAR.js} +1 -1
- package/build/assets/{trash-2-Bq4lHbTB.js → trash-2-gZPlx23R.js} +1 -1
- package/build/assets/{typography-C6fuRTTT.js → typography-B1UxuuEC.js} +1 -1
- package/build/assets/{use-acp-credential-form-B0_pZ_uS.js → use-acp-credential-form-CjSvKWFH.js} +1 -1
- package/build/assets/use-activate-agent-profile-DCRf47e-.js +1 -0
- package/build/assets/use-agent-profiles-DmT9zxAN.js +1 -0
- package/build/assets/{use-can-manage-org-profiles-CrG1km7R.js → use-can-manage-org-profiles-vBiP7rHF.js} +1 -1
- package/build/assets/use-cloud-current-user-id-BMq9gw93.js +1 -0
- package/build/assets/use-config-C959QcRC.js +1 -0
- package/build/assets/{use-create-conversation-Dsa4nzU9.js → use-create-conversation-CakhLF0X.js} +1 -1
- package/build/assets/{use-create-secret-BnxuAjDt.js → use-create-secret-RT4QtSS9.js} +1 -1
- package/build/assets/use-llm-profiles-CdaxVpyY.js +1 -0
- package/build/assets/use-onboarding-completion-CJU4MNAY.js +1 -0
- package/build/assets/use-plugins-marketplace-D-93nqtJ.js +1 -0
- package/build/assets/{use-save-agent-profile-2Mk9ZX37.js → use-save-agent-profile-DGJ3-u0M.js} +1 -1
- package/build/assets/{use-save-settings-BRh9p_fv.js → use-save-settings--C6LZXfI.js} +1 -1
- package/build/assets/use-settings-DIAo5KFk.js +1 -0
- package/build/assets/use-settings-nav-items-DPK5sxvR.js +16 -0
- package/build/assets/use-skills-D_mLhYNa.js +1 -0
- package/build/assets/use-tracking-COqZLobY.js +1 -0
- package/build/assets/{use-user-conversation-GnGhs_Ob.js → use-user-conversation-DhcAEyJX.js} +1 -1
- package/build/assets/{useMutation-DyQXZFEF.js → useMutation-DHLpZ6J0.js} +1 -1
- package/build/assets/utils-DsWhWn2F.js +1 -0
- package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~g4xrpilz-CJ7amdtQ.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~g4xrpilz-CYPMCkX1.js} +1 -1
- package/build/assets/{vendor~files-tab-vm3f67Pz.js → vendor~files-tab-atebg0iG.js} +1 -1
- package/build/assets/{vendor~index-home~home~conversation-panel~conversation-kX4LrGQC.js → vendor~index-home~home~conversation-panel~conversation-G9DMrR7m.js} +1 -1
- package/build/assets/{vendor~root-layout~conversation-panel~conversation-DpA6fir3.js → vendor~root-layout~conversation-panel~conversation-DEhW1h3I.js} +1 -1
- package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation-EShpjxey.js → vendor~root-layout~index-home~home~conversation-panel~conversation-Acmf1sKj.js} +1 -1
- package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-DD_UNO02.js → vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-CmzCY1BT.js} +1 -1
- package/build/assets/{vendor~root-layout~index-home~home~mcp~automations-list~onboarding-modal-Be63Q2Ln.js → vendor~root-layout~index-home~home~mcp~automations-list~onboarding-modal-C7Yh77RT.js} +1 -1
- package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~bi2r7bko-Bw4AuHN1.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~bi2r7bko-VFnwct3q.js} +1 -1
- package/build/assets/{verification-settings-CaCzojbc.js → verification-settings-Vr5cN4md.js} +1 -1
- package/build/assets/x-Dx_JbpHA.js +1 -0
- package/build/favicon-16x16.png +0 -0
- package/build/favicon-32x32.png +0 -0
- package/build/favicon.ico +0 -0
- package/build/index.html +4 -4
- package/build/locales/ar/openhands.json +2 -0
- package/build/locales/ca/openhands.json +2 -0
- package/build/locales/de/openhands.json +2 -0
- package/build/locales/en/openhands.json +2 -0
- package/build/locales/es/openhands.json +2 -0
- package/build/locales/fr/openhands.json +2 -0
- package/build/locales/it/openhands.json +2 -0
- package/build/locales/ja/openhands.json +2 -0
- package/build/locales/ko-KR/openhands.json +2 -0
- package/build/locales/no/openhands.json +2 -0
- package/build/locales/pt/openhands.json +2 -0
- package/build/locales/tr/openhands.json +2 -0
- package/build/locales/uk/openhands.json +2 -0
- package/build/locales/zh-CN/openhands.json +2 -0
- package/build/locales/zh-TW/openhands.json +2 -0
- package/config/defaults.json +4 -4
- package/dist/api/agent-server-adapter.cjs +1 -1
- package/dist/api/agent-server-adapter.cjs.map +1 -1
- package/dist/api/agent-server-adapter.js +7 -7
- package/dist/api/agent-server-adapter.js.map +1 -1
- package/dist/api/automation-service/automation-service.api.cjs +1 -1
- package/dist/api/automation-service/automation-service.api.cjs.map +1 -1
- package/dist/api/automation-service/automation-service.api.js +95 -95
- package/dist/api/automation-service/automation-service.api.js.map +1 -1
- package/dist/api/backend-registry/storage.cjs +1 -1
- package/dist/api/backend-registry/storage.cjs.map +1 -1
- package/dist/api/backend-registry/storage.js +1 -1
- package/dist/api/backend-registry/storage.js.map +1 -1
- package/dist/api/backend-registry/types.d.ts +2 -0
- package/dist/api/cloud/conversation-service.api.cjs +1 -1
- package/dist/api/cloud/conversation-service.api.js +22 -22
- package/dist/api/mcp-service/mcp-service.api.cjs +1 -1
- package/dist/api/mcp-service/mcp-service.api.cjs.map +1 -1
- package/dist/api/mcp-service/mcp-service.api.js +19 -19
- package/dist/api/mcp-service/mcp-service.api.js.map +1 -1
- package/dist/api/settings-service/settings-service.api.cjs +1 -1
- package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
- package/dist/api/settings-service/settings-service.api.js +26 -12
- package/dist/api/settings-service/settings-service.api.js.map +1 -1
- package/dist/components/features/mcp-page/custom-server-editor.cjs +1 -1
- package/dist/components/features/mcp-page/custom-server-editor.js +16 -16
- package/dist/components/features/mcp-page/installed-server-card.cjs +1 -1
- package/dist/components/features/mcp-page/installed-server-card.cjs.map +1 -1
- package/dist/components/features/mcp-page/installed-server-card.d.ts +2 -2
- package/dist/components/features/mcp-page/installed-server-card.js +6 -7
- package/dist/components/features/mcp-page/installed-server-card.js.map +1 -1
- package/dist/components/features/mcp-page/installed-servers-section.cjs +1 -1
- package/dist/components/features/mcp-page/installed-servers-section.cjs.map +1 -1
- package/dist/components/features/mcp-page/installed-servers-section.d.ts +2 -2
- package/dist/components/features/mcp-page/installed-servers-section.js +2 -2
- package/dist/components/features/mcp-page/installed-servers-section.js.map +1 -1
- package/dist/components/features/mcp-page/mcp-server-health.cjs +1 -1
- package/dist/components/features/mcp-page/mcp-server-health.js +4 -4
- package/dist/components/features/settings/mcp-settings/mcp-server-form.cjs +1 -1
- package/dist/components/features/settings/mcp-settings/mcp-server-form.cjs.map +1 -1
- package/dist/components/features/settings/mcp-settings/mcp-server-form.js +2 -1
- package/dist/components/features/settings/mcp-settings/mcp-server-form.js.map +1 -1
- package/dist/config/defaults.cjs +1 -1
- package/dist/config/defaults.cjs.map +1 -1
- package/dist/config/defaults.js +3 -3
- package/dist/config/defaults.js.map +1 -1
- package/dist/contexts/active-backend-context.cjs +1 -1
- package/dist/contexts/active-backend-context.cjs.map +1 -1
- package/dist/contexts/active-backend-context.d.ts +3 -2
- package/dist/contexts/active-backend-context.js +55 -42
- package/dist/contexts/active-backend-context.js.map +1 -1
- package/dist/favicon-16x16.png +0 -0
- package/dist/favicon-32x32.png +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/hooks/mutation/use-update-mcp-server.cjs +1 -1
- package/dist/hooks/mutation/use-update-mcp-server.js +5 -5
- package/dist/hooks/query/use-cloud-current-user-id.cjs +1 -1
- package/dist/hooks/query/use-cloud-current-user-id.cjs.map +1 -1
- package/dist/hooks/query/use-cloud-current-user-id.js +7 -5
- package/dist/hooks/query/use-cloud-current-user-id.js.map +1 -1
- package/dist/hooks/query/use-cloud-organizations.cjs +1 -1
- package/dist/hooks/query/use-cloud-organizations.cjs.map +1 -1
- package/dist/hooks/query/use-cloud-organizations.js +5 -1
- package/dist/hooks/query/use-cloud-organizations.js.map +1 -1
- package/dist/hooks/use-can-manage-org-profiles.cjs +1 -1
- package/dist/hooks/use-can-manage-org-profiles.cjs.map +1 -1
- package/dist/hooks/use-can-manage-org-profiles.js +2 -1
- package/dist/hooks/use-can-manage-org-profiles.js.map +1 -1
- package/dist/hooks/use-tracking.cjs +1 -1
- package/dist/hooks/use-tracking.js +9 -9
- package/dist/i18n/declaration.cjs +1 -1
- package/dist/i18n/declaration.cjs.map +1 -1
- package/dist/i18n/declaration.d.ts +2 -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 +34 -0
- package/dist/i18n/translation.js.map +1 -1
- package/dist/locales/ar/openhands.json +2 -0
- package/dist/locales/ca/openhands.json +2 -0
- package/dist/locales/de/openhands.json +2 -0
- package/dist/locales/en/openhands.json +2 -0
- package/dist/locales/es/openhands.json +2 -0
- package/dist/locales/fr/openhands.json +2 -0
- package/dist/locales/it/openhands.json +2 -0
- package/dist/locales/ja/openhands.json +2 -0
- package/dist/locales/ko-KR/openhands.json +2 -0
- package/dist/locales/no/openhands.json +2 -0
- package/dist/locales/pt/openhands.json +2 -0
- package/dist/locales/tr/openhands.json +2 -0
- package/dist/locales/uk/openhands.json +2 -0
- package/dist/locales/zh-CN/openhands.json +2 -0
- package/dist/locales/zh-TW/openhands.json +2 -0
- package/dist/node_modules/@openhands/typescript-client/dist/client/settings-client.cjs +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/client/settings-client.cjs.map +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/client/settings-client.js +9 -0
- package/dist/node_modules/@openhands/typescript-client/dist/client/settings-client.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/app-settings.cjs +1 -1
- package/dist/routes/app-settings.js +5 -5
- package/dist/routes/mcp.cjs +1 -1
- package/dist/routes/mcp.cjs.map +1 -1
- package/dist/routes/mcp.js +73 -82
- package/dist/routes/mcp.js.map +1 -1
- package/dist/services/telemetry.cjs +1 -1
- package/dist/services/telemetry.cjs.map +1 -1
- package/dist/services/telemetry.d.ts +8 -0
- package/dist/services/telemetry.js +163 -116
- package/dist/services/telemetry.js.map +1 -1
- package/dist/types/mcp-server.d.ts +5 -0
- package/dist/types/settings.d.ts +3 -0
- package/dist/utils/mcp-config.cjs +1 -1
- package/dist/utils/mcp-config.cjs.map +1 -1
- package/dist/utils/mcp-config.d.ts +8 -0
- package/dist/utils/mcp-config.js +25 -20
- package/dist/utils/mcp-config.js.map +1 -1
- package/dist/utils/mcp-installed-servers.cjs +1 -1
- package/dist/utils/mcp-installed-servers.cjs.map +1 -1
- package/dist/utils/mcp-installed-servers.js +6 -3
- package/dist/utils/mcp-installed-servers.js.map +1 -1
- package/package.json +2 -2
- package/scripts/check-sdk-version-sync.mjs +6 -6
- package/scripts/dev-safe.mjs +2 -2
- package/build/assets/active-backend-context-BlZPzZNU.js +0 -1
- package/build/assets/agent-server-conversation-service.api-Btmi4q6d.js +0 -41
- package/build/assets/agent-settings-KQs-occT.js +0 -1
- package/build/assets/api-key-entry-screen-BhR24gkM.js +0 -1
- package/build/assets/app-settings-Bs4sY8WP.js +0 -1
- package/build/assets/automation-detail-CwDnJbYR.js +0 -2
- package/build/assets/backend-form-modal-Ce-KRHOJ.js +0 -1
- package/build/assets/check-BMOG005H.js +0 -1
- package/build/assets/chevron-down-CV1zZP_K.js +0 -1
- package/build/assets/circle-check-lvN_jFN5.js +0 -1
- package/build/assets/client-source-1v-kWLNU.js +0 -1
- package/build/assets/cloud-funnel-analytics-CasMM7EO.js +0 -1
- package/build/assets/conversation-C25pc2dT.js +0 -1
- package/build/assets/conversation-CObLYSJd.js +0 -90
- package/build/assets/conversation-panel-C25pc2dT.js +0 -1
- package/build/assets/conversation-state-store-DTq4hFoP.js +0 -1
- package/build/assets/extensions-hub-BKftOQ-X.js +0 -1
- package/build/assets/extensions-navigation-DF24q1H1.js +0 -1
- package/build/assets/files-tab-SAiFCnS5.js +0 -1
- package/build/assets/git-provider-icon-BREx-yUX.js +0 -1
- package/build/assets/home-EPNFgzUh.js +0 -1
- package/build/assets/index-home-EPNFgzUh.js +0 -1
- package/build/assets/install-server-modal-BI8yUfmr.js +0 -1
- package/build/assets/llm-settings-BXk5_Pac.js +0 -1
- package/build/assets/llm-settings-C1vpgWam.js +0 -1
- package/build/assets/loader-circle-6zlEYCJw.js +0 -1
- package/build/assets/manage-backends-modal-DhflyGTf.js +0 -1
- package/build/assets/manifest-8082e879.js +0 -1
- package/build/assets/markdown-renderer-CvOO0YIr.js +0 -8
- package/build/assets/mcp-CxLPda-W.js +0 -9
- package/build/assets/onboarding-modal-DdvE6rzw.js +0 -1
- package/build/assets/organization-service.api-CT2OQlLz.js +0 -1
- package/build/assets/plugins-management-service-DVkoM3eA.js +0 -1
- package/build/assets/plus-vIbGD459.js +0 -1
- package/build/assets/profiles-service.api-CBafmAPO.js +0 -1
- package/build/assets/providers-9UzRB9mT.js +0 -1
- package/build/assets/proxy-C79PCMjo.js +0 -1
- package/build/assets/root-Sj7W28yE.js +0 -2
- package/build/assets/root-layout-DbhKVoc7.js +0 -3
- package/build/assets/schema-field-CV6bFnoL.js +0 -1
- package/build/assets/search-sKuYKllf.js +0 -1
- package/build/assets/secrets-service-BjK-bPN2.js +0 -1
- package/build/assets/settings-Bj5Oo45A.js +0 -1
- package/build/assets/settings-service.api-DryhKdSr.js +0 -1
- package/build/assets/telemetry-cmCL3o6Z.js +0 -2
- package/build/assets/use-activate-agent-profile-DH33MvVj.js +0 -1
- package/build/assets/use-agent-profiles-Dmd_4H8q.js +0 -1
- package/build/assets/use-cloud-current-user-id-B6DP3s47.js +0 -1
- package/build/assets/use-config-Csy8EAP9.js +0 -1
- package/build/assets/use-llm-profiles-BMQoc612.js +0 -1
- package/build/assets/use-onboarding-completion-B-2BTS-y.js +0 -1
- package/build/assets/use-plugins-marketplace-PBc_XNz_.js +0 -1
- package/build/assets/use-settings-DBI44RnH.js +0 -1
- package/build/assets/use-settings-nav-items-Cva28C-c.js +0 -16
- package/build/assets/use-skills-RQsCtdzN.js +0 -1
- package/build/assets/use-tracking-CN1nIMnY.js +0 -1
- package/build/assets/utils-qjs_KuS4.js +0 -1
- package/build/assets/x-EjvKGawc.js +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../../node_modules/react-i18next/dist/es/useTranslation.cjs"),n=require("../../../i18n/declaration.cjs"),r=require("../../../utils/utils.cjs"),i=require("../../../utils/retrieve-axios-error-message.cjs"),a=require("../../../utils/custom-toast-handlers.cjs"),o=require("../../../contexts/active-backend-context.cjs"),s=require("../../../
|
|
1
|
+
const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../../node_modules/react-i18next/dist/es/useTranslation.cjs"),n=require("../../../i18n/declaration.cjs"),r=require("../../../utils/utils.cjs"),i=require("../../../utils/retrieve-axios-error-message.cjs"),a=require("../../../utils/custom-toast-handlers.cjs"),o=require("../../../contexts/active-backend-context.cjs"),s=require("../../../hooks/mutation/use-update-mcp-server.cjs"),c=require("../../../api/mcp-health/probe-mcp-server-health.cjs"),l=require("../../../hooks/use-mcp-server-health.cjs"),u=require("../../../utils/mcp-test-error-message.cjs");let d=require("react");d=e.__toESM(d,1);let f=require("react/jsx-runtime");var p={unchecked:`bg-[var(--oh-text-tertiary)]`,checking:`bg-[var(--oh-interactive-selected)] animate-pulse`,healthy:`bg-[var(--oh-status-success)]`,failed:`bg-red-500`},m=`text-xs text-[var(--oh-muted)] underline transition-colors hover:text-white disabled:cursor-not-allowed disabled:opacity-60`;function h(e){return e.status===`healthy`?e.verification===`verified`?`healthy`:`healthy-connectivity`:e.status}function g(e,t){switch(t.status){case`checking`:return e(n.I18nKey.MCP$HEALTH_STATUS_CHECKING);case`healthy`:return t.verification===`verified`?e(n.I18nKey.MCP$HEALTH_STATUS_VERIFIED,{count:t.toolCount}):e(n.I18nKey.MCP$HEALTH_STATUS_CONNECTIVITY_ONLY);case`failed`:return u.makeMcpTestErrorMessage(e,t.kind,t.error);default:return e(n.I18nKey.MCP$HEALTH_STATUS_UNCHECKED)}}function _({server:e,catalog:u,onEdit:d}){let{t:_}=t.useTranslation(`openhands`),{backend:v}=o.useActiveBackend(),{health:y,probe:b,reauthorize:x}=l.useMcpServerHealth(e),{mutate:S}=s.useUpdateMcpServer();if(v.kind===`cloud`)return null;let C=y.status===`checking`,w=y.status===`failed`,T=g(_,y),E=async()=>{let t=await x();if(!t?.ok||!t.oauth_state||e.auth?.strategy!==`oauth2`)return;let r={...e,auth:{...e.auth,state:t.oauth_state}};S({serverId:e.id,server:r},{onSuccess:()=>c.seedMcpServerHealth(r,t,[]),onError:e=>a.displayErrorToast(i.retrieveAxiosErrorMessage(e)||_(n.I18nKey.ERROR$GENERIC))})};return(0,f.jsxs)(`div`,{"data-testid":`mcp-server-health-${e.id}`,role:`presentation`,className:`flex flex-col gap-1.5`,onClick:e=>e.stopPropagation(),onKeyDown:e=>e.stopPropagation(),children:[(0,f.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,f.jsx)(`span`,{"data-testid":`mcp-health-dot`,"data-status":h(y),"aria-hidden":`true`,className:r.cn(`inline-block h-2 w-2 shrink-0 rounded-full`,p[y.status])}),(0,f.jsx)(`p`,{"data-testid":`mcp-health-label-${e.id}`,className:r.cn(`line-clamp-2 break-words text-xs`,w?`text-red-500`:`text-tertiary-alt`),title:T,children:T})]}),y.status===`healthy`&&y.verification===`connectivity-only`?(0,f.jsx)(`p`,{className:`text-xs text-tertiary-light`,children:_(n.I18nKey.MCP$HEALTH_CONNECTIVITY_ONLY_HINT)}):null,(0,f.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,f.jsx)(`button`,{type:`button`,"data-testid":`mcp-health-probe-${e.id}`,className:m,onClick:()=>void b(),disabled:C,children:_(w?n.I18nKey.MCP$HEALTH_RETRY:n.I18nKey.MCP$TEST_BUTTON)}),w&&y.kind===`credentials`?(0,f.jsx)(`button`,{type:`button`,"data-testid":`mcp-health-update-credentials-${e.id}`,className:m,onClick:d,children:_(n.I18nKey.MCP$HEALTH_UPDATE_CREDENTIALS)}):null,w&&e.auth?.strategy===`oauth2`?(0,f.jsx)(`button`,{type:`button`,"data-testid":`mcp-health-reauthorize-${e.id}`,className:m,onClick:()=>void E(),children:_(n.I18nKey.MCP$HEALTH_REAUTHORIZE)}):null,w&&u?.docsUrl?(0,f.jsx)(`a`,{href:u.docsUrl,target:`_blank`,rel:`noreferrer`,className:m,children:_(n.I18nKey.MCP$VIEW_DOCS)}):null]})]})}exports.McpServerHealthSection=_;
|
|
2
2
|
//# sourceMappingURL=mcp-server-health.cjs.map
|
|
@@ -4,8 +4,8 @@ import { cn as n } from "../../../utils/utils.js";
|
|
|
4
4
|
import { retrieveAxiosErrorMessage as r } from "../../../utils/retrieve-axios-error-message.js";
|
|
5
5
|
import { displayErrorToast as i } from "../../../utils/custom-toast-handlers.js";
|
|
6
6
|
import { useActiveBackend as a } from "../../../contexts/active-backend-context.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { useUpdateMcpServer as o } from "../../../hooks/mutation/use-update-mcp-server.js";
|
|
8
|
+
import { seedMcpServerHealth as s } from "../../../api/mcp-health/probe-mcp-server-health.js";
|
|
9
9
|
import { useMcpServerHealth as c } from "../../../hooks/use-mcp-server-health.js";
|
|
10
10
|
import { makeMcpTestErrorMessage as l } from "../../../utils/mcp-test-error-message.js";
|
|
11
11
|
import "react";
|
|
@@ -29,7 +29,7 @@ function h(e, n) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
function g({ server: l, catalog: g, onEdit: _ }) {
|
|
32
|
-
let { t: v } = e("openhands"), { backend: y } = a(), { health: b, probe: x, reauthorize: S } = c(l), { mutate: C } =
|
|
32
|
+
let { t: v } = e("openhands"), { backend: y } = a(), { health: b, probe: x, reauthorize: S } = c(l), { mutate: C } = o();
|
|
33
33
|
if (y.kind === "cloud") return null;
|
|
34
34
|
let w = b.status === "checking", T = b.status === "failed", E = h(v, b), D = async () => {
|
|
35
35
|
let e = await S();
|
|
@@ -45,7 +45,7 @@ function g({ server: l, catalog: g, onEdit: _ }) {
|
|
|
45
45
|
serverId: l.id,
|
|
46
46
|
server: n
|
|
47
47
|
}, {
|
|
48
|
-
onSuccess: () =>
|
|
48
|
+
onSuccess: () => s(n, e, []),
|
|
49
49
|
onError: (e) => i(r(e) || v(t.ERROR$GENERIC))
|
|
50
50
|
});
|
|
51
51
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
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("../../../../node_modules/lucide-react/dist/esm/icons/trash-2.cjs"),i=require("../../../../utils/utils.cjs"),a=require("../../../../utils/mcp-server-name.cjs"),o=require("../../../../utils/form-control-classes.cjs"),s=require("../brand-button.cjs"),c=require("../optional-tag.cjs"),l=require("../settings-input.cjs"),u=require("../settings-dropdown-input.cjs");let d=require("react");d=e.__toESM(d,1);let f=require("react/jsx-runtime");function p({mode:e,server:p,existingServers:m,onSubmit:h,onCancel:g,onDelete:_,isActionDisabled:v=!1,onTest:y,isTestPending:b=!1,testMessage:x=null}){let{t:S}=t.useTranslation(`openhands`),[C,w]=d.default.useState(p?.type||`sse`),[T,E]=d.default.useState(()=>p?.auth?.strategy===`oauth2`?`oauth2`:p?.auth?.strategy===`header`?`header`:p?.auth?.strategy===`bearer`||p?.auth?.strategy===`api_key`?`bearer`:`none`),[D,O]=d.default.useState(()=>p?.auth?.strategy===`oauth2`?p.auth.authentication?.client_auth_method??`auto`:`auto`),[k,A]=d.default.useState(null),j=d.default.useRef(null),M=[{key:`sse`,label:S(n.I18nKey.SETTINGS$MCP_SERVER_TYPE_SSE)},{key:`stdio`,label:S(n.I18nKey.SETTINGS$MCP_SERVER_TYPE_STDIO)},{key:`shttp`,label:S(n.I18nKey.SETTINGS$MCP_SERVER_TYPE_SHTTP)}],N=[{key:`none`,label:S(n.I18nKey.SETTINGS$MCP_AUTH_MODE_NONE)},{key:`bearer`,label:S(n.I18nKey.SETTINGS$MCP_AUTH_MODE_BEARER)},{key:`header`,label:S(n.I18nKey.SETTINGS$MCP_AUTH_MODE_HEADER)},{key:`oauth2`,label:S(n.I18nKey.SETTINGS$MCP_AUTH_MODE_OAUTH)}],P=[{key:`auto`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_AUTO)},{key:`none`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_NONE)},{key:`client_secret_post`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_POST)},{key:`client_secret_basic`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_BASIC)},{key:`private_key_jwt`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_PRIVATE_KEY_JWT)}],F=e=>{if(!e)return S(n.I18nKey.SETTINGS$MCP_ERROR_URL_REQUIRED);try{let t=new URL(e);if(![`http:`,`https:`].includes(t.protocol))return S(n.I18nKey.SETTINGS$MCP_ERROR_URL_INVALID_PROTOCOL)}catch{return S(n.I18nKey.SETTINGS$MCP_ERROR_URL_INVALID)}return null},I=e=>e?a.isValidMcpServerName(e)?null:S(n.I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID):S(n.I18nKey.SETTINGS$MCP_ERROR_NAME_REQUIRED),L=t=>!m||!(e===`add`||e===`edit`&&p?.name!==t)?null:m.filter(e=>e.type===`stdio`).map(e=>e.name).filter(Boolean).includes(t)?S(n.I18nKey.SETTINGS$MCP_ERROR_NAME_DUPLICATE):null,R=e=>e?e.includes(` `)?S(n.I18nKey.SETTINGS$MCP_ERROR_COMMAND_NO_SPACES):null:S(n.I18nKey.SETTINGS$MCP_ERROR_COMMAND_REQUIRED),z=t=>{if(!m)return null;let r=p?.url;return(e===`add`||e===`edit`&&r!==t)&&m.some(e=>(e.type===`sse`||e.type===`shttp`)&&e.url===t)?S(n.I18nKey.SETTINGS$MCP_ERROR_URL_DUPLICATE):null},B=e=>{if(!e.trim())return null;let t=e.split(`
|
|
2
2
|
`);for(let e=0;e<t.length;e+=1){let r=t[e].trim();if(r){let e=r.indexOf(`=`);if(e===-1||!r.substring(0,e).trim())return S(n.I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT)}}return null},V=e=>{if(T===`header`){let t=e.get(`headers`)?.toString()||``;return t.trim()?B(t):S(n.I18nKey.SETTINGS$MCP_ERROR_HEADER_REQUIRED)}if(T===`oauth2`){let t=e.get(`oauth_client_id`)?.toString().trim();if(e.get(`oauth_client_secret`)?.toString().trim()&&!t)return S(n.I18nKey.SETTINGS$MCP_ERROR_OAUTH_SECRET_REQUIRES_ID)}return null},H=e=>{if(!e.trim())return null;let t=parseInt(e.trim(),10);return Number.isNaN(t)?S(n.I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_INVALID_NUMBER):t<=0?S(n.I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_POSITIVE):t>3600?S(n.I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_MAX_EXCEEDED):null},U=e=>{let t=e.get(`name`)?.toString().trim()||``,n=e.get(`command`)?.toString().trim()||``,r=e.get(`env`)?.toString()||``;return I(t)||L(t)||R(n)||B(r)||null},W=e=>{if(C===`sse`||C===`shttp`){let t=e.get(`url`)?.toString().trim()||``,r=F(t);if(r)return r;let i=z(t);if(i)return i;let o=e.get(`name`)?.toString().trim()||``;if(o&&!a.isValidMcpServerName(o))return S(n.I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);if(C===`shttp`){let t=H(e.get(`timeout`)?.toString()||``);if(t)return t}return V(e)}return C===`stdio`?U(e):null},G=e=>{let t={},n=e.trim();if(!n)return t;for(let e of n.split(`
|
|
3
3
|
`)){let n=e.trim(),r=n.indexOf(`=`),i=r>=0?n.substring(0,r).trim():``;n&&r!==-1&&i&&(t[i]=n.substring(r+1).trim())}return t},K=e=>e?Object.entries(e).map(([e,t])=>`${e}=${t}`).join(`
|
|
4
|
-
`):``,q=e=>e?.strategy===`bearer`||e?.strategy===`api_key`?e.value??``:``,J=e=>e?.strategy===`header`?K(e.headers):``,Y=p?.auth?.strategy===`oauth2`?p.auth.authentication:void 0,X=p?.auth?.strategy===`oauth2`?p.auth.state:void 0,Z=e=>{if(T===`none`)return;if(T===`bearer`){let t=e.get(`api_key`)?.toString().trim();return t?p?.auth?.strategy===`api_key`?{...p.auth,value:t}:{strategy:`bearer`,value:t}:void 0}if(T===`header`)return{strategy:`header`,headers:G(e.get(`headers`)?.toString()||``)};let t=e.get(`oauth_scopes`)?.toString().trim(),n=e.get(`oauth_client_id`)?.toString().trim(),r=e.get(`oauth_client_secret`)?.toString().trim();return{strategy:`oauth2`,authentication:{type:`oauth`,...D!==`auto`&&{client_auth_method:D},...t&&{scopes:t},...n&&{client_id:n},...r&&{client_secret:r}},...X&&{state:X}}},Q=e=>{let t={id:p?.id||`${C}-${Date.now()}`,type:C};if(C===`sse`||C===`shttp`){let n=e.get(`name`)?.toString().trim(),r=e.get(`url`)?.toString().trim(),i=e.get(`timeout`)?.toString().trim(),a=Z(e),o={...t,...n&&{name:n},url:r,...a&&{auth:a}};if(C===`shttp`&&i){let e=parseInt(i,10);Number.isNaN(e)||(o.timeout=e)}return o}let n=e.get(`name`)?.toString().trim(),r=e.get(`command`)?.toString().trim(),i=e.get(`args`)?.toString().trim(),a=e.get(`env`)?.toString().trim(),o=i?i.split(`
|
|
4
|
+
`):``,q=e=>e?.strategy===`bearer`||e?.strategy===`api_key`?e.value??``:``,J=e=>e?.strategy===`header`?K(e.headers):``,Y=p?.auth?.strategy===`oauth2`?p.auth.authentication:void 0,X=p?.auth?.strategy===`oauth2`?p.auth.state:void 0,Z=e=>{if(T===`none`)return;if(T===`bearer`){let t=e.get(`api_key`)?.toString().trim();return t?p?.auth?.strategy===`api_key`?{...p.auth,value:t}:{strategy:`bearer`,value:t}:void 0}if(T===`header`)return{strategy:`header`,headers:G(e.get(`headers`)?.toString()||``)};let t=e.get(`oauth_scopes`)?.toString().trim(),n=e.get(`oauth_client_id`)?.toString().trim(),r=e.get(`oauth_client_secret`)?.toString().trim();return{strategy:`oauth2`,authentication:{type:`oauth`,...D!==`auto`&&{client_auth_method:D},...t&&{scopes:t},...n&&{client_id:n},...r&&{client_secret:r}},...X&&{state:X}}},Q=e=>{let t={id:p?.id||`${C}-${Date.now()}`,type:C,...p?.enabled===!1&&{enabled:!1}};if(C===`sse`||C===`shttp`){let n=e.get(`name`)?.toString().trim(),r=e.get(`url`)?.toString().trim(),i=e.get(`timeout`)?.toString().trim(),a=Z(e),o={...t,...n&&{name:n},url:r,...a&&{auth:a}};if(C===`shttp`&&i){let e=parseInt(i,10);Number.isNaN(e)||(o.timeout=e)}return o}let n=e.get(`name`)?.toString().trim(),r=e.get(`command`)?.toString().trim(),i=e.get(`args`)?.toString().trim(),a=e.get(`env`)?.toString().trim(),o=i?i.split(`
|
|
5
5
|
`).map(e=>e.trim()).filter(Boolean):[],s=G(a||``);return{...t,name:n,command:r,...o.length>0&&{args:o},...Object.keys(s).length>0&&{env:s}}};return(0,f.jsxs)(`form`,{ref:j,"data-testid":e===`add`?`add-mcp-server-form`:`edit-mcp-server-form`,onSubmit:e=>{e.preventDefault(),A(null);let t=new FormData(e.currentTarget),n=W(t);if(n){A(n);return}h(Q(t))},className:`flex flex-col items-start gap-6`,noValidate:!0,children:[e===`add`&&(0,f.jsx)(u.SettingsDropdownInput,{testId:`server-type-dropdown`,name:`server-type`,label:S(n.I18nKey.SETTINGS$MCP_SERVER_TYPE),items:M,selectedKey:C,onSelectionChange:e=>w(e),onInputChange:()=>{},isClearable:!1,allowsCustomValue:!1,required:!0,wrapperClassName:`w-full min-w-0`}),k&&(0,f.jsx)(`p`,{className:`text-red-500 text-sm`,children:k}),(C===`sse`||C===`shttp`)&&(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(l.SettingsInput,{testId:`server-name-input`,name:`name`,type:`text`,label:S(n.I18nKey.SETTINGS$MCP_SERVER_NAME),className:`w-full min-w-0`,showOptionalTag:!0,defaultValue:p?.name||``,placeholder:`my_search_server`,pattern:a.MCP_SERVER_NAME_PATTERN.source}),(0,f.jsx)(l.SettingsInput,{testId:`url-input`,name:`url`,type:`url`,label:S(n.I18nKey.SETTINGS$MCP_URL),className:`w-full min-w-0`,required:!0,defaultValue:p?.url||``,placeholder:`https://api.example.com`}),(0,f.jsx)(u.SettingsDropdownInput,{testId:`auth-mode-dropdown`,name:`auth-mode`,label:S(n.I18nKey.SETTINGS$MCP_AUTHENTICATION),items:N,selectedKey:T,onSelectionChange:e=>E(e),onInputChange:()=>{},isClearable:!1,allowsCustomValue:!1,wrapperClassName:`w-full min-w-0`}),T===`bearer`&&(0,f.jsx)(l.SettingsInput,{testId:`api-key-input`,name:`api_key`,type:`password`,label:S(n.I18nKey.SETTINGS$MCP_API_KEY),className:`w-full min-w-0`,required:!0,defaultValue:q(p?.auth),placeholder:S(n.I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER)}),T===`header`&&(0,f.jsxs)(`label`,{className:`flex flex-col gap-2.5 w-full min-w-0`,children:[(0,f.jsx)(`span`,{className:`text-sm`,children:S(n.I18nKey.SETTINGS$MCP_HEADERS)}),(0,f.jsx)(`textarea`,{"data-testid":`headers-input`,name:`headers`,rows:4,defaultValue:J(p?.auth),placeholder:S(n.I18nKey.SETTINGS$MCP_HEADERS_PLACEHOLDER),className:i.cn(o.formControlMultilineFieldClassName,`resize-none placeholder:italic`,`disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]`)})]}),T===`oauth2`&&(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(u.SettingsDropdownInput,{testId:`oauth-client-auth-method-dropdown`,name:`oauth_client_auth_method`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH),items:P,selectedKey:D,onSelectionChange:e=>O(e),onInputChange:()=>{},isClearable:!1,allowsCustomValue:!1,wrapperClassName:`w-full min-w-0`}),(0,f.jsx)(l.SettingsInput,{testId:`oauth-client-id-input`,name:`oauth_client_id`,type:`text`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID),className:`w-full min-w-0`,showOptionalTag:!0,defaultValue:Y?.client_id||``,placeholder:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID_PLACEHOLDER)}),(0,f.jsx)(l.SettingsInput,{testId:`oauth-client-secret-input`,name:`oauth_client_secret`,type:`password`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET),className:`w-full min-w-0`,showOptionalTag:!0,defaultValue:Y?.client_secret||``,placeholder:S(n.I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET_PLACEHOLDER)}),(0,f.jsx)(l.SettingsInput,{testId:`oauth-scopes-input`,name:`oauth_scopes`,type:`text`,label:S(n.I18nKey.SETTINGS$MCP_OAUTH_SCOPES),className:`w-full min-w-0`,showOptionalTag:!0,defaultValue:Array.isArray(Y?.scopes)?Y.scopes.join(` `):Y?.scopes||``,placeholder:S(n.I18nKey.SETTINGS$MCP_OAUTH_SCOPES_PLACEHOLDER)})]}),C===`shttp`&&(0,f.jsx)(l.SettingsInput,{testId:`timeout-input`,name:`timeout`,type:`number`,label:S(n.I18nKey.SETTINGS$MCP_TIMEOUT_LABEL),className:`w-full min-w-0`,showOptionalTag:!0,defaultValue:p?.timeout?.toString()||``,placeholder:`60`,min:1,max:3600})]}),C===`stdio`&&(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(l.SettingsInput,{testId:`name-input`,name:`name`,type:`text`,label:S(n.I18nKey.SETTINGS$MCP_NAME),className:`w-full min-w-0`,required:!0,defaultValue:p?.name||``,placeholder:`my_mcp_server`,pattern:a.MCP_SERVER_NAME_PATTERN.source}),(0,f.jsx)(l.SettingsInput,{testId:`command-input`,name:`command`,type:`text`,label:S(n.I18nKey.SETTINGS$MCP_COMMAND),className:`w-full min-w-0`,required:!0,defaultValue:p?.command||``,placeholder:`npx`}),(0,f.jsxs)(`label`,{className:`flex flex-col gap-2.5 w-full min-w-0`,children:[(0,f.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,f.jsx)(`span`,{className:`text-sm`,children:S(n.I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS)}),(0,f.jsx)(c.OptionalTag,{})]}),(0,f.jsx)(`textarea`,{"data-testid":`args-input`,name:`args`,rows:3,defaultValue:p?.args?.join(`
|
|
6
6
|
`)||``,placeholder:`arg1
|
|
7
7
|
arg2
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server-form.cjs","names":[],"sources":["../../../../../src/components/features/settings/mcp-settings/mcp-server-form.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Trash2 } from \"lucide-react\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { SettingsInput } from \"../settings-input\";\nimport { SettingsDropdownInput } from \"../settings-dropdown-input\";\nimport { BrandButton } from \"../brand-button\";\nimport { OptionalTag } from \"../optional-tag\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlMultilineFieldClassName } from \"#/utils/form-control-classes\";\nimport {\n isValidMcpServerName,\n MCP_SERVER_NAME_PATTERN,\n} from \"#/utils/mcp-server-name\";\nimport type {\n MCPAuthCredential,\n MCPAuthenticationConfig,\n MCPOAuthClientAuthMethod,\n} from \"#/types/mcp-auth\";\nimport type { MCPServerConfig } from \"#/types/mcp-server\";\n\ntype MCPServerType = \"sse\" | \"stdio\" | \"shttp\";\ntype RemoteAuthMode = \"none\" | \"bearer\" | \"header\" | \"oauth2\";\ntype OAuthClientAuthMethodOption = \"auto\" | MCPOAuthClientAuthMethod;\n\nexport interface TestMessage {\n ok: boolean;\n text: string;\n}\n\ninterface MCPServerFormProps {\n mode: \"add\" | \"edit\";\n server?: MCPServerConfig;\n existingServers?: MCPServerConfig[];\n onSubmit: (server: MCPServerConfig) => void;\n onCancel: () => void;\n onDelete?: () => void;\n isActionDisabled?: boolean;\n onTest?: (server: MCPServerConfig) => void;\n isTestPending?: boolean;\n testMessage?: TestMessage | null;\n}\n\nexport function MCPServerForm({\n mode,\n server,\n existingServers,\n onSubmit,\n onCancel,\n onDelete,\n isActionDisabled = false,\n onTest,\n isTestPending = false,\n testMessage = null,\n}: MCPServerFormProps) {\n const { t } = useTranslation(\"openhands\");\n const [serverType, setServerType] = React.useState<MCPServerType>(\n server?.type || \"sse\",\n );\n const [authMode, setAuthMode] = React.useState<RemoteAuthMode>(() => {\n if (server?.auth?.strategy === \"oauth2\") return \"oauth2\";\n if (server?.auth?.strategy === \"header\") return \"header\";\n if (\n server?.auth?.strategy === \"bearer\" ||\n server?.auth?.strategy === \"api_key\"\n ) {\n return \"bearer\";\n }\n return \"none\";\n });\n const [oauthClientAuthMethod, setOAuthClientAuthMethod] =\n React.useState<OAuthClientAuthMethodOption>(() =>\n server?.auth?.strategy === \"oauth2\"\n ? (server.auth.authentication?.client_auth_method ?? \"auto\")\n : \"auto\",\n );\n const [error, setError] = React.useState<string | null>(null);\n const formRef = React.useRef<HTMLFormElement>(null);\n\n const serverTypeOptions = [\n { key: \"sse\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_SSE) },\n { key: \"stdio\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_STDIO) },\n { key: \"shttp\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_SHTTP) },\n ];\n const authModeOptions = [\n { key: \"none\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_NONE) },\n { key: \"bearer\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_BEARER) },\n { key: \"header\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_HEADER) },\n { key: \"oauth2\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_OAUTH) },\n ];\n const oauthClientAuthMethodOptions = [\n { key: \"auto\", label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_AUTO) },\n { key: \"none\", label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_NONE) },\n {\n key: \"client_secret_post\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_POST),\n },\n {\n key: \"client_secret_basic\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_BASIC),\n },\n {\n key: \"private_key_jwt\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_PRIVATE_KEY_JWT),\n },\n ];\n\n const validateUrl = (url: string): string | null => {\n if (!url) return t(I18nKey.SETTINGS$MCP_ERROR_URL_REQUIRED);\n try {\n const urlObj = new URL(url);\n if (![\"http:\", \"https:\"].includes(urlObj.protocol)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_URL_INVALID_PROTOCOL);\n }\n } catch {\n return t(I18nKey.SETTINGS$MCP_ERROR_URL_INVALID);\n }\n return null;\n };\n\n const validateName = (name: string): string | null => {\n if (!name) return t(I18nKey.SETTINGS$MCP_ERROR_NAME_REQUIRED);\n if (!isValidMcpServerName(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);\n }\n return null;\n };\n\n const validateNameUniqueness = (name: string): string | null => {\n if (!existingServers) return null;\n const shouldCheckUniqueness =\n mode === \"add\" || (mode === \"edit\" && server?.name !== name);\n if (!shouldCheckUniqueness) return null;\n\n const existingStdioNames = existingServers\n .filter((s) => s.type === \"stdio\")\n .map((s) => s.name)\n .filter(Boolean);\n if (existingStdioNames.includes(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_DUPLICATE);\n }\n return null;\n };\n\n const validateCommand = (command: string): string | null => {\n if (!command) return t(I18nKey.SETTINGS$MCP_ERROR_COMMAND_REQUIRED);\n if (command.includes(\" \")) {\n return t(I18nKey.SETTINGS$MCP_ERROR_COMMAND_NO_SPACES);\n }\n return null;\n };\n\n const validateUrlUniqueness = (url: string): string | null => {\n if (!existingServers) return null;\n const originalUrl = server?.url;\n const changed = mode === \"add\" || (mode === \"edit\" && originalUrl !== url);\n if (!changed) return null;\n // For URL-based servers (sse/shttp), ensure URL is unique across both types\n const exists = existingServers.some(\n (s) => (s.type === \"sse\" || s.type === \"shttp\") && s.url === url,\n );\n if (exists) return t(I18nKey.SETTINGS$MCP_ERROR_URL_DUPLICATE);\n return null;\n };\n\n const validateEnvFormat = (envString: string): string | null => {\n if (!envString.trim()) return null;\n const lines = envString.split(\"\\n\");\n for (let i = 0; i < lines.length; i += 1) {\n const trimmed = lines[i].trim();\n if (trimmed) {\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) return t(I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT);\n const key = trimmed.substring(0, eq).trim();\n if (!key) return t(I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT);\n }\n }\n return null;\n };\n\n const validateRemoteAuth = (formData: FormData): string | null => {\n if (authMode === \"header\") {\n const headerString = formData.get(\"headers\")?.toString() || \"\";\n if (!headerString.trim())\n return t(I18nKey.SETTINGS$MCP_ERROR_HEADER_REQUIRED);\n return validateEnvFormat(headerString);\n }\n if (authMode === \"oauth2\") {\n const clientId = formData.get(\"oauth_client_id\")?.toString().trim();\n const clientSecret = formData\n .get(\"oauth_client_secret\")\n ?.toString()\n .trim();\n if (clientSecret && !clientId) {\n return t(I18nKey.SETTINGS$MCP_ERROR_OAUTH_SECRET_REQUIRES_ID);\n }\n }\n return null;\n };\n\n const validateTimeout = (timeoutStr: string): string | null => {\n if (!timeoutStr.trim()) return null; // Optional field\n\n const timeout = parseInt(timeoutStr.trim(), 10);\n if (Number.isNaN(timeout)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_INVALID_NUMBER);\n }\n if (timeout <= 0) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_POSITIVE);\n }\n if (timeout > 3600) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_MAX_EXCEEDED);\n }\n return null;\n };\n\n const validateStdioServer = (formData: FormData): string | null => {\n const name = formData.get(\"name\")?.toString().trim() || \"\";\n const command = formData.get(\"command\")?.toString().trim() || \"\";\n const envString = formData.get(\"env\")?.toString() || \"\";\n\n const nameError = validateName(name);\n if (nameError) return nameError;\n\n const uniquenessError = validateNameUniqueness(name);\n if (uniquenessError) return uniquenessError;\n\n const commandError = validateCommand(command);\n if (commandError) return commandError;\n\n // Validate environment variable format\n const envError = validateEnvFormat(envString);\n if (envError) return envError;\n\n return null;\n };\n\n const validateForm = (formData: FormData): string | null => {\n if (serverType === \"sse\" || serverType === \"shttp\") {\n const url = formData.get(\"url\")?.toString().trim() || \"\";\n const urlError = validateUrl(url);\n if (urlError) return urlError;\n const urlDupError = validateUrlUniqueness(url);\n if (urlDupError) return urlDupError;\n\n // The name is optional, but when provided it becomes the mcp_config\n // key (and the reference used in mcp_server_refs), so hold it to the\n // same safe-identifier rule as stdio names.\n const name = formData.get(\"name\")?.toString().trim() || \"\";\n if (name && !isValidMcpServerName(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);\n }\n\n // Validate timeout for SHTTP servers only\n if (serverType === \"shttp\") {\n const timeoutStr = formData.get(\"timeout\")?.toString() || \"\";\n const timeoutError = validateTimeout(timeoutStr);\n if (timeoutError) return timeoutError;\n }\n\n return validateRemoteAuth(formData);\n }\n\n if (serverType === \"stdio\") {\n return validateStdioServer(formData);\n }\n\n return null;\n };\n\n const parseEnvironmentVariables = (\n envString: string,\n ): Record<string, string> => {\n const env: Record<string, string> = {};\n const input = envString.trim();\n if (!input) return env;\n\n for (const line of input.split(\"\\n\")) {\n const trimmed = line.trim();\n const eq = trimmed.indexOf(\"=\");\n const key = eq >= 0 ? trimmed.substring(0, eq).trim() : \"\";\n if (trimmed && eq !== -1 && key) {\n env[key] = trimmed.substring(eq + 1).trim();\n }\n }\n return env;\n };\n\n const formatEnvironmentVariables = (\n env?: Record<string, string> | null,\n ): string => {\n if (!env) return \"\";\n return Object.entries(env)\n .map(([key, value]) => `${key}=${value}`)\n .join(\"\\n\");\n };\n\n const editableAuthValue = (auth: MCPAuthCredential | undefined): string => {\n if (auth?.strategy === \"bearer\" || auth?.strategy === \"api_key\") {\n return auth.value ?? \"\";\n }\n return \"\";\n };\n\n const editableHeaderValue = (auth: MCPAuthCredential | undefined): string => {\n if (auth?.strategy !== \"header\") return \"\";\n return formatEnvironmentVariables(auth.headers);\n };\n\n const oauthAuthentication =\n server?.auth?.strategy === \"oauth2\"\n ? server.auth.authentication\n : undefined;\n const oauthState =\n server?.auth?.strategy === \"oauth2\" ? server.auth.state : undefined;\n\n const authFromFormData = (\n formData: FormData,\n ): MCPAuthCredential | undefined => {\n if (authMode === \"none\") return undefined;\n if (authMode === \"bearer\") {\n const value = formData.get(\"api_key\")?.toString().trim();\n if (!value) return undefined;\n if (server?.auth?.strategy === \"api_key\") {\n return { ...server.auth, value };\n }\n return { strategy: \"bearer\", value };\n }\n if (authMode === \"header\") {\n const headers = parseEnvironmentVariables(\n formData.get(\"headers\")?.toString() || \"\",\n );\n return { strategy: \"header\", headers };\n }\n\n const scopes = formData.get(\"oauth_scopes\")?.toString().trim();\n const clientId = formData.get(\"oauth_client_id\")?.toString().trim();\n const clientSecret = formData.get(\"oauth_client_secret\")?.toString().trim();\n const authentication: MCPAuthenticationConfig = {\n type: \"oauth\",\n ...(oauthClientAuthMethod !== \"auto\" && {\n client_auth_method: oauthClientAuthMethod,\n }),\n ...(scopes && { scopes }),\n ...(clientId && { client_id: clientId }),\n ...(clientSecret && { client_secret: clientSecret }),\n };\n return {\n strategy: \"oauth2\",\n authentication,\n ...(oauthState && { state: oauthState }),\n };\n };\n\n const buildConfig = (formData: FormData): MCPServerConfig => {\n const baseConfig = {\n id: server?.id || `${serverType}-${Date.now()}`,\n type: serverType,\n };\n\n if (serverType === \"sse\" || serverType === \"shttp\") {\n const name = formData.get(\"name\")?.toString().trim();\n const url = formData.get(\"url\")?.toString().trim();\n const timeoutStr = formData.get(\"timeout\")?.toString().trim();\n const auth = authFromFormData(formData);\n\n const serverConfig: MCPServerConfig = {\n ...baseConfig,\n ...(name && { name }),\n url: url!,\n ...(auth && { auth }),\n };\n\n // Only add timeout for SHTTP servers\n if (serverType === \"shttp\" && timeoutStr) {\n const timeoutValue = parseInt(timeoutStr, 10);\n if (!Number.isNaN(timeoutValue)) {\n serverConfig.timeout = timeoutValue;\n }\n }\n\n return serverConfig;\n }\n\n // stdio\n const name = formData.get(\"name\")?.toString().trim();\n const command = formData.get(\"command\")?.toString().trim();\n const argsString = formData.get(\"args\")?.toString().trim();\n const envString = formData.get(\"env\")?.toString().trim();\n\n const args = argsString\n ? argsString\n .split(\"\\n\")\n .map((arg) => arg.trim())\n .filter(Boolean)\n : [];\n const env = parseEnvironmentVariables(envString || \"\");\n\n return {\n ...baseConfig,\n name: name!,\n command: command!,\n ...(args.length > 0 && { args }),\n ...(Object.keys(env).length > 0 && { env }),\n };\n };\n\n const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setError(null);\n\n const formData = new FormData(event.currentTarget);\n const validationError = validateForm(formData);\n\n if (validationError) {\n setError(validationError);\n return;\n }\n\n onSubmit(buildConfig(formData));\n };\n\n const handleTestClick = () => {\n if (!onTest || !formRef.current) return;\n setError(null);\n const formData = new FormData(formRef.current);\n const validationError = validateForm(formData);\n if (validationError) {\n setError(validationError);\n return;\n }\n onTest(buildConfig(formData));\n };\n\n const formTestId =\n mode === \"add\" ? \"add-mcp-server-form\" : \"edit-mcp-server-form\";\n\n return (\n <form\n ref={formRef}\n data-testid={formTestId}\n onSubmit={handleSubmit}\n className=\"flex flex-col items-start gap-6\"\n noValidate\n >\n {mode === \"add\" && (\n <SettingsDropdownInput\n testId=\"server-type-dropdown\"\n name=\"server-type\"\n label={t(I18nKey.SETTINGS$MCP_SERVER_TYPE)}\n items={serverTypeOptions}\n selectedKey={serverType}\n onSelectionChange={(key) => setServerType(key as MCPServerType)}\n onInputChange={() => {}} // Prevent input changes\n isClearable={false}\n allowsCustomValue={false}\n required\n wrapperClassName=\"w-full min-w-0\"\n />\n )}\n\n {error && <p className=\"text-red-500 text-sm\">{error}</p>}\n\n {(serverType === \"sse\" || serverType === \"shttp\") && (\n <>\n <SettingsInput\n testId=\"server-name-input\"\n name=\"name\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_SERVER_NAME)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={server?.name || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"my_search_server\"\n pattern={MCP_SERVER_NAME_PATTERN.source}\n />\n\n <SettingsInput\n testId=\"url-input\"\n name=\"url\"\n type=\"url\"\n label={t(I18nKey.SETTINGS$MCP_URL)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.url || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"https://api.example.com\"\n />\n\n <SettingsDropdownInput\n testId=\"auth-mode-dropdown\"\n name=\"auth-mode\"\n label={t(I18nKey.SETTINGS$MCP_AUTHENTICATION)}\n items={authModeOptions}\n selectedKey={authMode}\n onSelectionChange={(key) => setAuthMode(key as RemoteAuthMode)}\n onInputChange={() => {}}\n isClearable={false}\n allowsCustomValue={false}\n wrapperClassName=\"w-full min-w-0\"\n />\n\n {authMode === \"bearer\" && (\n <SettingsInput\n testId=\"api-key-input\"\n name=\"api_key\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_API_KEY)}\n className=\"w-full min-w-0\"\n required\n defaultValue={editableAuthValue(server?.auth)}\n placeholder={t(I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER)}\n />\n )}\n\n {authMode === \"header\" && (\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <span className=\"text-sm\">{t(I18nKey.SETTINGS$MCP_HEADERS)}</span>\n <textarea\n data-testid=\"headers-input\"\n name=\"headers\"\n rows={4}\n defaultValue={editableHeaderValue(server?.auth)}\n placeholder={t(I18nKey.SETTINGS$MCP_HEADERS_PLACEHOLDER)}\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n </label>\n )}\n\n {authMode === \"oauth2\" && (\n <>\n <SettingsDropdownInput\n testId=\"oauth-client-auth-method-dropdown\"\n name=\"oauth_client_auth_method\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH)}\n items={oauthClientAuthMethodOptions}\n selectedKey={oauthClientAuthMethod}\n onSelectionChange={(key) =>\n setOAuthClientAuthMethod(key as OAuthClientAuthMethodOption)\n }\n onInputChange={() => {}}\n isClearable={false}\n allowsCustomValue={false}\n wrapperClassName=\"w-full min-w-0\"\n />\n <SettingsInput\n testId=\"oauth-client-id-input\"\n name=\"oauth_client_id\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={oauthAuthentication?.client_id || \"\"}\n placeholder={t(\n I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID_PLACEHOLDER,\n )}\n />\n <SettingsInput\n testId=\"oauth-client-secret-input\"\n name=\"oauth_client_secret\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={oauthAuthentication?.client_secret || \"\"}\n placeholder={t(\n I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET_PLACEHOLDER,\n )}\n />\n <SettingsInput\n testId=\"oauth-scopes-input\"\n name=\"oauth_scopes\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_SCOPES)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={\n Array.isArray(oauthAuthentication?.scopes)\n ? oauthAuthentication.scopes.join(\" \")\n : oauthAuthentication?.scopes || \"\"\n }\n placeholder={t(I18nKey.SETTINGS$MCP_OAUTH_SCOPES_PLACEHOLDER)}\n />\n </>\n )}\n\n {serverType === \"shttp\" && (\n <SettingsInput\n testId=\"timeout-input\"\n name=\"timeout\"\n type=\"number\"\n label={t(I18nKey.SETTINGS$MCP_TIMEOUT_LABEL)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={server?.timeout?.toString() || \"\"}\n placeholder=\"60\"\n min={1}\n max={3600}\n />\n )}\n </>\n )}\n\n {serverType === \"stdio\" && (\n <>\n <SettingsInput\n testId=\"name-input\"\n name=\"name\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_NAME)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.name || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"my_mcp_server\"\n pattern={MCP_SERVER_NAME_PATTERN.source}\n />\n\n <SettingsInput\n testId=\"command-input\"\n name=\"command\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_COMMAND)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.command || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"npx\"\n />\n\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">\n {t(I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS)}\n </span>\n <OptionalTag />\n </div>\n <textarea\n data-testid=\"args-input\"\n name=\"args\"\n rows={3}\n defaultValue={server?.args?.join(\"\\n\") || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"arg1 arg2 arg3\"\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n <p className=\"text-xs text-tertiary-alt\">\n {t(I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS_HELP)}\n </p>\n </label>\n\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">\n {t(I18nKey.SETTINGS$MCP_ENVIRONMENT_VARIABLES)}\n </span>\n <OptionalTag />\n </div>\n <textarea\n data-testid=\"env-input\"\n name=\"env\"\n rows={4}\n defaultValue={formatEnvironmentVariables(server?.env)}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"KEY1=value1 KEY2=value2\"\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n </label>\n </>\n )}\n\n {testMessage && (\n <p\n data-testid=\"mcp-test-message\"\n className={\n testMessage.ok\n ? \"text-sm text-green-500 whitespace-pre-wrap\"\n : \"text-sm text-red-500 whitespace-pre-wrap\"\n }\n >\n {testMessage.text}\n </p>\n )}\n\n <div\n className={cn(\n \"flex w-full items-center gap-2\",\n onDelete ? \"justify-between\" : \"justify-end\",\n )}\n >\n {onDelete ? (\n <BrandButton\n testId=\"mcp-custom-editor-delete\"\n type=\"button\"\n variant=\"secondary\"\n onClick={onDelete}\n isDisabled={isActionDisabled}\n startContent={\n <Trash2 aria-hidden className=\"size-4\" strokeWidth={2} />\n }\n >\n {t(I18nKey.BUTTON$DELETE)}\n </BrandButton>\n ) : null}\n <div className=\"flex items-center gap-2\">\n <BrandButton\n testId=\"cancel-button\"\n type=\"button\"\n variant=\"secondary\"\n onClick={onCancel}\n isDisabled={isActionDisabled}\n >\n {t(I18nKey.BUTTON$CANCEL)}\n </BrandButton>\n {onTest && (\n <BrandButton\n testId=\"mcp-test-connection\"\n type=\"button\"\n variant=\"secondary\"\n onClick={handleTestClick}\n isDisabled={isActionDisabled || isTestPending}\n >\n {isTestPending\n ? t(I18nKey.MCP$VERIFYING)\n : t(I18nKey.MCP$TEST_BUTTON)}\n </BrandButton>\n )}\n <BrandButton\n testId=\"submit-button\"\n type=\"submit\"\n variant=\"primary\"\n isDisabled={isActionDisabled || isTestPending}\n >\n {mode === \"add\" && t(I18nKey.SETTINGS$MCP_ADD_SERVER)}\n {mode === \"edit\" && t(I18nKey.SETTINGS$MCP_SAVE_SERVER)}\n </BrandButton>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp-server-form.cjs","names":[],"sources":["../../../../../src/components/features/settings/mcp-settings/mcp-server-form.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Trash2 } from \"lucide-react\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { SettingsInput } from \"../settings-input\";\nimport { SettingsDropdownInput } from \"../settings-dropdown-input\";\nimport { BrandButton } from \"../brand-button\";\nimport { OptionalTag } from \"../optional-tag\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlMultilineFieldClassName } from \"#/utils/form-control-classes\";\nimport {\n isValidMcpServerName,\n MCP_SERVER_NAME_PATTERN,\n} from \"#/utils/mcp-server-name\";\nimport type {\n MCPAuthCredential,\n MCPAuthenticationConfig,\n MCPOAuthClientAuthMethod,\n} from \"#/types/mcp-auth\";\nimport type { MCPServerConfig } from \"#/types/mcp-server\";\n\ntype MCPServerType = \"sse\" | \"stdio\" | \"shttp\";\ntype RemoteAuthMode = \"none\" | \"bearer\" | \"header\" | \"oauth2\";\ntype OAuthClientAuthMethodOption = \"auto\" | MCPOAuthClientAuthMethod;\n\nexport interface TestMessage {\n ok: boolean;\n text: string;\n}\n\ninterface MCPServerFormProps {\n mode: \"add\" | \"edit\";\n server?: MCPServerConfig;\n existingServers?: MCPServerConfig[];\n onSubmit: (server: MCPServerConfig) => void;\n onCancel: () => void;\n onDelete?: () => void;\n isActionDisabled?: boolean;\n onTest?: (server: MCPServerConfig) => void;\n isTestPending?: boolean;\n testMessage?: TestMessage | null;\n}\n\nexport function MCPServerForm({\n mode,\n server,\n existingServers,\n onSubmit,\n onCancel,\n onDelete,\n isActionDisabled = false,\n onTest,\n isTestPending = false,\n testMessage = null,\n}: MCPServerFormProps) {\n const { t } = useTranslation(\"openhands\");\n const [serverType, setServerType] = React.useState<MCPServerType>(\n server?.type || \"sse\",\n );\n const [authMode, setAuthMode] = React.useState<RemoteAuthMode>(() => {\n if (server?.auth?.strategy === \"oauth2\") return \"oauth2\";\n if (server?.auth?.strategy === \"header\") return \"header\";\n if (\n server?.auth?.strategy === \"bearer\" ||\n server?.auth?.strategy === \"api_key\"\n ) {\n return \"bearer\";\n }\n return \"none\";\n });\n const [oauthClientAuthMethod, setOAuthClientAuthMethod] =\n React.useState<OAuthClientAuthMethodOption>(() =>\n server?.auth?.strategy === \"oauth2\"\n ? (server.auth.authentication?.client_auth_method ?? \"auto\")\n : \"auto\",\n );\n const [error, setError] = React.useState<string | null>(null);\n const formRef = React.useRef<HTMLFormElement>(null);\n\n const serverTypeOptions = [\n { key: \"sse\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_SSE) },\n { key: \"stdio\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_STDIO) },\n { key: \"shttp\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_SHTTP) },\n ];\n const authModeOptions = [\n { key: \"none\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_NONE) },\n { key: \"bearer\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_BEARER) },\n { key: \"header\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_HEADER) },\n { key: \"oauth2\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_OAUTH) },\n ];\n const oauthClientAuthMethodOptions = [\n { key: \"auto\", label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_AUTO) },\n { key: \"none\", label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_NONE) },\n {\n key: \"client_secret_post\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_POST),\n },\n {\n key: \"client_secret_basic\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_BASIC),\n },\n {\n key: \"private_key_jwt\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_PRIVATE_KEY_JWT),\n },\n ];\n\n const validateUrl = (url: string): string | null => {\n if (!url) return t(I18nKey.SETTINGS$MCP_ERROR_URL_REQUIRED);\n try {\n const urlObj = new URL(url);\n if (![\"http:\", \"https:\"].includes(urlObj.protocol)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_URL_INVALID_PROTOCOL);\n }\n } catch {\n return t(I18nKey.SETTINGS$MCP_ERROR_URL_INVALID);\n }\n return null;\n };\n\n const validateName = (name: string): string | null => {\n if (!name) return t(I18nKey.SETTINGS$MCP_ERROR_NAME_REQUIRED);\n if (!isValidMcpServerName(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);\n }\n return null;\n };\n\n const validateNameUniqueness = (name: string): string | null => {\n if (!existingServers) return null;\n const shouldCheckUniqueness =\n mode === \"add\" || (mode === \"edit\" && server?.name !== name);\n if (!shouldCheckUniqueness) return null;\n\n const existingStdioNames = existingServers\n .filter((s) => s.type === \"stdio\")\n .map((s) => s.name)\n .filter(Boolean);\n if (existingStdioNames.includes(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_DUPLICATE);\n }\n return null;\n };\n\n const validateCommand = (command: string): string | null => {\n if (!command) return t(I18nKey.SETTINGS$MCP_ERROR_COMMAND_REQUIRED);\n if (command.includes(\" \")) {\n return t(I18nKey.SETTINGS$MCP_ERROR_COMMAND_NO_SPACES);\n }\n return null;\n };\n\n const validateUrlUniqueness = (url: string): string | null => {\n if (!existingServers) return null;\n const originalUrl = server?.url;\n const changed = mode === \"add\" || (mode === \"edit\" && originalUrl !== url);\n if (!changed) return null;\n // For URL-based servers (sse/shttp), ensure URL is unique across both types\n const exists = existingServers.some(\n (s) => (s.type === \"sse\" || s.type === \"shttp\") && s.url === url,\n );\n if (exists) return t(I18nKey.SETTINGS$MCP_ERROR_URL_DUPLICATE);\n return null;\n };\n\n const validateEnvFormat = (envString: string): string | null => {\n if (!envString.trim()) return null;\n const lines = envString.split(\"\\n\");\n for (let i = 0; i < lines.length; i += 1) {\n const trimmed = lines[i].trim();\n if (trimmed) {\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) return t(I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT);\n const key = trimmed.substring(0, eq).trim();\n if (!key) return t(I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT);\n }\n }\n return null;\n };\n\n const validateRemoteAuth = (formData: FormData): string | null => {\n if (authMode === \"header\") {\n const headerString = formData.get(\"headers\")?.toString() || \"\";\n if (!headerString.trim())\n return t(I18nKey.SETTINGS$MCP_ERROR_HEADER_REQUIRED);\n return validateEnvFormat(headerString);\n }\n if (authMode === \"oauth2\") {\n const clientId = formData.get(\"oauth_client_id\")?.toString().trim();\n const clientSecret = formData\n .get(\"oauth_client_secret\")\n ?.toString()\n .trim();\n if (clientSecret && !clientId) {\n return t(I18nKey.SETTINGS$MCP_ERROR_OAUTH_SECRET_REQUIRES_ID);\n }\n }\n return null;\n };\n\n const validateTimeout = (timeoutStr: string): string | null => {\n if (!timeoutStr.trim()) return null; // Optional field\n\n const timeout = parseInt(timeoutStr.trim(), 10);\n if (Number.isNaN(timeout)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_INVALID_NUMBER);\n }\n if (timeout <= 0) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_POSITIVE);\n }\n if (timeout > 3600) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_MAX_EXCEEDED);\n }\n return null;\n };\n\n const validateStdioServer = (formData: FormData): string | null => {\n const name = formData.get(\"name\")?.toString().trim() || \"\";\n const command = formData.get(\"command\")?.toString().trim() || \"\";\n const envString = formData.get(\"env\")?.toString() || \"\";\n\n const nameError = validateName(name);\n if (nameError) return nameError;\n\n const uniquenessError = validateNameUniqueness(name);\n if (uniquenessError) return uniquenessError;\n\n const commandError = validateCommand(command);\n if (commandError) return commandError;\n\n // Validate environment variable format\n const envError = validateEnvFormat(envString);\n if (envError) return envError;\n\n return null;\n };\n\n const validateForm = (formData: FormData): string | null => {\n if (serverType === \"sse\" || serverType === \"shttp\") {\n const url = formData.get(\"url\")?.toString().trim() || \"\";\n const urlError = validateUrl(url);\n if (urlError) return urlError;\n const urlDupError = validateUrlUniqueness(url);\n if (urlDupError) return urlDupError;\n\n // The name is optional, but when provided it becomes the mcp_config\n // key (and the reference used in mcp_server_refs), so hold it to the\n // same safe-identifier rule as stdio names.\n const name = formData.get(\"name\")?.toString().trim() || \"\";\n if (name && !isValidMcpServerName(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);\n }\n\n // Validate timeout for SHTTP servers only\n if (serverType === \"shttp\") {\n const timeoutStr = formData.get(\"timeout\")?.toString() || \"\";\n const timeoutError = validateTimeout(timeoutStr);\n if (timeoutError) return timeoutError;\n }\n\n return validateRemoteAuth(formData);\n }\n\n if (serverType === \"stdio\") {\n return validateStdioServer(formData);\n }\n\n return null;\n };\n\n const parseEnvironmentVariables = (\n envString: string,\n ): Record<string, string> => {\n const env: Record<string, string> = {};\n const input = envString.trim();\n if (!input) return env;\n\n for (const line of input.split(\"\\n\")) {\n const trimmed = line.trim();\n const eq = trimmed.indexOf(\"=\");\n const key = eq >= 0 ? trimmed.substring(0, eq).trim() : \"\";\n if (trimmed && eq !== -1 && key) {\n env[key] = trimmed.substring(eq + 1).trim();\n }\n }\n return env;\n };\n\n const formatEnvironmentVariables = (\n env?: Record<string, string> | null,\n ): string => {\n if (!env) return \"\";\n return Object.entries(env)\n .map(([key, value]) => `${key}=${value}`)\n .join(\"\\n\");\n };\n\n const editableAuthValue = (auth: MCPAuthCredential | undefined): string => {\n if (auth?.strategy === \"bearer\" || auth?.strategy === \"api_key\") {\n return auth.value ?? \"\";\n }\n return \"\";\n };\n\n const editableHeaderValue = (auth: MCPAuthCredential | undefined): string => {\n if (auth?.strategy !== \"header\") return \"\";\n return formatEnvironmentVariables(auth.headers);\n };\n\n const oauthAuthentication =\n server?.auth?.strategy === \"oauth2\"\n ? server.auth.authentication\n : undefined;\n const oauthState =\n server?.auth?.strategy === \"oauth2\" ? server.auth.state : undefined;\n\n const authFromFormData = (\n formData: FormData,\n ): MCPAuthCredential | undefined => {\n if (authMode === \"none\") return undefined;\n if (authMode === \"bearer\") {\n const value = formData.get(\"api_key\")?.toString().trim();\n if (!value) return undefined;\n if (server?.auth?.strategy === \"api_key\") {\n return { ...server.auth, value };\n }\n return { strategy: \"bearer\", value };\n }\n if (authMode === \"header\") {\n const headers = parseEnvironmentVariables(\n formData.get(\"headers\")?.toString() || \"\",\n );\n return { strategy: \"header\", headers };\n }\n\n const scopes = formData.get(\"oauth_scopes\")?.toString().trim();\n const clientId = formData.get(\"oauth_client_id\")?.toString().trim();\n const clientSecret = formData.get(\"oauth_client_secret\")?.toString().trim();\n const authentication: MCPAuthenticationConfig = {\n type: \"oauth\",\n ...(oauthClientAuthMethod !== \"auto\" && {\n client_auth_method: oauthClientAuthMethod,\n }),\n ...(scopes && { scopes }),\n ...(clientId && { client_id: clientId }),\n ...(clientSecret && { client_secret: clientSecret }),\n };\n return {\n strategy: \"oauth2\",\n authentication,\n ...(oauthState && { state: oauthState }),\n };\n };\n\n const buildConfig = (formData: FormData): MCPServerConfig => {\n const baseConfig = {\n id: server?.id || `${serverType}-${Date.now()}`,\n type: serverType,\n // The form has no enable/disable control (that lives on the card), so\n // carry the existing state forward — otherwise saving an edit to a\n // disabled server would silently switch it back on.\n ...(server?.enabled === false && { enabled: false }),\n };\n\n if (serverType === \"sse\" || serverType === \"shttp\") {\n const name = formData.get(\"name\")?.toString().trim();\n const url = formData.get(\"url\")?.toString().trim();\n const timeoutStr = formData.get(\"timeout\")?.toString().trim();\n const auth = authFromFormData(formData);\n\n const serverConfig: MCPServerConfig = {\n ...baseConfig,\n ...(name && { name }),\n url: url!,\n ...(auth && { auth }),\n };\n\n // Only add timeout for SHTTP servers\n if (serverType === \"shttp\" && timeoutStr) {\n const timeoutValue = parseInt(timeoutStr, 10);\n if (!Number.isNaN(timeoutValue)) {\n serverConfig.timeout = timeoutValue;\n }\n }\n\n return serverConfig;\n }\n\n // stdio\n const name = formData.get(\"name\")?.toString().trim();\n const command = formData.get(\"command\")?.toString().trim();\n const argsString = formData.get(\"args\")?.toString().trim();\n const envString = formData.get(\"env\")?.toString().trim();\n\n const args = argsString\n ? argsString\n .split(\"\\n\")\n .map((arg) => arg.trim())\n .filter(Boolean)\n : [];\n const env = parseEnvironmentVariables(envString || \"\");\n\n return {\n ...baseConfig,\n name: name!,\n command: command!,\n ...(args.length > 0 && { args }),\n ...(Object.keys(env).length > 0 && { env }),\n };\n };\n\n const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setError(null);\n\n const formData = new FormData(event.currentTarget);\n const validationError = validateForm(formData);\n\n if (validationError) {\n setError(validationError);\n return;\n }\n\n onSubmit(buildConfig(formData));\n };\n\n const handleTestClick = () => {\n if (!onTest || !formRef.current) return;\n setError(null);\n const formData = new FormData(formRef.current);\n const validationError = validateForm(formData);\n if (validationError) {\n setError(validationError);\n return;\n }\n onTest(buildConfig(formData));\n };\n\n const formTestId =\n mode === \"add\" ? \"add-mcp-server-form\" : \"edit-mcp-server-form\";\n\n return (\n <form\n ref={formRef}\n data-testid={formTestId}\n onSubmit={handleSubmit}\n className=\"flex flex-col items-start gap-6\"\n noValidate\n >\n {mode === \"add\" && (\n <SettingsDropdownInput\n testId=\"server-type-dropdown\"\n name=\"server-type\"\n label={t(I18nKey.SETTINGS$MCP_SERVER_TYPE)}\n items={serverTypeOptions}\n selectedKey={serverType}\n onSelectionChange={(key) => setServerType(key as MCPServerType)}\n onInputChange={() => {}} // Prevent input changes\n isClearable={false}\n allowsCustomValue={false}\n required\n wrapperClassName=\"w-full min-w-0\"\n />\n )}\n\n {error && <p className=\"text-red-500 text-sm\">{error}</p>}\n\n {(serverType === \"sse\" || serverType === \"shttp\") && (\n <>\n <SettingsInput\n testId=\"server-name-input\"\n name=\"name\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_SERVER_NAME)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={server?.name || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"my_search_server\"\n pattern={MCP_SERVER_NAME_PATTERN.source}\n />\n\n <SettingsInput\n testId=\"url-input\"\n name=\"url\"\n type=\"url\"\n label={t(I18nKey.SETTINGS$MCP_URL)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.url || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"https://api.example.com\"\n />\n\n <SettingsDropdownInput\n testId=\"auth-mode-dropdown\"\n name=\"auth-mode\"\n label={t(I18nKey.SETTINGS$MCP_AUTHENTICATION)}\n items={authModeOptions}\n selectedKey={authMode}\n onSelectionChange={(key) => setAuthMode(key as RemoteAuthMode)}\n onInputChange={() => {}}\n isClearable={false}\n allowsCustomValue={false}\n wrapperClassName=\"w-full min-w-0\"\n />\n\n {authMode === \"bearer\" && (\n <SettingsInput\n testId=\"api-key-input\"\n name=\"api_key\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_API_KEY)}\n className=\"w-full min-w-0\"\n required\n defaultValue={editableAuthValue(server?.auth)}\n placeholder={t(I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER)}\n />\n )}\n\n {authMode === \"header\" && (\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <span className=\"text-sm\">{t(I18nKey.SETTINGS$MCP_HEADERS)}</span>\n <textarea\n data-testid=\"headers-input\"\n name=\"headers\"\n rows={4}\n defaultValue={editableHeaderValue(server?.auth)}\n placeholder={t(I18nKey.SETTINGS$MCP_HEADERS_PLACEHOLDER)}\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n </label>\n )}\n\n {authMode === \"oauth2\" && (\n <>\n <SettingsDropdownInput\n testId=\"oauth-client-auth-method-dropdown\"\n name=\"oauth_client_auth_method\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH)}\n items={oauthClientAuthMethodOptions}\n selectedKey={oauthClientAuthMethod}\n onSelectionChange={(key) =>\n setOAuthClientAuthMethod(key as OAuthClientAuthMethodOption)\n }\n onInputChange={() => {}}\n isClearable={false}\n allowsCustomValue={false}\n wrapperClassName=\"w-full min-w-0\"\n />\n <SettingsInput\n testId=\"oauth-client-id-input\"\n name=\"oauth_client_id\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={oauthAuthentication?.client_id || \"\"}\n placeholder={t(\n I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID_PLACEHOLDER,\n )}\n />\n <SettingsInput\n testId=\"oauth-client-secret-input\"\n name=\"oauth_client_secret\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={oauthAuthentication?.client_secret || \"\"}\n placeholder={t(\n I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET_PLACEHOLDER,\n )}\n />\n <SettingsInput\n testId=\"oauth-scopes-input\"\n name=\"oauth_scopes\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_SCOPES)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={\n Array.isArray(oauthAuthentication?.scopes)\n ? oauthAuthentication.scopes.join(\" \")\n : oauthAuthentication?.scopes || \"\"\n }\n placeholder={t(I18nKey.SETTINGS$MCP_OAUTH_SCOPES_PLACEHOLDER)}\n />\n </>\n )}\n\n {serverType === \"shttp\" && (\n <SettingsInput\n testId=\"timeout-input\"\n name=\"timeout\"\n type=\"number\"\n label={t(I18nKey.SETTINGS$MCP_TIMEOUT_LABEL)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={server?.timeout?.toString() || \"\"}\n placeholder=\"60\"\n min={1}\n max={3600}\n />\n )}\n </>\n )}\n\n {serverType === \"stdio\" && (\n <>\n <SettingsInput\n testId=\"name-input\"\n name=\"name\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_NAME)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.name || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"my_mcp_server\"\n pattern={MCP_SERVER_NAME_PATTERN.source}\n />\n\n <SettingsInput\n testId=\"command-input\"\n name=\"command\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_COMMAND)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.command || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"npx\"\n />\n\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">\n {t(I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS)}\n </span>\n <OptionalTag />\n </div>\n <textarea\n data-testid=\"args-input\"\n name=\"args\"\n rows={3}\n defaultValue={server?.args?.join(\"\\n\") || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"arg1 arg2 arg3\"\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n <p className=\"text-xs text-tertiary-alt\">\n {t(I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS_HELP)}\n </p>\n </label>\n\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">\n {t(I18nKey.SETTINGS$MCP_ENVIRONMENT_VARIABLES)}\n </span>\n <OptionalTag />\n </div>\n <textarea\n data-testid=\"env-input\"\n name=\"env\"\n rows={4}\n defaultValue={formatEnvironmentVariables(server?.env)}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"KEY1=value1 KEY2=value2\"\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n </label>\n </>\n )}\n\n {testMessage && (\n <p\n data-testid=\"mcp-test-message\"\n className={\n testMessage.ok\n ? \"text-sm text-green-500 whitespace-pre-wrap\"\n : \"text-sm text-red-500 whitespace-pre-wrap\"\n }\n >\n {testMessage.text}\n </p>\n )}\n\n <div\n className={cn(\n \"flex w-full items-center gap-2\",\n onDelete ? \"justify-between\" : \"justify-end\",\n )}\n >\n {onDelete ? (\n <BrandButton\n testId=\"mcp-custom-editor-delete\"\n type=\"button\"\n variant=\"secondary\"\n onClick={onDelete}\n isDisabled={isActionDisabled}\n startContent={\n <Trash2 aria-hidden className=\"size-4\" strokeWidth={2} />\n }\n >\n {t(I18nKey.BUTTON$DELETE)}\n </BrandButton>\n ) : null}\n <div className=\"flex items-center gap-2\">\n <BrandButton\n testId=\"cancel-button\"\n type=\"button\"\n variant=\"secondary\"\n onClick={onCancel}\n isDisabled={isActionDisabled}\n >\n {t(I18nKey.BUTTON$CANCEL)}\n </BrandButton>\n {onTest && (\n <BrandButton\n testId=\"mcp-test-connection\"\n type=\"button\"\n variant=\"secondary\"\n onClick={handleTestClick}\n isDisabled={isActionDisabled || isTestPending}\n >\n {isTestPending\n ? t(I18nKey.MCP$VERIFYING)\n : t(I18nKey.MCP$TEST_BUTTON)}\n </BrandButton>\n )}\n <BrandButton\n testId=\"submit-button\"\n type=\"submit\"\n variant=\"primary\"\n isDisabled={isActionDisabled || isTestPending}\n >\n {mode === \"add\" && t(I18nKey.SETTINGS$MCP_ADD_SERVER)}\n {mode === \"edit\" && t(I18nKey.SETTINGS$MCP_SAVE_SERVER)}\n </BrandButton>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
@@ -158,7 +158,8 @@ function h({ mode: h, server: g, existingServers: _, onSubmit: v, onCancel: y, o
|
|
|
158
158
|
}, $ = (e) => {
|
|
159
159
|
let t = {
|
|
160
160
|
id: g?.id || `${E}-${Date.now()}`,
|
|
161
|
-
type: E
|
|
161
|
+
type: E,
|
|
162
|
+
...g?.enabled === !1 && { enabled: !1 }
|
|
162
163
|
};
|
|
163
164
|
if (E === "sse" || E === "shttp") {
|
|
164
165
|
let n = e.get("name")?.toString().trim(), r = e.get("url")?.toString().trim(), i = e.get("timeout")?.toString().trim(), a = te(e), o = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server-form.js","names":[],"sources":["../../../../../src/components/features/settings/mcp-settings/mcp-server-form.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Trash2 } from \"lucide-react\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { SettingsInput } from \"../settings-input\";\nimport { SettingsDropdownInput } from \"../settings-dropdown-input\";\nimport { BrandButton } from \"../brand-button\";\nimport { OptionalTag } from \"../optional-tag\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlMultilineFieldClassName } from \"#/utils/form-control-classes\";\nimport {\n isValidMcpServerName,\n MCP_SERVER_NAME_PATTERN,\n} from \"#/utils/mcp-server-name\";\nimport type {\n MCPAuthCredential,\n MCPAuthenticationConfig,\n MCPOAuthClientAuthMethod,\n} from \"#/types/mcp-auth\";\nimport type { MCPServerConfig } from \"#/types/mcp-server\";\n\ntype MCPServerType = \"sse\" | \"stdio\" | \"shttp\";\ntype RemoteAuthMode = \"none\" | \"bearer\" | \"header\" | \"oauth2\";\ntype OAuthClientAuthMethodOption = \"auto\" | MCPOAuthClientAuthMethod;\n\nexport interface TestMessage {\n ok: boolean;\n text: string;\n}\n\ninterface MCPServerFormProps {\n mode: \"add\" | \"edit\";\n server?: MCPServerConfig;\n existingServers?: MCPServerConfig[];\n onSubmit: (server: MCPServerConfig) => void;\n onCancel: () => void;\n onDelete?: () => void;\n isActionDisabled?: boolean;\n onTest?: (server: MCPServerConfig) => void;\n isTestPending?: boolean;\n testMessage?: TestMessage | null;\n}\n\nexport function MCPServerForm({\n mode,\n server,\n existingServers,\n onSubmit,\n onCancel,\n onDelete,\n isActionDisabled = false,\n onTest,\n isTestPending = false,\n testMessage = null,\n}: MCPServerFormProps) {\n const { t } = useTranslation(\"openhands\");\n const [serverType, setServerType] = React.useState<MCPServerType>(\n server?.type || \"sse\",\n );\n const [authMode, setAuthMode] = React.useState<RemoteAuthMode>(() => {\n if (server?.auth?.strategy === \"oauth2\") return \"oauth2\";\n if (server?.auth?.strategy === \"header\") return \"header\";\n if (\n server?.auth?.strategy === \"bearer\" ||\n server?.auth?.strategy === \"api_key\"\n ) {\n return \"bearer\";\n }\n return \"none\";\n });\n const [oauthClientAuthMethod, setOAuthClientAuthMethod] =\n React.useState<OAuthClientAuthMethodOption>(() =>\n server?.auth?.strategy === \"oauth2\"\n ? (server.auth.authentication?.client_auth_method ?? \"auto\")\n : \"auto\",\n );\n const [error, setError] = React.useState<string | null>(null);\n const formRef = React.useRef<HTMLFormElement>(null);\n\n const serverTypeOptions = [\n { key: \"sse\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_SSE) },\n { key: \"stdio\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_STDIO) },\n { key: \"shttp\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_SHTTP) },\n ];\n const authModeOptions = [\n { key: \"none\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_NONE) },\n { key: \"bearer\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_BEARER) },\n { key: \"header\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_HEADER) },\n { key: \"oauth2\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_OAUTH) },\n ];\n const oauthClientAuthMethodOptions = [\n { key: \"auto\", label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_AUTO) },\n { key: \"none\", label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_NONE) },\n {\n key: \"client_secret_post\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_POST),\n },\n {\n key: \"client_secret_basic\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_BASIC),\n },\n {\n key: \"private_key_jwt\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_PRIVATE_KEY_JWT),\n },\n ];\n\n const validateUrl = (url: string): string | null => {\n if (!url) return t(I18nKey.SETTINGS$MCP_ERROR_URL_REQUIRED);\n try {\n const urlObj = new URL(url);\n if (![\"http:\", \"https:\"].includes(urlObj.protocol)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_URL_INVALID_PROTOCOL);\n }\n } catch {\n return t(I18nKey.SETTINGS$MCP_ERROR_URL_INVALID);\n }\n return null;\n };\n\n const validateName = (name: string): string | null => {\n if (!name) return t(I18nKey.SETTINGS$MCP_ERROR_NAME_REQUIRED);\n if (!isValidMcpServerName(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);\n }\n return null;\n };\n\n const validateNameUniqueness = (name: string): string | null => {\n if (!existingServers) return null;\n const shouldCheckUniqueness =\n mode === \"add\" || (mode === \"edit\" && server?.name !== name);\n if (!shouldCheckUniqueness) return null;\n\n const existingStdioNames = existingServers\n .filter((s) => s.type === \"stdio\")\n .map((s) => s.name)\n .filter(Boolean);\n if (existingStdioNames.includes(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_DUPLICATE);\n }\n return null;\n };\n\n const validateCommand = (command: string): string | null => {\n if (!command) return t(I18nKey.SETTINGS$MCP_ERROR_COMMAND_REQUIRED);\n if (command.includes(\" \")) {\n return t(I18nKey.SETTINGS$MCP_ERROR_COMMAND_NO_SPACES);\n }\n return null;\n };\n\n const validateUrlUniqueness = (url: string): string | null => {\n if (!existingServers) return null;\n const originalUrl = server?.url;\n const changed = mode === \"add\" || (mode === \"edit\" && originalUrl !== url);\n if (!changed) return null;\n // For URL-based servers (sse/shttp), ensure URL is unique across both types\n const exists = existingServers.some(\n (s) => (s.type === \"sse\" || s.type === \"shttp\") && s.url === url,\n );\n if (exists) return t(I18nKey.SETTINGS$MCP_ERROR_URL_DUPLICATE);\n return null;\n };\n\n const validateEnvFormat = (envString: string): string | null => {\n if (!envString.trim()) return null;\n const lines = envString.split(\"\\n\");\n for (let i = 0; i < lines.length; i += 1) {\n const trimmed = lines[i].trim();\n if (trimmed) {\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) return t(I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT);\n const key = trimmed.substring(0, eq).trim();\n if (!key) return t(I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT);\n }\n }\n return null;\n };\n\n const validateRemoteAuth = (formData: FormData): string | null => {\n if (authMode === \"header\") {\n const headerString = formData.get(\"headers\")?.toString() || \"\";\n if (!headerString.trim())\n return t(I18nKey.SETTINGS$MCP_ERROR_HEADER_REQUIRED);\n return validateEnvFormat(headerString);\n }\n if (authMode === \"oauth2\") {\n const clientId = formData.get(\"oauth_client_id\")?.toString().trim();\n const clientSecret = formData\n .get(\"oauth_client_secret\")\n ?.toString()\n .trim();\n if (clientSecret && !clientId) {\n return t(I18nKey.SETTINGS$MCP_ERROR_OAUTH_SECRET_REQUIRES_ID);\n }\n }\n return null;\n };\n\n const validateTimeout = (timeoutStr: string): string | null => {\n if (!timeoutStr.trim()) return null; // Optional field\n\n const timeout = parseInt(timeoutStr.trim(), 10);\n if (Number.isNaN(timeout)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_INVALID_NUMBER);\n }\n if (timeout <= 0) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_POSITIVE);\n }\n if (timeout > 3600) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_MAX_EXCEEDED);\n }\n return null;\n };\n\n const validateStdioServer = (formData: FormData): string | null => {\n const name = formData.get(\"name\")?.toString().trim() || \"\";\n const command = formData.get(\"command\")?.toString().trim() || \"\";\n const envString = formData.get(\"env\")?.toString() || \"\";\n\n const nameError = validateName(name);\n if (nameError) return nameError;\n\n const uniquenessError = validateNameUniqueness(name);\n if (uniquenessError) return uniquenessError;\n\n const commandError = validateCommand(command);\n if (commandError) return commandError;\n\n // Validate environment variable format\n const envError = validateEnvFormat(envString);\n if (envError) return envError;\n\n return null;\n };\n\n const validateForm = (formData: FormData): string | null => {\n if (serverType === \"sse\" || serverType === \"shttp\") {\n const url = formData.get(\"url\")?.toString().trim() || \"\";\n const urlError = validateUrl(url);\n if (urlError) return urlError;\n const urlDupError = validateUrlUniqueness(url);\n if (urlDupError) return urlDupError;\n\n // The name is optional, but when provided it becomes the mcp_config\n // key (and the reference used in mcp_server_refs), so hold it to the\n // same safe-identifier rule as stdio names.\n const name = formData.get(\"name\")?.toString().trim() || \"\";\n if (name && !isValidMcpServerName(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);\n }\n\n // Validate timeout for SHTTP servers only\n if (serverType === \"shttp\") {\n const timeoutStr = formData.get(\"timeout\")?.toString() || \"\";\n const timeoutError = validateTimeout(timeoutStr);\n if (timeoutError) return timeoutError;\n }\n\n return validateRemoteAuth(formData);\n }\n\n if (serverType === \"stdio\") {\n return validateStdioServer(formData);\n }\n\n return null;\n };\n\n const parseEnvironmentVariables = (\n envString: string,\n ): Record<string, string> => {\n const env: Record<string, string> = {};\n const input = envString.trim();\n if (!input) return env;\n\n for (const line of input.split(\"\\n\")) {\n const trimmed = line.trim();\n const eq = trimmed.indexOf(\"=\");\n const key = eq >= 0 ? trimmed.substring(0, eq).trim() : \"\";\n if (trimmed && eq !== -1 && key) {\n env[key] = trimmed.substring(eq + 1).trim();\n }\n }\n return env;\n };\n\n const formatEnvironmentVariables = (\n env?: Record<string, string> | null,\n ): string => {\n if (!env) return \"\";\n return Object.entries(env)\n .map(([key, value]) => `${key}=${value}`)\n .join(\"\\n\");\n };\n\n const editableAuthValue = (auth: MCPAuthCredential | undefined): string => {\n if (auth?.strategy === \"bearer\" || auth?.strategy === \"api_key\") {\n return auth.value ?? \"\";\n }\n return \"\";\n };\n\n const editableHeaderValue = (auth: MCPAuthCredential | undefined): string => {\n if (auth?.strategy !== \"header\") return \"\";\n return formatEnvironmentVariables(auth.headers);\n };\n\n const oauthAuthentication =\n server?.auth?.strategy === \"oauth2\"\n ? server.auth.authentication\n : undefined;\n const oauthState =\n server?.auth?.strategy === \"oauth2\" ? server.auth.state : undefined;\n\n const authFromFormData = (\n formData: FormData,\n ): MCPAuthCredential | undefined => {\n if (authMode === \"none\") return undefined;\n if (authMode === \"bearer\") {\n const value = formData.get(\"api_key\")?.toString().trim();\n if (!value) return undefined;\n if (server?.auth?.strategy === \"api_key\") {\n return { ...server.auth, value };\n }\n return { strategy: \"bearer\", value };\n }\n if (authMode === \"header\") {\n const headers = parseEnvironmentVariables(\n formData.get(\"headers\")?.toString() || \"\",\n );\n return { strategy: \"header\", headers };\n }\n\n const scopes = formData.get(\"oauth_scopes\")?.toString().trim();\n const clientId = formData.get(\"oauth_client_id\")?.toString().trim();\n const clientSecret = formData.get(\"oauth_client_secret\")?.toString().trim();\n const authentication: MCPAuthenticationConfig = {\n type: \"oauth\",\n ...(oauthClientAuthMethod !== \"auto\" && {\n client_auth_method: oauthClientAuthMethod,\n }),\n ...(scopes && { scopes }),\n ...(clientId && { client_id: clientId }),\n ...(clientSecret && { client_secret: clientSecret }),\n };\n return {\n strategy: \"oauth2\",\n authentication,\n ...(oauthState && { state: oauthState }),\n };\n };\n\n const buildConfig = (formData: FormData): MCPServerConfig => {\n const baseConfig = {\n id: server?.id || `${serverType}-${Date.now()}`,\n type: serverType,\n };\n\n if (serverType === \"sse\" || serverType === \"shttp\") {\n const name = formData.get(\"name\")?.toString().trim();\n const url = formData.get(\"url\")?.toString().trim();\n const timeoutStr = formData.get(\"timeout\")?.toString().trim();\n const auth = authFromFormData(formData);\n\n const serverConfig: MCPServerConfig = {\n ...baseConfig,\n ...(name && { name }),\n url: url!,\n ...(auth && { auth }),\n };\n\n // Only add timeout for SHTTP servers\n if (serverType === \"shttp\" && timeoutStr) {\n const timeoutValue = parseInt(timeoutStr, 10);\n if (!Number.isNaN(timeoutValue)) {\n serverConfig.timeout = timeoutValue;\n }\n }\n\n return serverConfig;\n }\n\n // stdio\n const name = formData.get(\"name\")?.toString().trim();\n const command = formData.get(\"command\")?.toString().trim();\n const argsString = formData.get(\"args\")?.toString().trim();\n const envString = formData.get(\"env\")?.toString().trim();\n\n const args = argsString\n ? argsString\n .split(\"\\n\")\n .map((arg) => arg.trim())\n .filter(Boolean)\n : [];\n const env = parseEnvironmentVariables(envString || \"\");\n\n return {\n ...baseConfig,\n name: name!,\n command: command!,\n ...(args.length > 0 && { args }),\n ...(Object.keys(env).length > 0 && { env }),\n };\n };\n\n const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setError(null);\n\n const formData = new FormData(event.currentTarget);\n const validationError = validateForm(formData);\n\n if (validationError) {\n setError(validationError);\n return;\n }\n\n onSubmit(buildConfig(formData));\n };\n\n const handleTestClick = () => {\n if (!onTest || !formRef.current) return;\n setError(null);\n const formData = new FormData(formRef.current);\n const validationError = validateForm(formData);\n if (validationError) {\n setError(validationError);\n return;\n }\n onTest(buildConfig(formData));\n };\n\n const formTestId =\n mode === \"add\" ? \"add-mcp-server-form\" : \"edit-mcp-server-form\";\n\n return (\n <form\n ref={formRef}\n data-testid={formTestId}\n onSubmit={handleSubmit}\n className=\"flex flex-col items-start gap-6\"\n noValidate\n >\n {mode === \"add\" && (\n <SettingsDropdownInput\n testId=\"server-type-dropdown\"\n name=\"server-type\"\n label={t(I18nKey.SETTINGS$MCP_SERVER_TYPE)}\n items={serverTypeOptions}\n selectedKey={serverType}\n onSelectionChange={(key) => setServerType(key as MCPServerType)}\n onInputChange={() => {}} // Prevent input changes\n isClearable={false}\n allowsCustomValue={false}\n required\n wrapperClassName=\"w-full min-w-0\"\n />\n )}\n\n {error && <p className=\"text-red-500 text-sm\">{error}</p>}\n\n {(serverType === \"sse\" || serverType === \"shttp\") && (\n <>\n <SettingsInput\n testId=\"server-name-input\"\n name=\"name\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_SERVER_NAME)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={server?.name || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"my_search_server\"\n pattern={MCP_SERVER_NAME_PATTERN.source}\n />\n\n <SettingsInput\n testId=\"url-input\"\n name=\"url\"\n type=\"url\"\n label={t(I18nKey.SETTINGS$MCP_URL)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.url || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"https://api.example.com\"\n />\n\n <SettingsDropdownInput\n testId=\"auth-mode-dropdown\"\n name=\"auth-mode\"\n label={t(I18nKey.SETTINGS$MCP_AUTHENTICATION)}\n items={authModeOptions}\n selectedKey={authMode}\n onSelectionChange={(key) => setAuthMode(key as RemoteAuthMode)}\n onInputChange={() => {}}\n isClearable={false}\n allowsCustomValue={false}\n wrapperClassName=\"w-full min-w-0\"\n />\n\n {authMode === \"bearer\" && (\n <SettingsInput\n testId=\"api-key-input\"\n name=\"api_key\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_API_KEY)}\n className=\"w-full min-w-0\"\n required\n defaultValue={editableAuthValue(server?.auth)}\n placeholder={t(I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER)}\n />\n )}\n\n {authMode === \"header\" && (\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <span className=\"text-sm\">{t(I18nKey.SETTINGS$MCP_HEADERS)}</span>\n <textarea\n data-testid=\"headers-input\"\n name=\"headers\"\n rows={4}\n defaultValue={editableHeaderValue(server?.auth)}\n placeholder={t(I18nKey.SETTINGS$MCP_HEADERS_PLACEHOLDER)}\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n </label>\n )}\n\n {authMode === \"oauth2\" && (\n <>\n <SettingsDropdownInput\n testId=\"oauth-client-auth-method-dropdown\"\n name=\"oauth_client_auth_method\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH)}\n items={oauthClientAuthMethodOptions}\n selectedKey={oauthClientAuthMethod}\n onSelectionChange={(key) =>\n setOAuthClientAuthMethod(key as OAuthClientAuthMethodOption)\n }\n onInputChange={() => {}}\n isClearable={false}\n allowsCustomValue={false}\n wrapperClassName=\"w-full min-w-0\"\n />\n <SettingsInput\n testId=\"oauth-client-id-input\"\n name=\"oauth_client_id\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={oauthAuthentication?.client_id || \"\"}\n placeholder={t(\n I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID_PLACEHOLDER,\n )}\n />\n <SettingsInput\n testId=\"oauth-client-secret-input\"\n name=\"oauth_client_secret\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={oauthAuthentication?.client_secret || \"\"}\n placeholder={t(\n I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET_PLACEHOLDER,\n )}\n />\n <SettingsInput\n testId=\"oauth-scopes-input\"\n name=\"oauth_scopes\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_SCOPES)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={\n Array.isArray(oauthAuthentication?.scopes)\n ? oauthAuthentication.scopes.join(\" \")\n : oauthAuthentication?.scopes || \"\"\n }\n placeholder={t(I18nKey.SETTINGS$MCP_OAUTH_SCOPES_PLACEHOLDER)}\n />\n </>\n )}\n\n {serverType === \"shttp\" && (\n <SettingsInput\n testId=\"timeout-input\"\n name=\"timeout\"\n type=\"number\"\n label={t(I18nKey.SETTINGS$MCP_TIMEOUT_LABEL)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={server?.timeout?.toString() || \"\"}\n placeholder=\"60\"\n min={1}\n max={3600}\n />\n )}\n </>\n )}\n\n {serverType === \"stdio\" && (\n <>\n <SettingsInput\n testId=\"name-input\"\n name=\"name\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_NAME)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.name || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"my_mcp_server\"\n pattern={MCP_SERVER_NAME_PATTERN.source}\n />\n\n <SettingsInput\n testId=\"command-input\"\n name=\"command\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_COMMAND)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.command || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"npx\"\n />\n\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">\n {t(I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS)}\n </span>\n <OptionalTag />\n </div>\n <textarea\n data-testid=\"args-input\"\n name=\"args\"\n rows={3}\n defaultValue={server?.args?.join(\"\\n\") || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"arg1 arg2 arg3\"\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n <p className=\"text-xs text-tertiary-alt\">\n {t(I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS_HELP)}\n </p>\n </label>\n\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">\n {t(I18nKey.SETTINGS$MCP_ENVIRONMENT_VARIABLES)}\n </span>\n <OptionalTag />\n </div>\n <textarea\n data-testid=\"env-input\"\n name=\"env\"\n rows={4}\n defaultValue={formatEnvironmentVariables(server?.env)}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"KEY1=value1 KEY2=value2\"\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n </label>\n </>\n )}\n\n {testMessage && (\n <p\n data-testid=\"mcp-test-message\"\n className={\n testMessage.ok\n ? \"text-sm text-green-500 whitespace-pre-wrap\"\n : \"text-sm text-red-500 whitespace-pre-wrap\"\n }\n >\n {testMessage.text}\n </p>\n )}\n\n <div\n className={cn(\n \"flex w-full items-center gap-2\",\n onDelete ? \"justify-between\" : \"justify-end\",\n )}\n >\n {onDelete ? (\n <BrandButton\n testId=\"mcp-custom-editor-delete\"\n type=\"button\"\n variant=\"secondary\"\n onClick={onDelete}\n isDisabled={isActionDisabled}\n startContent={\n <Trash2 aria-hidden className=\"size-4\" strokeWidth={2} />\n }\n >\n {t(I18nKey.BUTTON$DELETE)}\n </BrandButton>\n ) : null}\n <div className=\"flex items-center gap-2\">\n <BrandButton\n testId=\"cancel-button\"\n type=\"button\"\n variant=\"secondary\"\n onClick={onCancel}\n isDisabled={isActionDisabled}\n >\n {t(I18nKey.BUTTON$CANCEL)}\n </BrandButton>\n {onTest && (\n <BrandButton\n testId=\"mcp-test-connection\"\n type=\"button\"\n variant=\"secondary\"\n onClick={handleTestClick}\n isDisabled={isActionDisabled || isTestPending}\n >\n {isTestPending\n ? t(I18nKey.MCP$VERIFYING)\n : t(I18nKey.MCP$TEST_BUTTON)}\n </BrandButton>\n )}\n <BrandButton\n testId=\"submit-button\"\n type=\"submit\"\n variant=\"primary\"\n isDisabled={isActionDisabled || isTestPending}\n >\n {mode === \"add\" && t(I18nKey.SETTINGS$MCP_ADD_SERVER)}\n {mode === \"edit\" && t(I18nKey.SETTINGS$MCP_SAVE_SERVER)}\n </BrandButton>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp-server-form.js","names":[],"sources":["../../../../../src/components/features/settings/mcp-settings/mcp-server-form.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Trash2 } from \"lucide-react\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { SettingsInput } from \"../settings-input\";\nimport { SettingsDropdownInput } from \"../settings-dropdown-input\";\nimport { BrandButton } from \"../brand-button\";\nimport { OptionalTag } from \"../optional-tag\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlMultilineFieldClassName } from \"#/utils/form-control-classes\";\nimport {\n isValidMcpServerName,\n MCP_SERVER_NAME_PATTERN,\n} from \"#/utils/mcp-server-name\";\nimport type {\n MCPAuthCredential,\n MCPAuthenticationConfig,\n MCPOAuthClientAuthMethod,\n} from \"#/types/mcp-auth\";\nimport type { MCPServerConfig } from \"#/types/mcp-server\";\n\ntype MCPServerType = \"sse\" | \"stdio\" | \"shttp\";\ntype RemoteAuthMode = \"none\" | \"bearer\" | \"header\" | \"oauth2\";\ntype OAuthClientAuthMethodOption = \"auto\" | MCPOAuthClientAuthMethod;\n\nexport interface TestMessage {\n ok: boolean;\n text: string;\n}\n\ninterface MCPServerFormProps {\n mode: \"add\" | \"edit\";\n server?: MCPServerConfig;\n existingServers?: MCPServerConfig[];\n onSubmit: (server: MCPServerConfig) => void;\n onCancel: () => void;\n onDelete?: () => void;\n isActionDisabled?: boolean;\n onTest?: (server: MCPServerConfig) => void;\n isTestPending?: boolean;\n testMessage?: TestMessage | null;\n}\n\nexport function MCPServerForm({\n mode,\n server,\n existingServers,\n onSubmit,\n onCancel,\n onDelete,\n isActionDisabled = false,\n onTest,\n isTestPending = false,\n testMessage = null,\n}: MCPServerFormProps) {\n const { t } = useTranslation(\"openhands\");\n const [serverType, setServerType] = React.useState<MCPServerType>(\n server?.type || \"sse\",\n );\n const [authMode, setAuthMode] = React.useState<RemoteAuthMode>(() => {\n if (server?.auth?.strategy === \"oauth2\") return \"oauth2\";\n if (server?.auth?.strategy === \"header\") return \"header\";\n if (\n server?.auth?.strategy === \"bearer\" ||\n server?.auth?.strategy === \"api_key\"\n ) {\n return \"bearer\";\n }\n return \"none\";\n });\n const [oauthClientAuthMethod, setOAuthClientAuthMethod] =\n React.useState<OAuthClientAuthMethodOption>(() =>\n server?.auth?.strategy === \"oauth2\"\n ? (server.auth.authentication?.client_auth_method ?? \"auto\")\n : \"auto\",\n );\n const [error, setError] = React.useState<string | null>(null);\n const formRef = React.useRef<HTMLFormElement>(null);\n\n const serverTypeOptions = [\n { key: \"sse\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_SSE) },\n { key: \"stdio\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_STDIO) },\n { key: \"shttp\", label: t(I18nKey.SETTINGS$MCP_SERVER_TYPE_SHTTP) },\n ];\n const authModeOptions = [\n { key: \"none\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_NONE) },\n { key: \"bearer\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_BEARER) },\n { key: \"header\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_HEADER) },\n { key: \"oauth2\", label: t(I18nKey.SETTINGS$MCP_AUTH_MODE_OAUTH) },\n ];\n const oauthClientAuthMethodOptions = [\n { key: \"auto\", label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_AUTO) },\n { key: \"none\", label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_NONE) },\n {\n key: \"client_secret_post\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_POST),\n },\n {\n key: \"client_secret_basic\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_SECRET_BASIC),\n },\n {\n key: \"private_key_jwt\",\n label: t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH_PRIVATE_KEY_JWT),\n },\n ];\n\n const validateUrl = (url: string): string | null => {\n if (!url) return t(I18nKey.SETTINGS$MCP_ERROR_URL_REQUIRED);\n try {\n const urlObj = new URL(url);\n if (![\"http:\", \"https:\"].includes(urlObj.protocol)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_URL_INVALID_PROTOCOL);\n }\n } catch {\n return t(I18nKey.SETTINGS$MCP_ERROR_URL_INVALID);\n }\n return null;\n };\n\n const validateName = (name: string): string | null => {\n if (!name) return t(I18nKey.SETTINGS$MCP_ERROR_NAME_REQUIRED);\n if (!isValidMcpServerName(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);\n }\n return null;\n };\n\n const validateNameUniqueness = (name: string): string | null => {\n if (!existingServers) return null;\n const shouldCheckUniqueness =\n mode === \"add\" || (mode === \"edit\" && server?.name !== name);\n if (!shouldCheckUniqueness) return null;\n\n const existingStdioNames = existingServers\n .filter((s) => s.type === \"stdio\")\n .map((s) => s.name)\n .filter(Boolean);\n if (existingStdioNames.includes(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_DUPLICATE);\n }\n return null;\n };\n\n const validateCommand = (command: string): string | null => {\n if (!command) return t(I18nKey.SETTINGS$MCP_ERROR_COMMAND_REQUIRED);\n if (command.includes(\" \")) {\n return t(I18nKey.SETTINGS$MCP_ERROR_COMMAND_NO_SPACES);\n }\n return null;\n };\n\n const validateUrlUniqueness = (url: string): string | null => {\n if (!existingServers) return null;\n const originalUrl = server?.url;\n const changed = mode === \"add\" || (mode === \"edit\" && originalUrl !== url);\n if (!changed) return null;\n // For URL-based servers (sse/shttp), ensure URL is unique across both types\n const exists = existingServers.some(\n (s) => (s.type === \"sse\" || s.type === \"shttp\") && s.url === url,\n );\n if (exists) return t(I18nKey.SETTINGS$MCP_ERROR_URL_DUPLICATE);\n return null;\n };\n\n const validateEnvFormat = (envString: string): string | null => {\n if (!envString.trim()) return null;\n const lines = envString.split(\"\\n\");\n for (let i = 0; i < lines.length; i += 1) {\n const trimmed = lines[i].trim();\n if (trimmed) {\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) return t(I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT);\n const key = trimmed.substring(0, eq).trim();\n if (!key) return t(I18nKey.SETTINGS$MCP_ERROR_ENV_INVALID_FORMAT);\n }\n }\n return null;\n };\n\n const validateRemoteAuth = (formData: FormData): string | null => {\n if (authMode === \"header\") {\n const headerString = formData.get(\"headers\")?.toString() || \"\";\n if (!headerString.trim())\n return t(I18nKey.SETTINGS$MCP_ERROR_HEADER_REQUIRED);\n return validateEnvFormat(headerString);\n }\n if (authMode === \"oauth2\") {\n const clientId = formData.get(\"oauth_client_id\")?.toString().trim();\n const clientSecret = formData\n .get(\"oauth_client_secret\")\n ?.toString()\n .trim();\n if (clientSecret && !clientId) {\n return t(I18nKey.SETTINGS$MCP_ERROR_OAUTH_SECRET_REQUIRES_ID);\n }\n }\n return null;\n };\n\n const validateTimeout = (timeoutStr: string): string | null => {\n if (!timeoutStr.trim()) return null; // Optional field\n\n const timeout = parseInt(timeoutStr.trim(), 10);\n if (Number.isNaN(timeout)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_INVALID_NUMBER);\n }\n if (timeout <= 0) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_POSITIVE);\n }\n if (timeout > 3600) {\n return t(I18nKey.SETTINGS$MCP_ERROR_TIMEOUT_MAX_EXCEEDED);\n }\n return null;\n };\n\n const validateStdioServer = (formData: FormData): string | null => {\n const name = formData.get(\"name\")?.toString().trim() || \"\";\n const command = formData.get(\"command\")?.toString().trim() || \"\";\n const envString = formData.get(\"env\")?.toString() || \"\";\n\n const nameError = validateName(name);\n if (nameError) return nameError;\n\n const uniquenessError = validateNameUniqueness(name);\n if (uniquenessError) return uniquenessError;\n\n const commandError = validateCommand(command);\n if (commandError) return commandError;\n\n // Validate environment variable format\n const envError = validateEnvFormat(envString);\n if (envError) return envError;\n\n return null;\n };\n\n const validateForm = (formData: FormData): string | null => {\n if (serverType === \"sse\" || serverType === \"shttp\") {\n const url = formData.get(\"url\")?.toString().trim() || \"\";\n const urlError = validateUrl(url);\n if (urlError) return urlError;\n const urlDupError = validateUrlUniqueness(url);\n if (urlDupError) return urlDupError;\n\n // The name is optional, but when provided it becomes the mcp_config\n // key (and the reference used in mcp_server_refs), so hold it to the\n // same safe-identifier rule as stdio names.\n const name = formData.get(\"name\")?.toString().trim() || \"\";\n if (name && !isValidMcpServerName(name)) {\n return t(I18nKey.SETTINGS$MCP_ERROR_NAME_INVALID);\n }\n\n // Validate timeout for SHTTP servers only\n if (serverType === \"shttp\") {\n const timeoutStr = formData.get(\"timeout\")?.toString() || \"\";\n const timeoutError = validateTimeout(timeoutStr);\n if (timeoutError) return timeoutError;\n }\n\n return validateRemoteAuth(formData);\n }\n\n if (serverType === \"stdio\") {\n return validateStdioServer(formData);\n }\n\n return null;\n };\n\n const parseEnvironmentVariables = (\n envString: string,\n ): Record<string, string> => {\n const env: Record<string, string> = {};\n const input = envString.trim();\n if (!input) return env;\n\n for (const line of input.split(\"\\n\")) {\n const trimmed = line.trim();\n const eq = trimmed.indexOf(\"=\");\n const key = eq >= 0 ? trimmed.substring(0, eq).trim() : \"\";\n if (trimmed && eq !== -1 && key) {\n env[key] = trimmed.substring(eq + 1).trim();\n }\n }\n return env;\n };\n\n const formatEnvironmentVariables = (\n env?: Record<string, string> | null,\n ): string => {\n if (!env) return \"\";\n return Object.entries(env)\n .map(([key, value]) => `${key}=${value}`)\n .join(\"\\n\");\n };\n\n const editableAuthValue = (auth: MCPAuthCredential | undefined): string => {\n if (auth?.strategy === \"bearer\" || auth?.strategy === \"api_key\") {\n return auth.value ?? \"\";\n }\n return \"\";\n };\n\n const editableHeaderValue = (auth: MCPAuthCredential | undefined): string => {\n if (auth?.strategy !== \"header\") return \"\";\n return formatEnvironmentVariables(auth.headers);\n };\n\n const oauthAuthentication =\n server?.auth?.strategy === \"oauth2\"\n ? server.auth.authentication\n : undefined;\n const oauthState =\n server?.auth?.strategy === \"oauth2\" ? server.auth.state : undefined;\n\n const authFromFormData = (\n formData: FormData,\n ): MCPAuthCredential | undefined => {\n if (authMode === \"none\") return undefined;\n if (authMode === \"bearer\") {\n const value = formData.get(\"api_key\")?.toString().trim();\n if (!value) return undefined;\n if (server?.auth?.strategy === \"api_key\") {\n return { ...server.auth, value };\n }\n return { strategy: \"bearer\", value };\n }\n if (authMode === \"header\") {\n const headers = parseEnvironmentVariables(\n formData.get(\"headers\")?.toString() || \"\",\n );\n return { strategy: \"header\", headers };\n }\n\n const scopes = formData.get(\"oauth_scopes\")?.toString().trim();\n const clientId = formData.get(\"oauth_client_id\")?.toString().trim();\n const clientSecret = formData.get(\"oauth_client_secret\")?.toString().trim();\n const authentication: MCPAuthenticationConfig = {\n type: \"oauth\",\n ...(oauthClientAuthMethod !== \"auto\" && {\n client_auth_method: oauthClientAuthMethod,\n }),\n ...(scopes && { scopes }),\n ...(clientId && { client_id: clientId }),\n ...(clientSecret && { client_secret: clientSecret }),\n };\n return {\n strategy: \"oauth2\",\n authentication,\n ...(oauthState && { state: oauthState }),\n };\n };\n\n const buildConfig = (formData: FormData): MCPServerConfig => {\n const baseConfig = {\n id: server?.id || `${serverType}-${Date.now()}`,\n type: serverType,\n // The form has no enable/disable control (that lives on the card), so\n // carry the existing state forward — otherwise saving an edit to a\n // disabled server would silently switch it back on.\n ...(server?.enabled === false && { enabled: false }),\n };\n\n if (serverType === \"sse\" || serverType === \"shttp\") {\n const name = formData.get(\"name\")?.toString().trim();\n const url = formData.get(\"url\")?.toString().trim();\n const timeoutStr = formData.get(\"timeout\")?.toString().trim();\n const auth = authFromFormData(formData);\n\n const serverConfig: MCPServerConfig = {\n ...baseConfig,\n ...(name && { name }),\n url: url!,\n ...(auth && { auth }),\n };\n\n // Only add timeout for SHTTP servers\n if (serverType === \"shttp\" && timeoutStr) {\n const timeoutValue = parseInt(timeoutStr, 10);\n if (!Number.isNaN(timeoutValue)) {\n serverConfig.timeout = timeoutValue;\n }\n }\n\n return serverConfig;\n }\n\n // stdio\n const name = formData.get(\"name\")?.toString().trim();\n const command = formData.get(\"command\")?.toString().trim();\n const argsString = formData.get(\"args\")?.toString().trim();\n const envString = formData.get(\"env\")?.toString().trim();\n\n const args = argsString\n ? argsString\n .split(\"\\n\")\n .map((arg) => arg.trim())\n .filter(Boolean)\n : [];\n const env = parseEnvironmentVariables(envString || \"\");\n\n return {\n ...baseConfig,\n name: name!,\n command: command!,\n ...(args.length > 0 && { args }),\n ...(Object.keys(env).length > 0 && { env }),\n };\n };\n\n const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setError(null);\n\n const formData = new FormData(event.currentTarget);\n const validationError = validateForm(formData);\n\n if (validationError) {\n setError(validationError);\n return;\n }\n\n onSubmit(buildConfig(formData));\n };\n\n const handleTestClick = () => {\n if (!onTest || !formRef.current) return;\n setError(null);\n const formData = new FormData(formRef.current);\n const validationError = validateForm(formData);\n if (validationError) {\n setError(validationError);\n return;\n }\n onTest(buildConfig(formData));\n };\n\n const formTestId =\n mode === \"add\" ? \"add-mcp-server-form\" : \"edit-mcp-server-form\";\n\n return (\n <form\n ref={formRef}\n data-testid={formTestId}\n onSubmit={handleSubmit}\n className=\"flex flex-col items-start gap-6\"\n noValidate\n >\n {mode === \"add\" && (\n <SettingsDropdownInput\n testId=\"server-type-dropdown\"\n name=\"server-type\"\n label={t(I18nKey.SETTINGS$MCP_SERVER_TYPE)}\n items={serverTypeOptions}\n selectedKey={serverType}\n onSelectionChange={(key) => setServerType(key as MCPServerType)}\n onInputChange={() => {}} // Prevent input changes\n isClearable={false}\n allowsCustomValue={false}\n required\n wrapperClassName=\"w-full min-w-0\"\n />\n )}\n\n {error && <p className=\"text-red-500 text-sm\">{error}</p>}\n\n {(serverType === \"sse\" || serverType === \"shttp\") && (\n <>\n <SettingsInput\n testId=\"server-name-input\"\n name=\"name\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_SERVER_NAME)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={server?.name || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"my_search_server\"\n pattern={MCP_SERVER_NAME_PATTERN.source}\n />\n\n <SettingsInput\n testId=\"url-input\"\n name=\"url\"\n type=\"url\"\n label={t(I18nKey.SETTINGS$MCP_URL)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.url || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"https://api.example.com\"\n />\n\n <SettingsDropdownInput\n testId=\"auth-mode-dropdown\"\n name=\"auth-mode\"\n label={t(I18nKey.SETTINGS$MCP_AUTHENTICATION)}\n items={authModeOptions}\n selectedKey={authMode}\n onSelectionChange={(key) => setAuthMode(key as RemoteAuthMode)}\n onInputChange={() => {}}\n isClearable={false}\n allowsCustomValue={false}\n wrapperClassName=\"w-full min-w-0\"\n />\n\n {authMode === \"bearer\" && (\n <SettingsInput\n testId=\"api-key-input\"\n name=\"api_key\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_API_KEY)}\n className=\"w-full min-w-0\"\n required\n defaultValue={editableAuthValue(server?.auth)}\n placeholder={t(I18nKey.SETTINGS$MCP_API_KEY_PLACEHOLDER)}\n />\n )}\n\n {authMode === \"header\" && (\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <span className=\"text-sm\">{t(I18nKey.SETTINGS$MCP_HEADERS)}</span>\n <textarea\n data-testid=\"headers-input\"\n name=\"headers\"\n rows={4}\n defaultValue={editableHeaderValue(server?.auth)}\n placeholder={t(I18nKey.SETTINGS$MCP_HEADERS_PLACEHOLDER)}\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n </label>\n )}\n\n {authMode === \"oauth2\" && (\n <>\n <SettingsDropdownInput\n testId=\"oauth-client-auth-method-dropdown\"\n name=\"oauth_client_auth_method\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_AUTH)}\n items={oauthClientAuthMethodOptions}\n selectedKey={oauthClientAuthMethod}\n onSelectionChange={(key) =>\n setOAuthClientAuthMethod(key as OAuthClientAuthMethodOption)\n }\n onInputChange={() => {}}\n isClearable={false}\n allowsCustomValue={false}\n wrapperClassName=\"w-full min-w-0\"\n />\n <SettingsInput\n testId=\"oauth-client-id-input\"\n name=\"oauth_client_id\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={oauthAuthentication?.client_id || \"\"}\n placeholder={t(\n I18nKey.SETTINGS$MCP_OAUTH_CLIENT_ID_PLACEHOLDER,\n )}\n />\n <SettingsInput\n testId=\"oauth-client-secret-input\"\n name=\"oauth_client_secret\"\n type=\"password\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={oauthAuthentication?.client_secret || \"\"}\n placeholder={t(\n I18nKey.SETTINGS$MCP_OAUTH_CLIENT_SECRET_PLACEHOLDER,\n )}\n />\n <SettingsInput\n testId=\"oauth-scopes-input\"\n name=\"oauth_scopes\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_OAUTH_SCOPES)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={\n Array.isArray(oauthAuthentication?.scopes)\n ? oauthAuthentication.scopes.join(\" \")\n : oauthAuthentication?.scopes || \"\"\n }\n placeholder={t(I18nKey.SETTINGS$MCP_OAUTH_SCOPES_PLACEHOLDER)}\n />\n </>\n )}\n\n {serverType === \"shttp\" && (\n <SettingsInput\n testId=\"timeout-input\"\n name=\"timeout\"\n type=\"number\"\n label={t(I18nKey.SETTINGS$MCP_TIMEOUT_LABEL)}\n className=\"w-full min-w-0\"\n showOptionalTag\n defaultValue={server?.timeout?.toString() || \"\"}\n placeholder=\"60\"\n min={1}\n max={3600}\n />\n )}\n </>\n )}\n\n {serverType === \"stdio\" && (\n <>\n <SettingsInput\n testId=\"name-input\"\n name=\"name\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_NAME)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.name || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"my_mcp_server\"\n pattern={MCP_SERVER_NAME_PATTERN.source}\n />\n\n <SettingsInput\n testId=\"command-input\"\n name=\"command\"\n type=\"text\"\n label={t(I18nKey.SETTINGS$MCP_COMMAND)}\n className=\"w-full min-w-0\"\n required\n defaultValue={server?.command || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"npx\"\n />\n\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">\n {t(I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS)}\n </span>\n <OptionalTag />\n </div>\n <textarea\n data-testid=\"args-input\"\n name=\"args\"\n rows={3}\n defaultValue={server?.args?.join(\"\\n\") || \"\"}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"arg1 arg2 arg3\"\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n <p className=\"text-xs text-tertiary-alt\">\n {t(I18nKey.SETTINGS$MCP_COMMAND_ARGUMENTS_HELP)}\n </p>\n </label>\n\n <label className=\"flex flex-col gap-2.5 w-full min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">\n {t(I18nKey.SETTINGS$MCP_ENVIRONMENT_VARIABLES)}\n </span>\n <OptionalTag />\n </div>\n <textarea\n data-testid=\"env-input\"\n name=\"env\"\n rows={4}\n defaultValue={formatEnvironmentVariables(server?.env)}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"KEY1=value1 KEY2=value2\"\n className={cn(\n formControlMultilineFieldClassName,\n \"resize-none placeholder:italic\",\n \"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]\",\n )}\n />\n </label>\n </>\n )}\n\n {testMessage && (\n <p\n data-testid=\"mcp-test-message\"\n className={\n testMessage.ok\n ? \"text-sm text-green-500 whitespace-pre-wrap\"\n : \"text-sm text-red-500 whitespace-pre-wrap\"\n }\n >\n {testMessage.text}\n </p>\n )}\n\n <div\n className={cn(\n \"flex w-full items-center gap-2\",\n onDelete ? \"justify-between\" : \"justify-end\",\n )}\n >\n {onDelete ? (\n <BrandButton\n testId=\"mcp-custom-editor-delete\"\n type=\"button\"\n variant=\"secondary\"\n onClick={onDelete}\n isDisabled={isActionDisabled}\n startContent={\n <Trash2 aria-hidden className=\"size-4\" strokeWidth={2} />\n }\n >\n {t(I18nKey.BUTTON$DELETE)}\n </BrandButton>\n ) : null}\n <div className=\"flex items-center gap-2\">\n <BrandButton\n testId=\"cancel-button\"\n type=\"button\"\n variant=\"secondary\"\n onClick={onCancel}\n isDisabled={isActionDisabled}\n >\n {t(I18nKey.BUTTON$CANCEL)}\n </BrandButton>\n {onTest && (\n <BrandButton\n testId=\"mcp-test-connection\"\n type=\"button\"\n variant=\"secondary\"\n onClick={handleTestClick}\n isDisabled={isActionDisabled || isTestPending}\n >\n {isTestPending\n ? t(I18nKey.MCP$VERIFYING)\n : t(I18nKey.MCP$TEST_BUTTON)}\n </BrandButton>\n )}\n <BrandButton\n testId=\"submit-button\"\n type=\"submit\"\n variant=\"primary\"\n isDisabled={isActionDisabled || isTestPending}\n >\n {mode === \"add\" && t(I18nKey.SETTINGS$MCP_ADD_SERVER)}\n {mode === \"edit\" && t(I18nKey.SETTINGS$MCP_SAVE_SERVER)}\n </BrandButton>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
package/dist/config/defaults.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require("../_virtual/_rolldown/runtime.cjs");var e={_comment:`Single source of truth for version pins, ports, paths, and defaults shared across the npm and Docker install paths. Read by scripts/dev-safe.mjs, scripts/dev-with-automation.mjs, docker/entrypoint.sh (via generated defaults.env), and .github/workflows/docker.yml.`,versions:{agentServer:`1.
|
|
1
|
+
require("../_virtual/_rolldown/runtime.cjs");var e={_comment:`Single source of truth for version pins, ports, paths, and defaults shared across the npm and Docker install paths. Read by scripts/dev-safe.mjs, scripts/dev-with-automation.mjs, docker/entrypoint.sh (via generated defaults.env), and .github/workflows/docker.yml.`,versions:{agentServer:`1.39.1`,agentCanvas:`1.8.0`,automation:`1.5.0`},compatibility:{minimumAgentServer:`1.28.0`},constraints:{_comment:`Temporary upper bound on the agent-client-protocol (acp) transitive dependency. openhands-sdk 1.35.0 requires agent-client-protocol>=0.10.1 with no upper bound, but acp 0.11.0 reordered the ACP prompt() arguments and breaks the SDK's ACP client (a PromptRequest validation error surfaces as 'ACP error: 2 validation errors for PromptRequest'). Pin acp <0.11 until a fixed openhands-sdk ships. Consumed by scripts/dev-safe.mjs (agent-server uvx install) and .github/workflows/mock-llm-e2e.yml (mock ACP server venv).`,agentClientProtocol:`agent-client-protocol<0.11`},images:{agentServer:`ghcr.io/openhands/agent-server`,agentCanvas:`ghcr.io/openhands/agent-canvas`},ports:{agentServer:18e3,automation:18001,proxy:8e3},paths:{stateSubdir:`agent-canvas`,conversations:`agent-canvas/conversations`,bashEvents:`agent-canvas/bash_events`,automationDb:`automation/automations.db`,canvasBasePath:`/canvas`},packages:{agentServer:`openhands-agent-server`,automation:`openhands-automation`,tools:`openhands-tools`,workspace:`openhands-workspace`},telemetry:{posthogApiKey:`phc_kBtz5nKmxVRRQ7HtPwr2QX9eMC5j65zE86QKocVNwb4U`,posthogHost:`https://us.i.posthog.com`}};exports.default=e;
|
|
2
2
|
//# sourceMappingURL=defaults.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.cjs","names":[],"sources":["../../config/defaults.json"],"sourcesContent":["{\n \"_comment\": \"Single source of truth for version pins, ports, paths, and defaults shared across the npm and Docker install paths. Read by scripts/dev-safe.mjs, scripts/dev-with-automation.mjs, docker/entrypoint.sh (via generated defaults.env), and .github/workflows/docker.yml.\",\n \"versions\": {\n \"agentServer\": \"1.
|
|
1
|
+
{"version":3,"file":"defaults.cjs","names":[],"sources":["../../config/defaults.json"],"sourcesContent":["{\n \"_comment\": \"Single source of truth for version pins, ports, paths, and defaults shared across the npm and Docker install paths. Read by scripts/dev-safe.mjs, scripts/dev-with-automation.mjs, docker/entrypoint.sh (via generated defaults.env), and .github/workflows/docker.yml.\",\n \"versions\": {\n \"agentServer\": \"1.39.1\",\n \"agentCanvas\": \"1.8.0\",\n \"automation\": \"1.5.0\"\n },\n \"compatibility\": {\n \"minimumAgentServer\": \"1.28.0\"\n },\n \"constraints\": {\n \"_comment\": \"Temporary upper bound on the agent-client-protocol (acp) transitive dependency. openhands-sdk 1.35.0 requires agent-client-protocol>=0.10.1 with no upper bound, but acp 0.11.0 reordered the ACP prompt() arguments and breaks the SDK's ACP client (a PromptRequest validation error surfaces as 'ACP error: 2 validation errors for PromptRequest'). Pin acp <0.11 until a fixed openhands-sdk ships. Consumed by scripts/dev-safe.mjs (agent-server uvx install) and .github/workflows/mock-llm-e2e.yml (mock ACP server venv).\",\n \"agentClientProtocol\": \"agent-client-protocol<0.11\"\n },\n \"images\": {\n \"agentServer\": \"ghcr.io/openhands/agent-server\",\n \"agentCanvas\": \"ghcr.io/openhands/agent-canvas\"\n },\n \"ports\": {\n \"agentServer\": 18000,\n \"automation\": 18001,\n \"proxy\": 8000\n },\n \"paths\": {\n \"stateSubdir\": \"agent-canvas\",\n \"conversations\": \"agent-canvas/conversations\",\n \"bashEvents\": \"agent-canvas/bash_events\",\n \"automationDb\": \"automation/automations.db\",\n \"canvasBasePath\": \"/canvas\"\n },\n \"packages\": {\n \"agentServer\": \"openhands-agent-server\",\n \"automation\": \"openhands-automation\",\n \"tools\": \"openhands-tools\",\n \"workspace\": \"openhands-workspace\"\n },\n \"telemetry\": {\n \"posthogApiKey\": \"phc_kBtz5nKmxVRRQ7HtPwr2QX9eMC5j65zE86QKocVNwb4U\",\n \"posthogHost\": \"https://us.i.posthog.com\"\n }\n}\n"],"mappings":""}
|
package/dist/config/defaults.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var e = {
|
|
2
2
|
_comment: "Single source of truth for version pins, ports, paths, and defaults shared across the npm and Docker install paths. Read by scripts/dev-safe.mjs, scripts/dev-with-automation.mjs, docker/entrypoint.sh (via generated defaults.env), and .github/workflows/docker.yml.",
|
|
3
3
|
versions: {
|
|
4
|
-
agentServer: "1.
|
|
5
|
-
agentCanvas: "1.
|
|
6
|
-
automation: "1.
|
|
4
|
+
agentServer: "1.39.1",
|
|
5
|
+
agentCanvas: "1.8.0",
|
|
6
|
+
automation: "1.5.0"
|
|
7
7
|
},
|
|
8
8
|
compatibility: { minimumAgentServer: "1.28.0" },
|
|
9
9
|
constraints: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.js","names":[],"sources":["../../config/defaults.json"],"sourcesContent":["{\n \"_comment\": \"Single source of truth for version pins, ports, paths, and defaults shared across the npm and Docker install paths. Read by scripts/dev-safe.mjs, scripts/dev-with-automation.mjs, docker/entrypoint.sh (via generated defaults.env), and .github/workflows/docker.yml.\",\n \"versions\": {\n \"agentServer\": \"1.
|
|
1
|
+
{"version":3,"file":"defaults.js","names":[],"sources":["../../config/defaults.json"],"sourcesContent":["{\n \"_comment\": \"Single source of truth for version pins, ports, paths, and defaults shared across the npm and Docker install paths. Read by scripts/dev-safe.mjs, scripts/dev-with-automation.mjs, docker/entrypoint.sh (via generated defaults.env), and .github/workflows/docker.yml.\",\n \"versions\": {\n \"agentServer\": \"1.39.1\",\n \"agentCanvas\": \"1.8.0\",\n \"automation\": \"1.5.0\"\n },\n \"compatibility\": {\n \"minimumAgentServer\": \"1.28.0\"\n },\n \"constraints\": {\n \"_comment\": \"Temporary upper bound on the agent-client-protocol (acp) transitive dependency. openhands-sdk 1.35.0 requires agent-client-protocol>=0.10.1 with no upper bound, but acp 0.11.0 reordered the ACP prompt() arguments and breaks the SDK's ACP client (a PromptRequest validation error surfaces as 'ACP error: 2 validation errors for PromptRequest'). Pin acp <0.11 until a fixed openhands-sdk ships. Consumed by scripts/dev-safe.mjs (agent-server uvx install) and .github/workflows/mock-llm-e2e.yml (mock ACP server venv).\",\n \"agentClientProtocol\": \"agent-client-protocol<0.11\"\n },\n \"images\": {\n \"agentServer\": \"ghcr.io/openhands/agent-server\",\n \"agentCanvas\": \"ghcr.io/openhands/agent-canvas\"\n },\n \"ports\": {\n \"agentServer\": 18000,\n \"automation\": 18001,\n \"proxy\": 8000\n },\n \"paths\": {\n \"stateSubdir\": \"agent-canvas\",\n \"conversations\": \"agent-canvas/conversations\",\n \"bashEvents\": \"agent-canvas/bash_events\",\n \"automationDb\": \"automation/automations.db\",\n \"canvasBasePath\": \"/canvas\"\n },\n \"packages\": {\n \"agentServer\": \"openhands-agent-server\",\n \"automation\": \"openhands-automation\",\n \"tools\": \"openhands-tools\",\n \"workspace\": \"openhands-workspace\"\n },\n \"telemetry\": {\n \"posthogApiKey\": \"phc_kBtz5nKmxVRRQ7HtPwr2QX9eMC5j65zE86QKocVNwb4U\",\n \"posthogHost\": \"https://us.i.posthog.com\"\n }\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../api/backend-registry/health-store.cjs"),n=require("../api/backend-registry/default-backend.cjs"),r=require("../api/backend-registry/active-store.cjs"),i=require("../api/agent-server-compatibility.cjs"),a=require("../hooks/query/query-keys.cjs"),o=require("../query-client-config.cjs");let
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../api/backend-registry/health-store.cjs"),n=require("../api/backend-registry/default-backend.cjs"),r=require("../api/backend-registry/active-store.cjs"),i=require("../api/agent-server-compatibility.cjs"),a=require("../hooks/query/query-keys.cjs"),o=require("../query-client-config.cjs"),s=require("../services/telemetry.cjs");let c=require("react");c=e.__toESM(c,1);let l=require("react/jsx-runtime");var u=c.default.createContext(null);function d(){return typeof crypto<`u`&&typeof crypto.randomUUID==`function`?crypto.randomUUID():`backend-${Date.now()}-${Math.random().toString(36).slice(2,10)}`}function f(e,t){let n=e.backend.kind===`cloud`,r=t.backend.kind===`cloud`;!n&&!r||(s.setTelemetryCloudContext(null),r&&(!n||e.backend.id!==t.backend.id)&&s.setTelemetryIdentity(null))}function p({children:e}){let n=c.default.useSyncExternalStore(r.subscribeActiveBackend,r.getSnapshot,r.getSnapshot),p=c.default.useCallback(()=>{i.clearCachedAgentServerInfo(),o.queryClient.invalidateQueries({queryKey:a.QUERY_KEYS.WEB_CLIENT_CONFIG})},[]),m=c.default.useCallback((e,t)=>{let n=r.getSnapshot(),i=r.getActiveSelection()?.backendId??null,a=r.getActiveSelection()?.orgId??null,o=t??null;if(e===i&&o===a)return;let s=r.getRegisteredBackends(),c=s.find(t=>t.id===e),l={backend:c??s[0]??r.NO_BACKEND,orgId:c?o:null};f(n.active,l),r.setActiveSelection({backendId:e,orgId:o}),p()},[p]),h=c.default.useCallback(e=>{let t=r.getSnapshot().active,n={...e,id:d()},i=[...r.getRegisteredBackends(),n];return f(t,{backend:n,orgId:null}),r.setRegisteredBackends(i),r.setActiveSelection({backendId:n.id}),p(),n},[p]),g=c.default.useCallback((e,n)=>{let i=r.getRegisteredBackends().find(t=>t.id===e),a=r.getSnapshot().active.backend.id,o=n.host!==void 0&&i!==void 0&&n.host!==i.host,c=n.apiKey!==void 0&&i!==void 0&&n.apiKey!==i.apiKey;r.setRegisteredBackends(r.getRegisteredBackends().map(t=>t.id===e?{...t,...n,connectionRevision:o||c?(t.connectionRevision??0)+1:t.connectionRevision}:t)),(o||c)&&(t.resetBackendHealth(e),a===e&&(i?.kind===`cloud`&&(s.setTelemetryCloudContext(null),s.setTelemetryIdentity(null)),p()))},[p]),_=c.default.useCallback(e=>{let n=r.getRegisteredBackends().find(t=>t.id===e),i=n?.kind===`cloud`&&r.getSnapshot().active.backend.id===n.id;r.setRegisteredBackends(r.getRegisteredBackends().filter(t=>t.id!==e)),i&&(s.setTelemetryCloudContext(null),s.setTelemetryIdentity(null)),t.dropBackendHealth(e),p()},[p]),v=c.default.useMemo(()=>({backends:n.backends,active:n.active,setActive:m,addBackend:h,updateBackend:g,removeBackend:_}),[n,m,h,g,_]);return(0,l.jsx)(u.Provider,{value:v,children:e})}function m(){let e=c.default.useContext(u);if(!e)throw Error(`useActiveBackendContext must be used inside <ActiveBackendProvider>`);return e}function h(){let e=c.default.useContext(u);return e?e.active:{backend:n.makeDefaultLocalBackend()??r.NO_BACKEND,orgId:null}}exports.ActiveBackendProvider=p,exports.useActiveBackend=h,exports.useActiveBackendContext=m;
|
|
2
2
|
//# sourceMappingURL=active-backend-context.cjs.map
|