UNPKG

n8n-editor-ui

Version:

Workflow Editor UI for n8n

1,213 lines (1,212 loc) 47.5 kB
import { C as computed, Cn as toDisplayString, D as createElementBlock, E as createCommentVNode, Gt as unref, It as ref, M as createVNode, P as defineComponent, T as createBlock, Z as onMounted, _t as watch, at as renderSlot, bt as withCtx, et as openBlock, j as createTextVNode, k as createSlots, p as vShow, vn as normalizeClass, w as createBaseVNode, xt as withDirectives } from "./vue.runtime.esm-bundler-tP5dCd7J.js"; import { _t as useI18n } from "./_MapCache-ficiegRb.js"; import { Ct as N8nActionBox_default, D as N8nPopover_default, F as N8nMarkdown_default, R as N8nInfoTip_default, Sn as N8nButton_default, T as N8nRadioButtons_default, Tt as ElSwitch, at as N8nLoading_default, ht as N8nTooltip_default, it as N8nActionToggle_default, k as N8nNotice_default, mt as N8nLink_default, u as N8nDataTableServer_default, wn as N8nIcon_default, xn as N8nText_default, y as N8nTabs_default, yn as N8nHeading_default, yt as N8nInput_default } from "./src-DgvumQTi.js"; import "./en-CF30SCh2.js"; import "./preload-helper-CR0ecmWK.js"; import { t as __plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BwBpWJRZ.js"; import "./truncate-B1HVeveJ.js"; import { Dr as useUsersStore, O as useTelemetry, Yn as useDocumentTitle, oa as useUIStore, v as useToast } from "./builder.store-sBTWwxRU.js"; import "./empty-BuGRxzl4.js"; import "./sanitize-html-Cc45ZKm8.js"; import "./CalendarDate-zWqgZMlk.js"; import "./path-browserify-BtCDmZ3_.js"; import { Oo as getResourcePermissions, Ro as VIEWS, hs as WORKFLOW_DESCRIPTION_MODAL_KEY } from "./constants-BbpucWL4.js"; import "./merge-k8vSyyXH.js"; import { r as useRootStore } from "./_baseOrderBy-B_6CV8x6.js"; import "./dateformat-CM9k0--B.js"; import "./useDebounce-Bnb8W2LR.js"; import "./executions.store-CXjGPxs0.js"; import "./usePinnedData-CL2qWhwp.js"; import "./nodeCreator.store-FJrC_Pu6.js"; import "./nodeIcon-DI2Acud1.js"; import "./useCanvasOperations-CAHezq7d.js"; import "./folders.store-BjAwtSH4.js"; import "./NodeIcon-BVhrZNYI.js"; import "./versions.store-BwdT833-.js"; import "./banners.store-C9sgKjcT.js"; import "./dataTable.store-B4A4ieSD.js"; import { t as useClipboard } from "./useClipboard-CyG-SBdG.js"; import { t as TimeAgo_default } from "./TimeAgo-CWvYoS88.js"; import "./roles.store-DcLW4g80.js"; import { t as router_default } from "./router-BuH0cEsQ.js"; import "./sso.store-Ba2eVE3K.js"; import "./insights.store-BBo4cRMD.js"; import "./insights.constants-C8GITe6H.js"; import "./insights.utils-B7OXsqrQ.js"; import { a as MCP_DOCS_PAGE_URL, i as MCP_CONNECT_WORKFLOWS_MODAL_KEY, l as MCP_TOOLTIP_DELAY, n as LOADING_INDICATOR_TIMEOUT, o as MCP_ENDPOINT, r as MCP_CONNECT_POPOVER_WIDTH } from "./mcp.constants-DQPD3Ntp.js"; import "./constants-DLTaaipl.js"; import "./useRecentResources-BAGmp8Wq.js"; import { t as useMcp } from "./useMcp-DzTrtFvb.js"; import { t as useMCPStore } from "./mcp.store-Da5JCu0N.js"; import { t as WorkflowLocation_default } from "./WorkflowLocation-BSRxq9Yd.js"; var _hoisted_1$4 = { key: 0 }; var _hoisted_2$3 = { key: 1 }; var _hoisted_3$2 = ["href"]; var MCPEmptyState_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "MCPEmptyState", props: { disabled: { type: Boolean, default: false }, loading: { type: Boolean, default: false } }, emits: ["turnOnMcp"], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; const i18n = useI18n(); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container), "data-test-id": "mcp-empty-state-container" }, [createVNode(unref(N8nActionBox_default), { icon: { type: "icon", value: "mcp" }, heading: unref(i18n).baseText("settings.mcp.actionBox.heading"), description: unref(i18n).baseText("settings.mcp.description"), "button-text": unref(i18n).baseText("settings.mcp.actionBox.button.label"), "button-disabled": props.disabled || props.loading, "button-variant": "primary", "data-test-id": "enable-mcp-access-button", "onClick:button": _cache[0] || (_cache[0] = ($event) => emit("turnOnMcp")) }, { disabledButtonTooltip: withCtx(() => [props.loading ? (openBlock(), createElementBlock("span", _hoisted_1$4, toDisplayString(unref(i18n).baseText("generic.loading")) + "...", 1)) : props.disabled ? (openBlock(), createElementBlock("span", _hoisted_2$3, toDisplayString(unref(i18n).baseText("settings.mcp.toggle.disabled.tooltip")), 1)) : createCommentVNode("", true)]), additionalContent: withCtx(() => [createVNode(unref(N8nText_default), { color: "text-light", size: "small" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.emptyState.docs.part1")) + " ", 1), createBaseVNode("a", { href: unref(MCP_DOCS_PAGE_URL), class: normalizeClass(_ctx.$style["docs-link"]), target: "_blank", rel: "noopener noreferrer" }, toDisplayString(unref(i18n).baseText("generic.learnMore").toLowerCase()), 11, _hoisted_3$2)]), _: 1 })]), _: 1 }, 8, [ "heading", "description", "button-text", "button-disabled" ])], 2); }; } }); var MCPEmptyState_vue_vue_type_style_index_0_lang_module_default = { container: "_container_bvbc2_123", "docs-link": "_docs-link_bvbc2_132" }; var MCPEmptyState_default = /* @__PURE__ */ __plugin_vue_export_helper_default(MCPEmptyState_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": MCPEmptyState_vue_vue_type_style_index_0_lang_module_default }]]); var McpAccessToggle_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "McpAccessToggle", props: { modelValue: { type: Boolean }, disabled: { type: Boolean, default: false }, loading: { type: Boolean, default: false } }, emits: ["disableMcpAccess"], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; const i18n = useI18n(); const onUpdateMCPEnabled = () => { emit("disableMcpAccess"); }; return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style["main-toggle-container"]) }, [createBaseVNode("div", { class: normalizeClass(_ctx.$style["main-toggle-info"]) }, [createVNode(unref(N8nText_default), { bold: true, color: _ctx.modelValue ? `success` : `text-light`, size: "small" }, { default: withCtx(() => [createTextVNode(toDisplayString(_ctx.modelValue ? unref(i18n).baseText("settings.mcp.header.toggle.enabled") : unref(i18n).baseText("settings.mcp.header.toggle.disabled")), 1)]), _: 1 }, 8, ["color"])], 2), createBaseVNode("div", { class: normalizeClass(_ctx.$style["main-toggle"]), "data-test-id": "mcp-toggle-container" }, [createVNode(unref(N8nTooltip_default), { content: unref(i18n).baseText("settings.mcp.toggle.disabled.tooltip"), disabled: !props.disabled, placement: "top" }, { default: withCtx(() => [createVNode(unref(ElSwitch), { size: "large", "data-test-id": "mcp-access-toggle", "model-value": props.modelValue, disabled: props.disabled, loading: props.loading, "onUpdate:modelValue": onUpdateMCPEnabled }, null, 8, [ "model-value", "disabled", "loading" ])]), _: 1 }, 8, ["content", "disabled"])], 2)], 2); }; } }); var McpAccessToggle_vue_vue_type_style_index_0_lang_module_default = { "main-toggle-container": "_main-toggle-container_13hh6_123", "main-toggle": "_main-toggle_13hh6_123" }; var McpAccessToggle_default = /* @__PURE__ */ __plugin_vue_export_helper_default(McpAccessToggle_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": McpAccessToggle_vue_vue_type_style_index_0_lang_module_default }]]); var _hoisted_1$3 = ["for"]; var _hoisted_2$2 = ["id"]; var ConnectionParameter_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "ConnectionParameter", props: { id: {}, label: {}, value: {}, valueLoading: { type: Boolean, default: false }, infoTip: { default: void 0 }, allowCopy: { type: Boolean, default: true } }, emits: ["copy"], setup(__props, { emit: __emit }) { const { copy, copied, isSupported } = useClipboard(); const i18n = useI18n(); const props = __props; const emit = __emit; const handleCopy = async (value) => { await copy(value); emit("copy", value); }; return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container) }, [createBaseVNode("div", { class: normalizeClass(_ctx.$style["label-wrapper"]) }, [createBaseVNode("label", { class: normalizeClass(_ctx.$style.label), for: `connection-parameter-${props.id}` }, toDisplayString(props.label), 11, _hoisted_1$3), props.infoTip ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass(_ctx.$style["info-tip"]) }, [createVNode(unref(N8nInfoTip_default), { type: "tooltip", size: "small" }, { default: withCtx(() => [createTextVNode(toDisplayString(props.infoTip), 1)]), _: 1 })], 2)) : createCommentVNode("", true)], 2), createBaseVNode("div", { id: `connection-parameter-${props.id}`, class: normalizeClass({ [_ctx.$style["parameter-value"]]: true, [_ctx.$style["parameter-value--loading"]]: props.valueLoading }), "data-test-id": "connection-parameter-value" }, [createBaseVNode("div", { class: normalizeClass(_ctx.$style["input-wrapper"]) }, [props.valueLoading ? (openBlock(), createBlock(unref(N8nLoading_default), { key: 0, loading: props.valueLoading, variant: "h1", class: normalizeClass(_ctx.$style["parameter-skeleton"]) }, null, 8, ["loading", "class"])) : (openBlock(), createBlock(unref(N8nInput_default), { key: 1, modelValue: props.value, "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => props.value = $event), type: "text", readonly: true }, null, 8, ["modelValue"]))], 2), createBaseVNode("div", { class: normalizeClass(_ctx.$style["copy-button-wrapper"]) }, [renderSlot(_ctx.$slots, "customActions"), createVNode(unref(N8nTooltip_default), { disabled: !unref(isSupported), content: unref(copied) ? unref(i18n).baseText("generic.copied") : unref(i18n).baseText("generic.copy"), "show-after": unref(100), placement: "bottom" }, { default: withCtx(() => [props.allowCopy && unref(isSupported) ? (openBlock(), createBlock(unref(N8nButton_default), { key: 0, type: "tertiary", icon: unref(copied) ? "check" : "copy", square: true, class: normalizeClass(_ctx.$style["copy-button"]), disabled: props.valueLoading, onClick: _cache[1] || (_cache[1] = ($event) => handleCopy(props.value)) }, null, 8, [ "icon", "class", "disabled" ])) : createCommentVNode("", true)]), _: 1 }, 8, [ "disabled", "content", "show-after" ])], 2)], 10, _hoisted_2$2)], 2); }; } }); const container$4 = "_container_1p18l_123"; const label$1 = "_label_1p18l_129"; var ConnectionParameter_vue_vue_type_style_index_0_lang_module_default = { container: container$4, "label-wrapper": "_label-wrapper_1p18l_129", "info-tip": "_info-tip_1p18l_134", label: label$1, "parameter-value": "_parameter-value_1p18l_147", "parameter-value--loading": "_parameter-value--loading_1p18l_158", "input-wrapper": "_input-wrapper_1p18l_161", "parameter-skeleton": "_parameter-skeleton_1p18l_179", "copy-button-wrapper": "_copy-button-wrapper_1p18l_201", "copy-button": "_copy-button_1p18l_201" }; var ConnectionParameter_default = /* @__PURE__ */ __plugin_vue_export_helper_default(ConnectionParameter_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": ConnectionParameter_vue_vue_type_style_index_0_lang_module_default }]]); var MCPOAuthPopoverTab_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "MCPOAuthPopoverTab", props: { serverUrl: {} }, emits: ["copy"], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; const i18n = useI18n(); const handleServerUrlCopy = (value) => { emit("copy", value); }; return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container), "data-test-id": "mcp-oauth-popover-tab" }, [createVNode(ConnectionParameter_default, { id: "oauth-server-url", label: unref(i18n).baseText("settings.mcp.connectPopover.serverUrl"), value: props.serverUrl, onCopy: handleServerUrlCopy }, null, 8, ["label", "value"])], 2); }; } }); var MCPOAuthPopoverTab_vue_vue_type_style_index_0_lang_module_default = { container: "_container_1y944_123" }; var MCPOAuthPopoverTab_default = /* @__PURE__ */ __plugin_vue_export_helper_default(MCPOAuthPopoverTab_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": MCPOAuthPopoverTab_vue_vue_type_style_index_0_lang_module_default }]]); var MCPAccessTokenPopoverTab_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "MCPAccessTokenPopoverTab", props: { serverUrl: {} }, emits: ["copy"], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; const i18n = useI18n(); const toast = useToast(); const mcpStore = useMCPStore(); const loadingApiKey = ref(true); const keyRotating = ref(false); const apiKey = computed(() => mcpStore.currentUserMCPKey); const { copy, copied, isSupported } = useClipboard(); const connectionString = computed(() => { return ` { "mcpServers": { "n8n-mcp": { "command": "npx", "args": [ "-y", "supergateway", "--streamableHttp", "${props.serverUrl}", "--header", "authorization:Bearer ${apiKeyText.value}" ] } } } `; }); const isKeyRedacted = computed(() => { return apiKey.value?.apiKey?.includes("******") ?? false; }); const connectionCode = computed(() => { return `\`\`\`json${connectionString.value}\`\`\``; }); const apiKeyText = computed(() => { if (keyRotating.value) return `<${i18n.baseText("generic.loading")}...>`; return isKeyRedacted.value ? "<YOUR_ACCESS_TOKEN_HERE>" : apiKey.value?.apiKey; }); const fetchApiKey = async () => { try { loadingApiKey.value = true; await mcpStore.getOrCreateApiKey(); } catch (error) { toast.showError(error, i18n.baseText("settings.mcp.error.fetching.apiKey")); } finally { setTimeout(() => { loadingApiKey.value = false; }, 200); } }; const rotateKey = async () => { try { keyRotating.value = true; await mcpStore.generateNewApiKey(); } catch (error) { toast.showError(error, i18n.baseText("settings.mcp.error.rotating.apiKey")); } finally { setTimeout(() => { keyRotating.value = false; }, 200); } }; const handleConnectionStringCopy = async () => { await copy(connectionString.value); emit("copy", "mcpJson", connectionString.value); }; const handleUrlCopy = (url) => { emit("copy", "serverUrl", url); }; const handleAccessTokenCopy = () => { if (apiKey.value?.apiKey) emit("copy", "accessToken", apiKey.value.apiKey); }; onMounted(async () => { if (!apiKey.value) await fetchApiKey(); else loadingApiKey.value = false; }); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container), "data-test-id": "mcp-access-token-popover-tab" }, [createVNode(ConnectionParameter_default, { id: "oauth-server-url", label: unref(i18n).baseText("settings.mcp.connectPopover.serverUrl"), value: props.serverUrl, onCopy: handleUrlCopy }, null, 8, ["label", "value"]), loadingApiKey.value ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass(_ctx.$style["loading-container"]) }, [createVNode(unref(N8nLoading_default), { loading: loadingApiKey.value, variant: "h1", class: normalizeClass(_ctx.$style["url-skeleton"]) }, null, 8, ["loading", "class"]), createVNode(unref(N8nLoading_default), { loading: loadingApiKey.value, variant: "button", class: normalizeClass(_ctx.$style["code-skeleton"]) }, null, 8, ["loading", "class"])], 2)) : apiKey.value?.apiKey ? (openBlock(), createElementBlock("div", { key: 1, class: normalizeClass(_ctx.$style["parameters-container"]) }, [ createVNode(ConnectionParameter_default, { id: "access-token", value: apiKey.value.apiKey, "value-loading": keyRotating.value, label: unref(i18n).baseText("settings.mcp.connectPopover.tab.accessToken"), "info-tip": unref(i18n).baseText("settings.mcp.instructions.apiKey.tip"), "allow-copy": !isKeyRedacted.value, onCopy: handleAccessTokenCopy }, { customActions: withCtx(() => [createVNode(unref(N8nTooltip_default), { content: unref(i18n).baseText("settings.mcp.instructions.rotateKey.tooltip"), "show-after": unref(100) }, { default: withCtx(() => [createVNode(unref(N8nButton_default), { type: "tertiary", icon: "refresh-cw", square: true, disabled: keyRotating.value, onClick: rotateKey }, null, 8, ["disabled"])]), _: 1 }, 8, ["content", "show-after"])]), _: 1 }, 8, [ "value", "value-loading", "label", "info-tip", "allow-copy" ]), !isKeyRedacted.value ? (openBlock(), createBlock(unref(N8nNotice_default), { key: 0 }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.access.token.notice")), 1)]), _: 1 })) : createCommentVNode("", true), createBaseVNode("div", { class: normalizeClass(_ctx.$style["json-container"]), "data-test-id": "mcp-access-token-json" }, [ createBaseVNode("label", { class: normalizeClass(_ctx.$style.label), for: "mcp-json" }, toDisplayString(unref(i18n).baseText("settings.mcp.connectPopover.jsonConfig")), 3), createVNode(unref(N8nMarkdown_default), { id: "mcp-json", content: connectionCode.value }, null, 8, ["content"]), createVNode(unref(N8nTooltip_default), { disabled: !unref(isSupported), content: unref(copied) ? unref(i18n).baseText("generic.copied") : unref(i18n).baseText("generic.copy"), "show-after": unref(100) }, { default: withCtx(() => [unref(isSupported) && !loadingApiKey.value && !keyRotating.value ? (openBlock(), createBlock(unref(N8nButton_default), { key: 0, type: "tertiary", icon: unref(copied) ? "check" : "copy", square: true, class: normalizeClass(_ctx.$style["copy-json-button"]), "data-test-id": "mcp-json-copy-button", onClick: handleConnectionStringCopy }, null, 8, ["icon", "class"])) : createCommentVNode("", true)]), _: 1 }, 8, [ "disabled", "content", "show-after" ]) ], 2) ], 2)) : createCommentVNode("", true)], 2); }; } }); const container$2 = "_container_1ymth_123"; const label = "_label_1ymth_155"; var MCPAccessTokenPopoverTab_vue_vue_type_style_index_0_lang_module_default = { container: container$2, "loading-container": "_loading-container_1ymth_128", "url-skeleton": "_url-skeleton_1ymth_134", "code-skeleton": "_code-skeleton_1ymth_139", "parameters-container": "_parameters-container_1ymth_144", "json-container": "_json-container_1ymth_150", label, "copy-json-button": "_copy-json-button_1ymth_173" }; var MCPAccessTokenPopoverTab_default = /* @__PURE__ */ __plugin_vue_export_helper_default(MCPAccessTokenPopoverTab_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": MCPAccessTokenPopoverTab_vue_vue_type_style_index_0_lang_module_default }]]); var McpConnectPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "McpConnectPopover", props: { disabled: { type: Boolean } }, setup(__props) { const i18n = useI18n(); const telemetry = useTelemetry(); const rootStore = useRootStore(); const mcpStore = useMCPStore(); const props = __props; const TABS = { ACCESS_TOKEN: "accessToken", OAUTH: "oauth" }; const tabItems = ref([{ value: TABS.OAUTH, label: i18n.baseText("settings.mcp.connectPopover.tab.oauth") }, { value: TABS.ACCESS_TOKEN, label: i18n.baseText("settings.mcp.connectPopover.tab.accessToken") }]); const serverUrl = ref(`${rootStore.urlBaseEditor}${MCP_ENDPOINT}`); const activeTab = ref(tabItems.value[0].value); const handlePopoverOpenChange = (isOpen) => { if (isOpen) mcpStore.openConnectPopover(); else { mcpStore.closeConnectPopover(); mcpStore.resetCurrentUserMCPKey(); } }; const handleTabChange = (newTab) => { activeTab.value = newTab; }; const handleAccessTokenTabCopy = (type) => { trackCopyEvent({ item: { serverUrl: "server-url", accessToken: "access-token", mcpJson: "mcp-json" }[type], source: "token-tab" }); }; const trackCopyEvent = (payload) => { telemetry.track("User copied MCP connection parameter", { parameter: payload.item, source: payload.source }); }; watch(() => props.disabled, (newValue) => { if (!newValue) mcpStore.openConnectPopover(); }); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", null, [createVNode(unref(N8nPopover_default), { id: "mcp-connect-popover", open: unref(mcpStore).connectPopoverOpen, "content-class": _ctx.$style.popper, "show-arrow": false, width: `${unref(460)}px`, "onUpdate:open": handlePopoverOpenChange }, { trigger: withCtx(() => [createVNode(unref(N8nButton_default), { "data-test-id": "mcp-connect-popover-trigger-button", type: "tertiary", disabled: _ctx.disabled }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.connectPopover.triggerLabel")), 1)]), _: 1 }, 8, ["disabled"])]), content: withCtx(() => [createBaseVNode("div", { class: normalizeClass(_ctx.$style["popper-content"]), "data-test-id": "mcp-connect-popover-content" }, [createBaseVNode("header", null, [createVNode(unref(N8nRadioButtons_default), { "data-test-id": "mcp-connect-popover-tabs", "model-value": activeTab.value, options: tabItems.value, "onUpdate:modelValue": handleTabChange }, null, 8, ["model-value", "options"])]), createBaseVNode("main", null, [activeTab.value === TABS.OAUTH ? (openBlock(), createBlock(MCPOAuthPopoverTab_default, { key: 0, "server-url": serverUrl.value, onCopy: _cache[0] || (_cache[0] = ($event) => trackCopyEvent({ item: "server-url", source: "oauth-tab" })) }, null, 8, ["server-url"])) : activeTab.value === TABS.ACCESS_TOKEN ? (openBlock(), createBlock(MCPAccessTokenPopoverTab_default, { key: 1, "server-url": serverUrl.value, onCopy: handleAccessTokenTabCopy }, null, 8, ["server-url"])) : createCommentVNode("", true)])], 2)]), _: 1 }, 8, [ "open", "content-class", "width" ])]); }; } }); var McpConnectPopover_vue_vue_type_style_index_0_lang_module_default = { popper: "_popper_1vjps_123", "popper-content": "_popper-content_1vjps_127" }; var McpConnectPopover_default = /* @__PURE__ */ __plugin_vue_export_helper_default(McpConnectPopover_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": McpConnectPopover_vue_vue_type_style_index_0_lang_module_default }]]); var MCPHeaderActions_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "MCPHeaderActions", props: { toggleDisabled: { type: Boolean, default: false }, loading: { type: Boolean, default: false }, accessEnabled: { type: Boolean } }, emits: ["disableMcpAccess"], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container), "data-test-id": "mcp-header-actions" }, [createVNode(McpAccessToggle_default, { "model-value": _ctx.accessEnabled, disabled: props.toggleDisabled, loading: props.loading, class: normalizeClass(_ctx.$style["mcp-access-toggle"]), onDisableMcpAccess: _cache[0] || (_cache[0] = ($event) => emit("disableMcpAccess")) }, null, 8, [ "model-value", "disabled", "loading", "class" ]), createVNode(McpConnectPopover_default, { disabled: !_ctx.accessEnabled }, null, 8, ["disabled"])], 2); }; } }); var MCPHeaderActions_vue_vue_type_style_index_0_lang_module_default = { container: "_container_1bb92_123" }; var MCPHeaderActions_default = /* @__PURE__ */ __plugin_vue_export_helper_default(MCPHeaderActions_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": MCPHeaderActions_vue_vue_type_style_index_0_lang_module_default }]]); var _hoisted_1$2 = { key: 0 }; var _hoisted_2$1 = { key: 1, class: "mt-s mb-xl" }; var _hoisted_3$1 = ["onClick"]; var _hoisted_4 = { key: 0 }; var _hoisted_5 = { key: 1 }; var WorkflowsTable_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "WorkflowsTable", props: { workflows: {}, loading: { type: Boolean } }, emits: [ "removeMcpAccess", "connectWorkflows", "updateDescription" ], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; const i18n = useI18n(); const tableHeaders = ref([ { title: i18n.baseText("settings.mcp.workflows.table.column.name"), key: "workflow", width: 150, disableSort: true, value() {} }, { title: i18n.baseText("settings.mcp.workflows.table.column.location"), key: "location", width: 200, disableSort: true, value() {} }, { title: i18n.baseText("generic.description"), key: "description", width: 350, disableSort: true, value() {} }, { title: "", key: "actions", align: "end", width: 50, disableSort: true, value() {} } ]); const getAvailableActions = (workflow) => { const permissions = getResourcePermissions(workflow.scopes); return [{ label: i18n.baseText("settings.mcp.workflows.table.action.removeMCPAccess"), value: "removeFromMCP", disabled: !permissions.workflow.update }, { label: i18n.baseText("settings.mcp.workflows.table.action.updateDescription"), value: "updateDescription", disabled: !permissions.workflow.update }]; }; const onWorkflowAction = (action, workflow) => { switch (action) { case "removeFromMCP": emit("removeMcpAccess", workflow); break; case "updateDescription": emit("updateDescription", workflow); break; default: break; } }; const onConnectClick = () => { emit("connectWorkflows"); }; return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style["workflow-table-container"]) }, [props.loading ? (openBlock(), createElementBlock("div", _hoisted_1$2, [createVNode(unref(N8nLoading_default), { loading: props.loading, variant: "h1", class: "mb-l" }, null, 8, ["loading"]), createVNode(unref(N8nLoading_default), { loading: props.loading, variant: "p", rows: 5, "shrink-last": false }, null, 8, ["loading"])])) : (openBlock(), createElementBlock("div", _hoisted_2$1, [createVNode(unref(N8nDataTableServer_default), { class: normalizeClass(_ctx.$style["workflow-table"]), "data-test-id": "mcp-workflow-table", headers: tableHeaders.value, items: props.workflows, "items-length": props.workflows.length }, createSlots({ [`item.workflow`]: withCtx(({ item }) => [createBaseVNode("div", { class: normalizeClass(_ctx.$style["workflow-cell"]), "data-test-id": "mcp-workflow-cell" }, [createVNode(unref(N8nLink_default), { "data-test-id": "mcp-workflow-name-link", "new-window": true, to: unref(router_default).resolve({ name: unref(VIEWS).WORKFLOW, params: { name: item.id } }).fullPath, theme: "text", class: normalizeClass([_ctx.$style["table-link"], _ctx.$style.truncate]) }, { default: withCtx(() => [createVNode(unref(N8nText_default), { class: normalizeClass(_ctx.$style.truncate), "data-test-id": "mcp-workflow-name" }, { default: withCtx(() => [createTextVNode(toDisplayString(item.name), 1)]), _: 2 }, 1032, ["class"])]), _: 2 }, 1032, ["to", "class"])], 2)]), [`item.location`]: withCtx(({ item }) => [createBaseVNode("div", { class: normalizeClass(_ctx.$style["location-cell"]), "data-test-id": "mcp-workflow-location-cell" }, [createVNode(WorkflowLocation_default, { "workflow-id": item.id, "home-project": item.homeProject, "parent-folder": item.parentFolder, "as-links": true }, null, 8, [ "workflow-id", "home-project", "parent-folder" ])], 2)]), [`item.description`]: withCtx(({ item }) => [createVNode(unref(N8nTooltip_default), { content: item.description || unref(i18n).baseText("settings.mcp.workflows.table.column.description.emptyTooltip"), "show-after": unref(100), "popper-class": _ctx.$style["description-popper"] }, { default: withCtx(() => [createBaseVNode("div", { "data-test-id": "mcp-workflow-description-cell", class: normalizeClass(_ctx.$style["description-cell"]), onClick: ($event) => emit("updateDescription", item) }, [item.description ? (openBlock(), createElementBlock("span", _hoisted_4, [createVNode(unref(N8nText_default), { "data-test-id": "mcp-workflow-description" }, { default: withCtx(() => [createTextVNode(toDisplayString(item.description), 1)]), _: 2 }, 1024)])) : (openBlock(), createElementBlock("span", _hoisted_5, [createVNode(unref(N8nIcon_default), { icon: "triangle-alert", size: 14, color: "warning", class: "mr-2xs" }), createVNode(unref(N8nText_default), { "data-test-id": "mcp-workflow-description-empty" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.workflows.table.column.description.emptyContent")), 1)]), _: 1 })]))], 10, _hoisted_3$1)]), _: 2 }, 1032, [ "content", "show-after", "popper-class" ])]), [`item.actions`]: withCtx(({ item }) => [createVNode(unref(N8nActionToggle_default), { class: normalizeClass(_ctx.$style["action-toggle"]), "data-test-id": "mcp-workflow-action-toggle", placement: "bottom", actions: getAvailableActions(item), theme: "dark", onAction: ($event) => onWorkflowAction($event, item) }, null, 8, [ "class", "actions", "onAction" ])]), _: 2 }, [props.workflows.length === 0 ? { name: "cover", fn: withCtx(() => [createBaseVNode("div", { class: normalizeClass(_ctx.$style["empty-state"]) }, [ createVNode(unref(N8nText_default), { "data-test-id": "mcp-workflow-table-empty-state", size: "large", color: "text-base" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.workflows.table.empty.title")), 1)]), _: 1 }), createVNode(unref(N8nText_default), { "data-test-id": "mcp-workflow-table-empty-state-description", size: "small", color: "text-base" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.workflows.table.empty.description")), 1)]), _: 1 }), createVNode(unref(N8nButton_default), { "data-test-id": "mcp-workflow-table-empty-state-button", type: "primary", label: unref(i18n).baseText("settings.mcp.connectWorkflows"), onClick: onConnectClick }, null, 8, ["label"]) ], 2)]), key: "0" } : void 0]), 1032, [ "class", "headers", "items", "items-length" ])]))], 2); }; } }); const header$1 = "_header_1rtae_127"; const truncate = "_truncate_1rtae_184"; var WorkflowsTable_vue_vue_type_style_index_0_lang_module_default = { "workflow-table-container": "_workflow-table-container_1rtae_123", header: header$1, "workflow-table": "_workflow-table_1rtae_123", "empty-state": "_empty-state_1rtae_137", "workflow-cell": "_workflow-cell_1rtae_147", "location-cell": "_location-cell_1rtae_154", "description-cell": "_description-cell_1rtae_158", "description-popper": "_description-popper_1rtae_176", "table-link": "_table-link_1rtae_180", truncate }; var WorkflowsTable_default = /* @__PURE__ */ __plugin_vue_export_helper_default(WorkflowsTable_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": WorkflowsTable_vue_vue_type_style_index_0_lang_module_default }]]); var _hoisted_1$1 = { "data-test-id": "oauth-clients-table" }; var _hoisted_2 = { key: 0 }; var _hoisted_3 = { key: 1, class: "mt-s mb-xl" }; var OAuthClientsTable_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "OAuthClientsTable", props: { clients: {}, loading: { type: Boolean } }, emits: ["revokeClient"], setup(__props, { emit: __emit }) { const i18n = useI18n(); const mcpStore = useMCPStore(); const props = __props; const emit = __emit; const tableHeaders = ref([ { title: i18n.baseText("settings.mcp.oAuthClients.table.clientName"), key: "name", width: 250, disableSort: true, value() {} }, { title: i18n.baseText("settings.mcp.oAuthClients.table.connectedAt"), key: "createdAt", width: 250, disableSort: true, value() {} }, { title: "", key: "actions", align: "end", width: 50, disableSort: true, value() {} } ]); const tableActions = ref([{ label: i18n.baseText("settings.mcp.oAuthClients.table.action.revokeAccess"), value: "revokeClient" }]); const onTableAction = (action, item) => { if (action === "revokeClient") emit("revokeClient", item); }; return (_ctx, _cache) => { return openBlock(), createElementBlock("div", _hoisted_1$1, [props.loading ? (openBlock(), createElementBlock("div", _hoisted_2, [createVNode(unref(N8nLoading_default), { loading: props.loading, variant: "h1", class: "mb-l" }, null, 8, ["loading"]), createVNode(unref(N8nLoading_default), { loading: props.loading, variant: "p", rows: 5, "shrink-last": false }, null, 8, ["loading"])])) : (openBlock(), createElementBlock("div", _hoisted_3, [createVNode(unref(N8nDataTableServer_default), { "data-test-id": "oauth-clients-data-table", headers: tableHeaders.value, items: props.clients, "items-length": props.clients.length }, createSlots({ [`item.name`]: withCtx(({ item }) => [createVNode(unref(N8nText_default), { "data-test-id": "mcp-client-name", color: "text-base" }, { default: withCtx(() => [createTextVNode(toDisplayString(item.name), 1)]), _: 2 }, 1024)]), [`item.createdAt`]: withCtx(({ item }) => [createVNode(unref(N8nText_default), { "data-test-id": "mcp-client-created-at", color: "text-base" }, { default: withCtx(() => [createVNode(TimeAgo_default, { date: String(item.createdAt) }, null, 8, ["date"])]), _: 2 }, 1024)]), [`item.actions`]: withCtx(({ item }) => [createVNode(unref(N8nActionToggle_default), { "data-test-id": "mcp-oauth-client-action-toggle", placement: "bottom", actions: tableActions.value, theme: "dark", onAction: ($event) => onTableAction($event, item) }, null, 8, ["actions", "onAction"])]), _: 2 }, [props.clients.length === 0 ? { name: "cover", fn: withCtx(() => [createBaseVNode("div", { class: normalizeClass(_ctx.$style["empty-state"]) }, [ createVNode(unref(N8nText_default), { "data-test-id": "mcp-workflow-table-empty-state", size: "large", color: "text-base" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.oauth.table.empty.title")), 1)]), _: 1 }), createVNode(unref(N8nText_default), { "data-test-id": "mcp-workflow-table-empty-state-description", size: "small", color: "text-base" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.oauth.table.empty.description")), 1)]), _: 1 }), createVNode(unref(N8nButton_default), { "data-test-id": "mcp-oauth-create-client-button", variant: "primary", onClick: _cache[0] || (_cache[0] = ($event) => unref(mcpStore).openConnectPopover()) }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.oauth.table.empty.button")), 1)]), _: 1 }) ], 2)]), key: "0" } : void 0]), 1032, [ "headers", "items", "items-length" ])]))]); }; } }); var OAuthClientsTable_vue_vue_type_style_index_0_lang_module_default = { header: "_header_1awbw_123", "empty-state": "_empty-state_1awbw_129" }; var OAuthClientsTable_default = /* @__PURE__ */ __plugin_vue_export_helper_default(OAuthClientsTable_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": OAuthClientsTable_vue_vue_type_style_index_0_lang_module_default }]]); var _hoisted_1 = { "data-test-id": "mcp-settings-description" }; var SettingsMCPView_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({ __name: "SettingsMCPView", setup(__props) { const i18n = useI18n(); const toast = useToast(); const documentTitle = useDocumentTitle(); const mcp = useMcp(); const telemetry = useTelemetry(); const mcpStore = useMCPStore(); const usersStore = useUsersStore(); const uiStore = useUIStore(); const mcpStatusLoading = ref(false); const selectedTab = ref("workflows"); const tabs = ref([{ label: i18n.baseText("settings.mcp.tabs.workflows"), value: "workflows" }, { label: i18n.baseText("settings.mcp.tabs.oauth"), value: "oauth" }]); const workflowsLoading = ref(false); const availableWorkflows = ref([]); const oAuthClientsLoading = ref(false); const connectedOAuthClients = ref([]); const isOwner = computed(() => usersStore.isInstanceOwner); const isAdmin = computed(() => usersStore.isAdmin); const canToggleMCP = computed(() => isOwner.value || isAdmin.value); const showConnectWorkflowsButton = computed(() => { return selectedTab.value === "workflows" && availableWorkflows.value.length > 0; }); const onTabSelected = async (tab) => { selectedTab.value = tab; if (tab === "workflows" && availableWorkflows.value.length === 0) await fetchAvailableWorkflows(); else if (tab === "oauth" && connectedOAuthClients.value.length === 0) { await fetchoAuthCLients(); telemetry.track("User clicked connected clients tab"); } }; const onToggleMCPAccess = async (enabled) => { try { mcpStatusLoading.value = true; if (await mcpStore.setMcpAccessEnabled(enabled)) { await fetchAvailableWorkflows(); await fetchoAuthCLients(); } else workflowsLoading.value = false; mcp.trackUserToggledMcpAccess(enabled); } catch (error) { toast.showError(error, i18n.baseText("settings.mcp.toggle.error")); } finally { mcpStatusLoading.value = false; workflowsLoading.value = false; } }; const onToggleWorkflowMCPAccess = async (workflowId, isEnabled) => { try { await mcpStore.toggleWorkflowMcpAccess(workflowId, isEnabled); if (isEnabled) await fetchAvailableWorkflows(); else availableWorkflows.value = availableWorkflows.value.filter((w) => w.id !== workflowId); } catch (error) { toast.showError(error, i18n.baseText("workflowSettings.toggleMCP.error.title")); throw error; } }; const onUpdateDescription = (workflow) => { uiStore.openModalWithData({ name: WORKFLOW_DESCRIPTION_MODAL_KEY, data: { workflowId: workflow.id, workflowDescription: workflow.description ?? "", onSave: (updatedDescription) => { const index = availableWorkflows.value.findIndex((w) => w.id === workflow.id); if (index !== -1) availableWorkflows.value[index] = { ...availableWorkflows.value[index], description: updatedDescription ?? void 0 }; } } }); }; const onTableRefresh = async () => { if (selectedTab.value === "workflows") await fetchAvailableWorkflows(); else if (selectedTab.value === "oauth") await fetchoAuthCLients(); }; const fetchAvailableWorkflows = async () => { workflowsLoading.value = true; try { availableWorkflows.value = await mcpStore.fetchWorkflowsAvailableForMCP(1, 200); } catch (error) { toast.showError(error, i18n.baseText("workflows.list.error.fetching")); } finally { setTimeout(() => { workflowsLoading.value = false; }, 200); } }; const onRefreshWorkflows = async () => { await fetchAvailableWorkflows(); }; const fetchoAuthCLients = async () => { try { oAuthClientsLoading.value = true; connectedOAuthClients.value = await mcpStore.getAllOAuthClients(); } catch (error) { toast.showError(error, i18n.baseText("settings.mcp.error.fetching.oAuthClients")); } finally { setTimeout(() => { oAuthClientsLoading.value = false; }, 200); } }; const revokeClientAccess = async (client) => { try { await mcpStore.removeOAuthClient(client.id); connectedOAuthClients.value = connectedOAuthClients.value.filter((c) => c.id !== client.id); toast.showMessage({ type: "success", title: i18n.baseText("settings.mcp.oAuthClients.revoke.success.title"), message: i18n.baseText("settings.mcp.oAuthClients.revoke.success.message", { interpolate: { name: client.name } }) }); } catch (error) { toast.showError(error, i18n.baseText("settings.mcp.oAuthClients.revoke.error")); } }; const openConnectWorkflowsModal = () => { uiStore.openModalWithData({ name: MCP_CONNECT_WORKFLOWS_MODAL_KEY, data: { onEnableMcpAccess: async (workflowId) => { await onToggleWorkflowMCPAccess(workflowId, true); } } }); telemetry.track("User clicked connect workflows from mcp settings"); }; onMounted(async () => { documentTitle.set(i18n.baseText("settings.mcp")); if (!mcpStore.mcpAccessEnabled) return; await fetchAvailableWorkflows(); }); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.container) }, [ createBaseVNode("header", { class: normalizeClass(_ctx.$style["main-header"]), "data-test-id": "mcp-settings-header" }, [createBaseVNode("div", { class: normalizeClass(_ctx.$style.headings) }, [createVNode(unref(N8nHeading_default), { size: "2xlarge", class: "mb-2xs" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp")), 1)]), _: 1 }), withDirectives(createBaseVNode("div", _hoisted_1, [createVNode(unref(N8nText_default), { size: "small", color: "text-light" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("settings.mcp.description")) + ". ", 1)]), _: 1 }), createVNode(unref(N8nLink_default), { href: unref(MCP_DOCS_PAGE_URL), target: "_blank", rel: "noopener noreferrer", size: "small", "data-test-id": "mcp-docs-link" }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("generic.learnMore")), 1)]), _: 1 }, 8, ["href"])], 512), [[vShow, unref(mcpStore).mcpAccessEnabled]])], 2), createVNode(MCPHeaderActions_default, { "access-enabled": unref(mcpStore).mcpAccessEnabled, "toggle-disabled": !canToggleMCP.value, loading: mcpStatusLoading.value, onDisableMcpAccess: _cache[0] || (_cache[0] = ($event) => onToggleMCPAccess(!unref(mcpStore).mcpAccessEnabled)) }, null, 8, [ "access-enabled", "toggle-disabled", "loading" ])], 2), !unref(mcpStore).mcpAccessEnabled ? (openBlock(), createBlock(MCPEmptyState_default, { key: 0, disabled: !canToggleMCP.value, loading: mcpStatusLoading.value, onTurnOnMcp: _cache[1] || (_cache[1] = ($event) => onToggleMCPAccess(true)) }, null, 8, ["disabled", "loading"])) : createCommentVNode("", true), unref(mcpStore).mcpAccessEnabled ? (openBlock(), createElementBlock("div", { key: 1, class: normalizeClass(_ctx.$style.container), "data-test-id": "mcp-enabled-section" }, [createBaseVNode("header", { class: normalizeClass(_ctx.$style["tabs-header"]) }, [createVNode(unref(N8nTabs_default), { "model-value": selectedTab.value, options: tabs.value, "onUpdate:modelValue": onTabSelected }, null, 8, ["model-value", "options"]), createBaseVNode("div", { class: normalizeClass(_ctx.$style.actions) }, [showConnectWorkflowsButton.value ? (openBlock(), createBlock(unref(N8nButton_default), { key: 0, label: unref(i18n).baseText("settings.mcp.connectWorkflows"), "data-test-id": "mcp-connect-workflows-header-button", size: "small", type: "primary", onClick: openConnectWorkflowsModal }, null, 8, ["label"])) : createCommentVNode("", true), createVNode(unref(N8nTooltip_default), { content: unref(i18n).baseText("settings.mcp.refresh.tooltip") }, { default: withCtx(() => [createVNode(unref(N8nButton_default), { "data-test-id": "mcp-workflows-refresh-button", size: "small", type: "tertiary", icon: "refresh-cw", square: true, onClick: onTableRefresh })]), _: 1 }, 8, ["content"])], 2)], 2), createBaseVNode("main", null, [selectedTab.value === "workflows" ? (openBlock(), createBlock(WorkflowsTable_default, { key: 0, "data-test-id": "mcp-workflow-table", workflows: availableWorkflows.value, loading: workflowsLoading.value, onRemoveMcpAccess: _cache[2] || (_cache[2] = (workflow) => onToggleWorkflowMCPAccess(workflow.id, false)), onConnectWorkflows: openConnectWorkflowsModal, onUpdateDescription, onRefresh: onRefreshWorkflows }, null, 8, ["workflows", "loading"])) : selectedTab.value === "oauth" ? (openBlock(), createBlock(OAuthClientsTable_default, { key: 1, "data-test-id": "mcp-oauth-clients-table", clients: connectedOAuthClients.value, loading: oAuthClientsLoading.value, onRevokeClient: revokeClientAccess, onRefresh: onTableRefresh }, null, 8, ["clients", "loading"])) : createCommentVNode("", true)])], 2)) : createCommentVNode("", true) ], 2); }; } }); const container = "_container_84c03_123"; const headings = "_headings_84c03_141"; const actions = "_actions_84c03_153"; var SettingsMCPView_vue_vue_type_style_index_0_lang_module_default = { container, "main-header": "_main-header_84c03_128", headings, "tabs-header": "_tabs-header_84c03_147", actions }; var SettingsMCPView_default = /* @__PURE__ */ __plugin_vue_export_helper_default(SettingsMCPView_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": SettingsMCPView_vue_vue_type_style_index_0_lang_module_default }]]); export { SettingsMCPView_default as default };