UNPKG

@n8n-plus/n8n-plus

Version:

n8n Workflow Automation Tool (plus edition)

2,244 lines 108 kB
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InstanceAiAdapterService = void 0;
exports.resolveDataTableByIdOrName = resolveDataTableByIdOrName;
exports.truncateResultData = truncateResultData;
exports.extractExecutionResult = extractExecutionResult;
exports.formatExecutionError = formatExecutionError;
exports.truncateNodeOutput = truncateNodeOutput;
exports.extractNodeOutput = extractNodeOutput;
exports.extractExecutionDebugInfo = extractExecutionDebugInfo;
const node_crypto_1 = require("node:crypto");
const promises_1 = require("node:fs/promises");
const node_path_1 = __importDefault(require("node:path"));
const instance_ai_1 = require("@n8n/instance-ai");
const config_1 = require("@n8n/config");
const constants_1 = require("@n8n/constants");
const instance_ai_settings_service_1 = require("./instance-ai-settings.service");
const node_definition_resolver_1 = require("./node-definition-resolver");
const web_research_1 = require("./web-research");
const db_1 = require("@n8n/db");
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const permissions_1 = require("@n8n/permissions");
const typeorm_1 = require("@n8n/typeorm");
const n8n_workflow_1 = require("n8n-workflow");
const n8n_core_1 = require("n8n-core");
const active_executions_1 = require("../../active-executions");
const credentials_finder_service_1 = require("../../credentials/credentials-finder.service");
const credentials_service_1 = require("../../credentials/credentials.service");
const event_service_1 = require("../../events/event.service");
const execution_persistence_1 = require("../../executions/execution-persistence");
const license_1 = require("../../license");
const load_nodes_and_credentials_1 = require("../../load-nodes-and-credentials");
const node_types_1 = require("../../node-types");
const data_table_repository_1 = require("../../modules/data-table/data-table.repository");
const data_table_service_1 = require("../../modules/data-table/data-table.service");
const source_control_preferences_service_ee_1 = require("../../modules/source-control.ee/source-control-preferences.service.ee");
const check_access_1 = require("../../permissions.ee/check-access");
const dynamic_node_parameters_service_1 = require("../../services/dynamic-node-parameters.service");
const folder_service_1 = require("../../services/folder.service");
const project_service_ee_1 = require("../../services/project.service.ee");
const role_service_1 = require("../../services/role.service");
const ssrf_protection_service_1 = require("../../services/ssrf/ssrf-protection.service");
const tag_service_1 = require("../../services/tag.service");
const workflow_finder_service_1 = require("../../workflows/workflow-finder.service");
const workflow_history_service_1 = require("../../workflows/workflow-history/workflow-history.service");
const workflow_service_1 = require("../../workflows/workflow.service");
const utils_1 = require("../../workflows/utils");
const workflow_service_ee_1 = require("../../workflows/workflow.service.ee");
const telemetry_1 = require("../../telemetry");
const workflow_runner_1 = require("../../workflow-runner");
const workflow_execute_additional_data_1 = require("../../workflow-execute-additional-data");
function resolveDisplayedDefaults(nodeProperties, parameters, nodeType, typeVersion, desc) {
    const stubNode = {
        id: '',
        name: '',
        type: nodeType,
        typeVersion,
        parameters: parameters,
        position: [0, 0],
    };
    const resolved = n8n_workflow_1.NodeHelpers.getNodeParameters(nodeProperties, parameters, true, false, stubNode, desc);
    return resolved ?? parameters;
}
let InstanceAiAdapterService = class InstanceAiAdapterService {
    async getNodesFromCache() {
        if (this.nodesCache && Date.now() < this.nodesCache.expiresAt) {
            return await this.nodesCache.promise;
        }
        const filePath = node_path_1.default.join(this.instanceSettings.staticCacheDir, 'types/nodes.json');
        const promise = (0, promises_1.readFile)(filePath, 'utf-8').then((json) => (0, n8n_workflow_1.jsonParse)(json));
        this.nodesCache = { promise, expiresAt: Date.now() + this.NODES_CACHE_TTL_MS };
        promise.catch(() => {
            this.nodesCache = null;
        });
        return await promise;
    }
    constructor(logger, globalConfig, workflowService, workflowFinderService, workflowRepository, sharedWorkflowRepository, projectRepository, executionRepository, credentialsService, credentialsFinderService, activeExecutions, workflowRunner, loadNodesAndCredentials, nodeTypes, instanceSettings, dataTableService, dataTableRepository, dynamicNodeParametersService, folderService, projectService, tagService, sourceControlPreferencesService, settingsService, workflowHistoryService, enterpriseWorkflowService, license, executionPersistence, eventService, roleService, telemetry, aiBuilderTemporaryWorkflowRepository, ssrfProtectionService) {
        this.workflowService = workflowService;
        this.workflowFinderService = workflowFinderService;
        this.workflowRepository = workflowRepository;
        this.sharedWorkflowRepository = sharedWorkflowRepository;
        this.projectRepository = projectRepository;
        this.executionRepository = executionRepository;
        this.credentialsService = credentialsService;
        this.credentialsFinderService = credentialsFinderService;
        this.activeExecutions = activeExecutions;
        this.workflowRunner = workflowRunner;
        this.loadNodesAndCredentials = loadNodesAndCredentials;
        this.nodeTypes = nodeTypes;
        this.instanceSettings = instanceSettings;
        this.dataTableService = dataTableService;
        this.dataTableRepository = dataTableRepository;
        this.dynamicNodeParametersService = dynamicNodeParametersService;
        this.folderService = folderService;
        this.projectService = projectService;
        this.tagService = tagService;
        this.sourceControlPreferencesService = sourceControlPreferencesService;
        this.settingsService = settingsService;
        this.workflowHistoryService = workflowHistoryService;
        this.enterpriseWorkflowService = enterpriseWorkflowService;
        this.license = license;
        this.executionPersistence = executionPersistence;
        this.eventService = eventService;
        this.roleService = roleService;
        this.telemetry = telemetry;
        this.aiBuilderTemporaryWorkflowRepository = aiBuilderTemporaryWorkflowRepository;
        this.ssrfProtectionService = ssrfProtectionService;
        this.nodesCache = null;
        this.NODES_CACHE_TTL_MS = 5 * 60 * 1000;
        this.webResearchCache = new web_research_1.LRUCache({
            maxEntries: 100,
            ttlMs: 15 * 60 * 1000,
        });
        this.searchCache = new web_research_1.LRUCache({
            maxEntries: 100,
            ttlMs: 15 * 60 * 1000,
        });
        this.logger = logger.scoped('instance-ai');
        this.allowSendingParameterValues = globalConfig.ai.allowSendingParameterValues;
    }
    createContext(user, options) {
        const { searchProxyConfig, pushRef, threadId } = options ?? {};
        return {
            userId: user.id,
            workflowService: this.createWorkflowAdapter(user, threadId),
            executionService: this.createExecutionAdapter(user, pushRef, threadId),
            credentialService: this.createCredentialAdapter(user),
            nodeService: this.createNodeAdapter(user),
            dataTableService: this.createDataTableAdapter(user),
            webResearchService: this.createWebResearchAdapter(user, searchProxyConfig),
            workspaceService: this.createWorkspaceAdapter(user),
            templatesService: this.getTemplatesService(),
            licenseHints: this.buildLicenseHints(),
            logger: this.logger,
            nodeTypesProvider: this.nodeTypes,
            allowSendingParameterValues: this.allowSendingParameterValues,
        };
    }
    getTemplatesService() {
        if (!this.templatesService) {
            this.templatesService = new instance_ai_1.BuilderTemplatesService({
                ...(0, instance_ai_1.builderTemplatesOptionsFromEnv)({ logger: this.logger }),
                cacheDir: node_path_1.default.join(this.instanceSettings.n8nFolder, 'n8n-sdk-templates'),
                logger: this.logger,
            });
        }
        return this.templatesService;
    }
    buildLicenseHints() {
        const hints = [];
        if (!this.license.isLicensed('feat:namedVersions')) {
            hints.push('**Named workflow versions** — naming and describing workflow versions (update-workflow-version) is available on the Pro plan and above.');
        }
        if (!this.license.isLicensed('feat:folders')) {
            hints.push('**Folders** — organizing workflows into folders (list-folders, create-folder, delete-folder, move-workflow-to-folder) is available on registered Community Edition or paid plans.');
        }
        return hints;
    }
    assertInstanceNotReadOnly(resourceType) {
        if (this.sourceControlPreferencesService.getPreferences().branchReadOnly) {
            throw new Error(`Cannot modify ${resourceType} on a protected instance. This instance is in read-only mode.`);
        }
    }
    createProjectScopeHelpers(user) {
        const { projectRepository } = this;
        let personalProjectIdPromise = null;
        const getPersonalProjectId = async () => {
            personalProjectIdPromise ??= projectRepository
                .getPersonalProjectForUserOrFail(user.id)
                .then((p) => p.id);
            return await personalProjectIdPromise;
        };
        const assertProjectScope = async (scopes, projectId) => {
            const allowed = await (0, check_access_1.userHasScopes)(user, scopes, false, { projectId });
            if (!allowed) {
                throw new Error('User does not have the required permissions in this project');
            }
        };
        const resolveProjectId = async (scopes, providedProjectId) => {
            const projectId = providedProjectId ?? (await getPersonalProjectId());
            await assertProjectScope(scopes, projectId);
            return projectId;
        };
        return { getPersonalProjectId, assertProjectScope, resolveProjectId };
    }
    createWorkflowAdapter(user, threadId) {
        const { workflowService, workflowFinderService, workflowRepository, sharedWorkflowRepository, aiBuilderTemporaryWorkflowRepository, workflowHistoryService, enterpriseWorkflowService, executionRepository, license, allowSendingParameterValues, telemetry, } = this;
        const logger = this.logger;
        const assertNotReadOnly = () => this.assertInstanceNotReadOnly('workflows');
        const { resolveProjectId } = this.createProjectScopeHelpers(user);
        const redactParameters = !allowSendingParameterValues;
        return {
            async list(options) {
                const filter = {
                    ...(options?.status === 'all' ? {} : { isArchived: options?.status === 'archived' }),
                    ...(options?.query ? { query: options.query } : {}),
                };
                const { workflows } = await workflowService.getMany(user, {
                    take: options?.limit ?? 50,
                    filter,
                });
                return workflows
                    .filter((wf) => 'versionId' in wf)
                    .map((wf) => ({
                    id: wf.id,
                    name: wf.name,
                    versionId: wf.versionId,
                    activeVersionId: wf.activeVersionId ?? null,
                    isArchived: wf.isArchived,
                    createdAt: wf.createdAt.toISOString(),
                    updatedAt: wf.updatedAt.toISOString(),
                }));
            },
            async get(workflowId) {
                const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:read',
                ]);
                if (!workflow) {
                    throw new Error(`Workflow ${workflowId} not found or not accessible`);
                }
                return toWorkflowDetail(workflow, { redactParameters });
            },
            async archive(workflowId) {
                assertNotReadOnly();
                const result = await workflowService.archive(user, workflowId, { skipArchived: true });
                if (!result) {
                    throw new Error(`Workflow ${workflowId} not found or not accessible`);
                }
            },
            async unarchive(workflowId) {
                assertNotReadOnly();
                const result = await workflowService.unarchive(user, workflowId);
                if (!result) {
                    throw new Error(`Workflow ${workflowId} not found or not accessible`);
                }
            },
            async clearAiTemporary(workflowId) {
                assertNotReadOnly();
                const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:update',
                ]);
                if (!workflow)
                    return;
                if (!(await aiBuilderTemporaryWorkflowRepository.existsForWorkflow(workflowId)))
                    return;
                await aiBuilderTemporaryWorkflowRepository.unmark(workflowId);
            },
            async archiveIfAiTemporary(workflowId) {
                assertNotReadOnly();
                const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:update',
                ]);
                if (!workflow)
                    return false;
                if (!(await aiBuilderTemporaryWorkflowRepository.existsForWorkflow(workflowId))) {
                    return false;
                }
                if (workflow.isArchived) {
                    await aiBuilderTemporaryWorkflowRepository.unmark(workflowId);
                    return false;
                }
                await workflowService.archive(user, workflowId, { skipArchived: true });
                await aiBuilderTemporaryWorkflowRepository.unmark(workflowId);
                return true;
            },
            async publish(workflowId, options) {
                const wf = await workflowService.activateWorkflow(user, workflowId, {
                    versionId: options?.versionId,
                    name: options?.name,
                    description: options?.description,
                    source: 'n8n-ai',
                });
                if (!wf.activeVersionId) {
                    throw new Error(`Workflow ${workflowId} was not activated — no active version set`);
                }
                if (threadId) {
                    telemetry.track('Builder published workflow', {
                        thread_id: threadId,
                        workflow_id: workflowId,
                        executed_by: 'ai',
                    });
                }
                return { activeVersionId: wf.activeVersionId };
            },
            async unpublish(workflowId) {
                await workflowService.deactivateWorkflow(user, workflowId, {
                    source: 'n8n-ai',
                });
            },
            async getAsWorkflowJSON(workflowId) {
                const wf = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:read',
                ]);
                if (!wf)
                    throw new Error(`Workflow ${workflowId} not found or not accessible`);
                return toWorkflowJSON(wf, { redactParameters });
            },
            async getLatestRunData(workflowId) {
                const accessible = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:read',
                ]);
                if (!accessible)
                    return null;
                const [latest] = await executionRepository.find({
                    select: ['id'],
                    where: { workflowId },
                    order: { startedAt: 'DESC' },
                    take: 1,
                });
                if (!latest)
                    return null;
                const execution = await executionRepository.findSingleExecution(latest.id, {
                    includeData: true,
                    unflattenData: true,
                });
                return execution?.data?.resultData?.runData ?? null;
            },
            async createFromWorkflowJSON(json, options) {
                assertNotReadOnly();
                const projectId = await resolveProjectId(['workflow:create'], options?.projectId);
                const settings = (json.settings ?? {});
                if (settings.redactionPolicy !== undefined && settings.redactionPolicy !== 'none') {
                    const canUpdateRedaction = await (0, check_access_1.userHasScopes)(user, ['workflow:enableRedaction'], false, { projectId });
                    if (!canUpdateRedaction) {
                        delete settings.redactionPolicy;
                    }
                }
                const newWorkflow = workflowRepository.create({
                    name: json.name,
                    nodes: [],
                    connections: {},
                    settings,
                    active: false,
                    versionId: (0, node_crypto_1.randomUUID)(),
                });
                const saved = await workflowRepository.manager.transaction(async (transactionManager) => {
                    const workflow = await transactionManager.save(db_1.WorkflowEntity, newWorkflow);
                    await sharedWorkflowRepository.makeOwner([workflow.id], projectId, transactionManager);
                    if (options?.markAsAiTemporary) {
                        if (!threadId) {
                            throw new n8n_workflow_1.UnexpectedError('Cannot mark AI-builder temporary workflow without a thread ID');
                        }
                        await aiBuilderTemporaryWorkflowRepository.mark(workflow.id, threadId, transactionManager);
                    }
                    return workflow;
                });
                let updateData = workflowRepository.create({
                    name: json.name,
                    nodes: json.nodes,
                    connections: json.connections,
                    settings,
                    pinData: sdkPinDataToRuntime(json.pinData),
                });
                let updated;
                try {
                    if (license.isSharingEnabled()) {
                        updateData = await enterpriseWorkflowService.preventTampering(updateData, saved.id, user);
                    }
                    updated = await workflowService.update(user, updateData, saved.id, {
                        source: 'n8n-ai',
                    });
                }
                catch (error) {
                    logger.warn('AI-builder workflow save failed', {
                        threadId,
                        workflowId: saved.id,
                        error: error instanceof Error ? error.message : String(error),
                    });
                    try {
                        const archived = await workflowService.archive(user, saved.id, { skipArchived: true });
                        if (archived && options?.markAsAiTemporary) {
                            await aiBuilderTemporaryWorkflowRepository.unmark(saved.id);
                        }
                    }
                    catch (cleanupError) {
                        logger.warn('Failed to clean up AI-builder workflow shell after create failure', {
                            threadId,
                            workflowId: saved.id,
                            error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),
                        });
                    }
                    throw error;
                }
                if (threadId) {
                    telemetry.track('Builder created workflow', {
                        thread_id: threadId,
                        workflow_id: updated.id,
                    });
                }
                return toWorkflowDetail(updated, { redactParameters });
            },
            async updateFromWorkflowJSON(workflowId, json, _options) {
                assertNotReadOnly();
                const settings = (json.settings ?? {});
                if (settings.redactionPolicy !== undefined) {
                    const [existingWorkflow, ownerProject] = await Promise.all([
                        workflowRepository.findOne({ where: { id: workflowId } }),
                        sharedWorkflowRepository.getWorkflowOwningProject(workflowId),
                    ]);
                    const currentPolicy = existingWorkflow?.settings?.redactionPolicy;
                    if (settings.redactionPolicy !== currentPolicy) {
                        const requiredScopes = (0, utils_1.getRequiredRedactionScopes)(currentPolicy, settings.redactionPolicy);
                        const canUpdateRedaction = ownerProject &&
                            (await (0, check_access_1.userHasScopes)(user, requiredScopes, false, { projectId: ownerProject.id }));
                        if (!canUpdateRedaction) {
                            delete settings.redactionPolicy;
                        }
                    }
                }
                let updateData = workflowRepository.create({
                    name: json.name,
                    nodes: json.nodes,
                    connections: json.connections,
                    settings,
                    pinData: sdkPinDataToRuntime(json.pinData),
                });
                let updated;
                try {
                    if (license.isSharingEnabled()) {
                        updateData = await enterpriseWorkflowService.preventTampering(updateData, workflowId, user);
                    }
                    updated = await workflowService.update(user, updateData, workflowId, {
                        source: 'n8n-ai',
                    });
                }
                catch (error) {
                    logger.warn('AI-builder workflow save failed', {
                        threadId,
                        workflowId,
                        error: error instanceof Error ? error.message : String(error),
                    });
                    throw error;
                }
                if (threadId) {
                    telemetry.track('Builder modified workflow', {
                        thread_id: threadId,
                        workflow_id: workflowId,
                    });
                }
                return toWorkflowDetail(updated, { redactParameters });
            },
            async listVersions(workflowId, options) {
                const take = options?.limit ?? 20;
                const skip = options?.skip ?? 0;
                const versions = await workflowHistoryService.getList(user, workflowId, take, skip);
                const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:read',
                ]);
                const activeVersionId = workflow?.activeVersionId ?? null;
                const currentDraftVersionId = workflow?.versionId ?? null;
                return versions.map((v) => ({
                    versionId: v.versionId,
                    name: v.name ?? null,
                    description: v.description ?? null,
                    authors: v.authors,
                    createdAt: v.createdAt.toISOString(),
                    autosaved: v.autosaved ?? false,
                    isActive: v.versionId === activeVersionId,
                    isCurrentDraft: v.versionId === currentDraftVersionId,
                }));
            },
            async getVersion(workflowId, versionId) {
                const version = await workflowHistoryService.getVersion(user, workflowId, versionId);
                const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:read',
                ]);
                const activeVersionId = workflow?.activeVersionId ?? null;
                const currentDraftVersionId = workflow?.versionId ?? null;
                return {
                    versionId: version.versionId,
                    name: version.name ?? null,
                    description: version.description ?? null,
                    authors: version.authors,
                    createdAt: version.createdAt.toISOString(),
                    autosaved: version.autosaved ?? false,
                    isActive: version.versionId === activeVersionId,
                    isCurrentDraft: version.versionId === currentDraftVersionId,
                    nodes: (version.nodes ?? []).map((n) => ({
                        name: n.name,
                        type: n.type,
                        parameters: redactParameters ? undefined : n.parameters,
                        position: n.position,
                    })),
                    connections: version.connections,
                };
            },
            async restoreVersion(workflowId, versionId) {
                const version = await workflowHistoryService.getVersion(user, workflowId, versionId);
                const updateData = workflowRepository.create({
                    nodes: version.nodes,
                    connections: version.connections,
                });
                await workflowService.update(user, updateData, workflowId, {
                    source: 'n8n-ai',
                });
            },
            ...(this.license.isLicensed('feat:namedVersions')
                ? {
                    async updateVersion(workflowId, versionId, data) {
                        await workflowHistoryService.updateVersionForUser(user, workflowId, versionId, data);
                    },
                }
                : {}),
        };
    }
    createExecutionAdapter(user, pushRef, threadId) {
        const { workflowFinderService, workflowRunner, activeExecutions, executionRepository, allowSendingParameterValues, license, roleService, telemetry, } = this;
        const assertNotReadOnly = () => this.assertInstanceNotReadOnly('executions');
        const DEFAULT_TIMEOUT_MS = 5 * constants_1.Time.minutes.toMilliseconds;
        const MAX_TIMEOUT_MS = 10 * constants_1.Time.minutes.toMilliseconds;
        const assertExecutionAccess = async (executionId, scopes = ['workflow:read']) => {
            const execution = await executionRepository.findSingleExecution(executionId, {
                includeData: false,
            });
            if (!execution) {
                throw new Error(`Execution ${executionId} not found`);
            }
            const workflow = await workflowFinderService.findWorkflowForUser(execution.workflowId, user, scopes);
            if (!workflow) {
                throw new Error(`Execution ${executionId} not found`);
            }
            return execution;
        };
        return {
            async list(options) {
                const scope = 'workflow:read';
                let sharingOptions;
                if (license.isSharingEnabled()) {
                    const projectRoles = await roleService.rolesWithScope('project', [scope]);
                    const workflowRoles = await roleService.rolesWithScope('workflow', [scope]);
                    sharingOptions = { scopes: [scope], projectRoles, workflowRoles };
                }
                else {
                    sharingOptions = {
                        workflowRoles: ['workflow:owner'],
                        projectRoles: [permissions_1.PROJECT_OWNER_ROLE_SLUG],
                    };
                }
                const query = {
                    kind: 'range',
                    range: { limit: options?.limit ?? 20, lastId: undefined, firstId: undefined },
                    order: { startedAt: 'DESC' },
                    user,
                    sharingOptions,
                    ...(options?.workflowId ? { workflowId: options.workflowId } : {}),
                    ...(options?.status
                        ? {
                            status: [options.status],
                        }
                        : {}),
                };
                const executions = await executionRepository.findManyByRangeQuery(query);
                return executions.map((e) => ({
                    id: e.id,
                    workflowId: e.workflowId,
                    workflowName: e.workflowName ?? '',
                    status: e.status,
                    startedAt: String(e.startedAt ?? ''),
                    finishedAt: e.stoppedAt ? String(e.stoppedAt) : undefined,
                    mode: e.mode,
                }));
            },
            async run(workflowId, inputData, options) {
                assertNotReadOnly();
                const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:execute',
                ]);
                if (!workflow) {
                    throw new Error(`Workflow ${workflowId} not found or not accessible`);
                }
                const nodes = workflow.nodes ?? [];
                const triggerNode = options?.triggerNodeName
                    ? (nodes.find((n) => n.name === options.triggerNodeName) ?? findTriggerNode(nodes))
                    : findTriggerNode(nodes);
                const runData = {
                    executionMode: triggerNode
                        ? getExecutionModeForTrigger(triggerNode)
                        : 'manual',
                    workflowData: {
                        ...workflow,
                        settings: {
                            ...workflow.settings,
                            saveManualExecutions: true,
                            saveDataSuccessExecution: 'all',
                            saveDataErrorExecution: 'all',
                        },
                    },
                    userId: user.id,
                    pushRef,
                };
                const workflowPinData = workflow.pinData ?? {};
                const overridePinData = options?.pinData
                    ? (sdkPinDataToRuntime(options.pinData) ?? {})
                    : {};
                const basePinData = { ...workflowPinData, ...overridePinData };
                if (inputData && triggerNode) {
                    const triggerPinData = getPinDataForTrigger(triggerNode, inputData);
                    const mergedPinData = { ...basePinData, ...triggerPinData };
                    runData.startNodes = [{ name: triggerNode.name, sourceData: null }];
                    runData.pinData = mergedPinData;
                    runData.executionData = (0, n8n_workflow_1.createRunExecutionData)({
                        startData: {},
                        resultData: { pinData: mergedPinData, runData: {} },
                        executionData: {
                            contextData: {},
                            metadata: {},
                            nodeExecutionStack: [
                                {
                                    node: triggerNode,
                                    data: { main: [triggerPinData[triggerNode.name]] },
                                    source: null,
                                },
                            ],
                            waitingExecution: {},
                            waitingExecutionSource: {},
                        },
                    });
                }
                else if (triggerNode) {
                    runData.triggerToStartFrom = { name: triggerNode.name };
                    if (Object.keys(basePinData).length > 0) {
                        runData.pinData = basePinData;
                    }
                }
                else if (Object.keys(basePinData).length > 0) {
                    runData.pinData = basePinData;
                }
                const trackBuilderExecutedWorkflow = (status) => {
                    if (!threadId)
                        return;
                    telemetry.track('Builder executed workflow', {
                        thread_id: threadId,
                        workflow_id: workflowId,
                        executed_by: 'ai',
                        pinned_node_count: Object.keys(runData.pinData ?? {}).length,
                        exec_type: runData.executionMode,
                        status,
                    });
                };
                const executionId = await workflowRunner.run(runData);
                const timeoutMs = Math.min(options?.timeout ?? DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
                if (activeExecutions.has(executionId)) {
                    let timeoutId;
                    const timeoutPromise = new Promise((_, reject) => {
                        timeoutId = setTimeout(() => {
                            reject(new Error(`Execution timed out after ${timeoutMs}ms`));
                        }, timeoutMs);
                    });
                    try {
                        await Promise.race([
                            activeExecutions.getPostExecutePromise(executionId),
                            timeoutPromise,
                        ]);
                        clearTimeout(timeoutId);
                    }
                    catch (error) {
                        clearTimeout(timeoutId);
                        if (error instanceof Error && error.message.includes('timed out')) {
                            try {
                                activeExecutions.stopExecution(executionId, new n8n_workflow_1.TimeoutExecutionCancelledError(executionId));
                            }
                            catch {
                            }
                            const result = {
                                executionId,
                                status: 'error',
                                error: `Execution timed out after ${timeoutMs}ms and was cancelled`,
                            };
                            trackBuilderExecutedWorkflow(result.status);
                            return result;
                        }
                        throw error;
                    }
                }
                const result = await extractExecutionResult(executionRepository, executionId, allowSendingParameterValues);
                trackBuilderExecutedWorkflow(result.status);
                return result;
            },
            async getStatus(executionId) {
                await assertExecutionAccess(executionId);
                const isRunning = activeExecutions.has(executionId);
                if (isRunning) {
                    return { executionId, status: 'running' };
                }
                return await extractExecutionResult(executionRepository, executionId, allowSendingParameterValues);
            },
            async getResult(executionId) {
                await assertExecutionAccess(executionId);
                if (activeExecutions.has(executionId)) {
                    await activeExecutions.getPostExecutePromise(executionId);
                }
                return await extractExecutionResult(executionRepository, executionId, allowSendingParameterValues);
            },
            async stop(executionId) {
                assertNotReadOnly();
                await assertExecutionAccess(executionId, ['workflow:execute']);
                if (!activeExecutions.has(executionId)) {
                    return {
                        success: false,
                        message: `Execution ${executionId} is not currently running`,
                    };
                }
                try {
                    activeExecutions.stopExecution(executionId, new n8n_workflow_1.TimeoutExecutionCancelledError(executionId));
                    return { success: true, message: `Execution ${executionId} cancelled` };
                }
                catch {
                    return {
                        success: false,
                        message: `Failed to cancel execution ${executionId}`,
                    };
                }
            },
            async getDebugInfo(executionId) {
                await assertExecutionAccess(executionId);
                return await extractExecutionDebugInfo(executionRepository, executionId, allowSendingParameterValues);
            },
            async getNodeOutput(executionId, nodeName, options) {
                await assertExecutionAccess(executionId);
                if (!allowSendingParameterValues) {
                    return {
                        nodeName,
                        items: [],
                        totalItems: 0,
                        returned: { from: 0, to: 0 },
                    };
                }
                return await extractNodeOutput(executionRepository, executionId, nodeName, options);
            },
        };
    }
    createCredentialAdapter(user) {
        const { credentialsService, credentialsFinderService, loadNodesAndCredentials } = this;
        return {
            async list(options) {
                if (options?.workflowId || options?.projectId) {
                    const scoped = options.workflowId
                        ? await credentialsService.getCredentialsAUserCanUseInAWorkflow(user, {
                            workflowId: options.workflowId,
                        })
                        : await credentialsService.getCredentialsAUserCanUseInAWorkflow(user, {
                            projectId: options.projectId,
                        });
                    const filtered = options.type ? scoped.filter((c) => c.type === options.type) : scoped;
                    return filtered.map((c) => ({
                        id: c.id,
                        name: c.name,
                        type: c.type,
                    }));
                }
                const credentials = await credentialsService.getMany(user, {
                    listQueryOptions: {
                        filter: options?.type ? { type: options.type } : undefined,
                    },
                    includeGlobal: true,
                });
                return credentials.map((c) => ({
                    id: c.id,
                    name: c.name,
                    type: c.type,
                }));
            },
            async get(credentialId) {
                const credential = await credentialsService.getOne(user, credentialId, false);
                return {
                    id: credential.id,
                    name: credential.name,
                    type: credential.type,
                };
            },
            async delete(credentialId) {
                await credentialsService.delete(user, credentialId);
            },
            async test(credentialId) {
                const credential = await credentialsFinderService.findCredentialForUser(credentialId, user, ['credential:read']);
                if (!credential) {
                    throw new Error(`Credential ${credentialId} not found or not accessible`);
                }
                const credentialsToTest = {
                    id: credential.id,
                    name: credential.name,
                    type: credential.type,
                    data: await credentialsService.decrypt(credential, true),
                };
                const result = await credentialsService.test(user.id, credentialsToTest);
                return {
                    success: result.status === 'OK',
                    message: result.message,
                };
            },
            async isTestable(credentialType) {
                try {
                    const credClass = loadNodesAndCredentials.getCredential(credentialType);
                    if (credClass.type.test)
                        return true;
                    const known = loadNodesAndCredentials.knownCredentials;
                    const supportedNodes = known[credentialType]?.supportedNodes ?? [];
                    for (const nodeName of supportedNodes) {
                        try {
                            const loaded = loadNodesAndCredentials.getNode(nodeName);
                            const nodeInstance = loaded.type;
                            const nodeDesc = 'nodeVersions' in nodeInstance
                                ? Object.values(nodeInstance.nodeVersions).pop()?.description
                                : nodeInstance.description;
                            const hasTestedBy = nodeDesc?.credentials?.some((cred) => cred.name === credentialType && cred.testedBy);
                            if (hasTestedBy)
                                return true;
                        }
                        catch {
                            continue;
                        }
                    }
                    return false;
                }
                catch {
                    return false;
                }
            },
            async getDocumentationUrl(credentialType) {
                try {
                    const credClass = loadNodesAndCredentials.getCredential(credentialType);
                    const slug = credClass.type.documentationUrl;
                    if (!slug)
                        return null;
                    if (slug.startsWith('http'))
                        return slug;
                    return `https://docs.n8n.io/integrations/builtin/credentials/${slug}/`;
                }
                catch {
                    return null;
                }
            },
            getCredentialFields(credentialType) {
                try {
                    const allTypes = [credentialType];
                    const known = loadNodesAndCredentials.knownCredentials;
                    for (const typeName of allTypes) {
                        const extendsArr = known[typeName]?.extends ?? [];
                        allTypes.push(...extendsArr);
                    }
                    const fields = [];
                    const seen = new Set();
                    for (const typeName of allTypes) {
                        try {
                            const credClass = loadNodesAndCredentials.getCredential(typeName);
                            for (const prop of credClass.type.properties) {
                                if (prop.type === 'hidden' || seen.has(prop.name))
                                    continue;
                                seen.add(prop.name);
                                fields.push({
                                    name: prop.name,
                                    displayName: prop.displayName,
                                    type: prop.type,
                                    required: prop.required ?? false,
                                    description: prop.description,
                                });
                            }
                        }
                        catch {
                        }
                    }
                    return fields;
                }
                catch {
                    return [];
                }
            },
            async searchCredentialTypes(query) {
                const q = query.toLowerCase().trim();
                if (!q)
                    return [];
                const known = loadNodesAndCredentials.knownCredentials;
                const results = [];
                for (const typeName of Object.keys(known)) {
                    if (typeName.toLowerCase().includes(q)) {
                        try {
                            const credClass = loadNodesAndCredentials.getCredential(typeName);
                            results.push({
                                type: typeName,
                                displayName: credClass.type.displayName,
                            });
                        }
                        catch {
                            results.push({ type: typeName, displayName: typeName });
                        }
                        continue;
                    }
                    try {
                        const credClass = loadNodesAndCredentials.getCredential(typeName);
                        if (credClass.type.displayName.toLowerCase().includes(q)) {
                            results.push({
                                type: typeName,
                                displayName: credClass.type.displayName,
                            });
                        }
                    }
                    catch {
                    }
                }
                return results;
            },
            async getAccountContext(credentialId) {
                const credential = await credentialsFinderService.findCredentialForUser(credentialId, user, ['credential:read']);
                if (!credential) {
                    return { accountIdentifier: undefined };
                }
                const mask = (id) => {
                    const atIdx = id.indexOf('@');
                    if (atIdx > 0) {
                        const local = id.slice(0, atIdx);
                        const domain = id.slice(atIdx);
                        const keep = Math.min(2, local.length);
                        return local.slice(0, keep) + '***' + domain;
                    }
                    if (id.length <= 3)
                        return id;
                    return id.slice(0, 2) + '***' + id.slice(-1);
                };
                try {
                    const redacted = await credentialsService.decrypt(credential, false);
                    if (typeof redacted.accountIdentifier === 'string' && redacted.accountIdentifier) {
                        return { accountIdentifier: mask(redacted.accountIdentifier) };
                    }
                    for (const key of ['email', 'user', 'username', 'account', 'serviceAccountEmail']) {
                        const value = redacted[key];
                        if (typeof value === 'string' && value) {
                            return { accountIdentifier: mask(value) };
                        }
                    }
                    const raw = await credentialsService.decrypt(credential, true);
                    const tokenData = raw.oauthTokenData;
                    if (tokenData && typeof tokenData === 'object') {
                        const { OauthService } = await Promise.resolve().then(() => __importStar(require('../../oauth/oauth.service')));
                        const identifier = OauthService.extractAccountIdentifier(tokenData);
                        if (identifier) {
                            return { accountIdentifier: mask(identifier) };
                        }
                    }
                    return { accountIdentifier: undefined };
                }
                catch {
                    return { accountIdentifier: undefined };
                }
            },
        };
    }
    createDataTableAdapter(user) {
        const { dataTableService, dataTableRepository } = this;
        const assertNotReadOnly = () => this.assertInstanceNotReadOnly('data tables');
        const { resolveProjectId } = this.createProjectScopeHelpers(user);
        const logger = this.logger;
        const resolveAccessibleTable = async (scopes, dataTableId, disambiguator) => {
            const projectIdFilter = disambiguator?.projectId;
            const result = await resolveDataTableByIdOrName(dataTableRepository, logger, dataTableId, {
                projectIdFilter,
                accessFilter: async (id) => await (0, check_access_1.userHasScopes)(user, scopes, false, { dataTableId: id }),
            });
            if (result.kind === 'miss') {
                throw new Error(`Data table "${dataTableId}" not found`);
            }
            if (result.kind === 'ambiguous') {
                const projectIds = result.candidates.map((c) => c.projectId).join(', ');
                throw new Error(`Data table name "${dataTableId}" is ambiguous across accessible projects ` +
                    `(${projectIds}); pass the UUID or include a \`projectId\` to disambiguate.`);
            }
            if (projectIdFilter && result.table.projectId !== projectIdFilter) {
                throw new Error(`Data table "${dataTableId}" does not belong to project "${projectIdFilter}".`);
            }
            return result.table;
        };
        const resolveProjectIdForTable = async (scopes, dataTableId, disambiguator) => {
            const table = await resolveAccessibleTable(scopes, dataTableId, disambiguator);
            return { projectId: table.projectId, resolvedId: table.id };
        };
        const resolveTableMeta = async (scopes, dataTableId, disambiguator) => {
            const table = await resolveAccessibleTable(scopes, dataTableId, disambiguator);
            return { projectId: table.projectId, tableName: table.name, resolvedId: table.id };
        };
        const referenceScopes = {
            read: ['dataTable:read'],
            readRow: ['dataTable:readRow'],
            writeRow: ['dataTable:writeRow'],
            update: ['dataTable:update'],
            delete: ['dataTable:delete'],
        };
        return {
            async list(options) {
                const projectId = await resolveProjectId(['dataTable:listProject'], options?.projectId);
                const { data: tables } = await dataTableService.getManyAndCount({
                    filter: { projectId },
                });
                return tables.map((t) => ({
                    id: t.id,
                    name: t.name,
                    projectId,
                    columns: t.columns.map((c) => ({ id: c.id, name: c.name, type: c.type })),
                    createdAt: t.createdAt.toISOString(),
                    updatedAt: t.updatedAt.toISOString(),
                }));
            },
            async create(name, columns, options) {
                assertNotReadOnly();
                const projectId = await resolveProjectId(['dataTable:create'], options?.projectId);
                const result = await dataTableService.createDataTable(projectId, { name, columns });
                return {
                    id: result.id,
                    name: result.name,
                    projectId,
                    columns: result.columns.map((c) => ({ id: c.id, name: c.name, type: c.type })),
                    createdAt: result.createdAt.toISOString(),
                    updatedAt: result.updatedAt.toISOString(),
                };
            },
            async delete(dataTableId, options) {
                assertNotReadOnly();
                const { projectId, resolvedId } = await resolveProjectIdForTable(['dataTable:delete'], dataTableId, options);
                await dataTableService.deleteDataTable(resolvedId, projectId);
            },
            async resolveTableReference(dataTableId, options) {
                const { projectId, tableName, resolvedId } = await resolveTableMeta(referenceScopes[options?.permission ?? 'read'], dataTableId, options);
                return { id: resolvedId, name: tableName, projectId };
            },
            async getSchema(dataTableId, options) {
                const { projectId, resolvedId } = await resolveProjectIdForTable(['dataTable:read'], dataTableId, options);
                const columns = await dataTableService.getColumns(resolvedId, projectId);
                return columns.map((c, index) => ({
                    id: c.id,
                    name: c.name,
                    type: c.type,
                    index,
                }));
            },
            async addColumn(dataTableId, column, options) {
                assertNotReadOnly();
                const { projectId, resolvedId } = await resolveProjectIdForTable(['dataTable:update'], dataTableId, options);
                const result = await dataTableService.addColumn(resolvedId, projectId, column);
                return {
                    id: result.id,
                    name: result.name,
                    type: result.type,
                    index: result.index,
                };
            },
            async deleteColumn(dataTableId, columnId, options) {
                assertNotReadOnly();
                const { projectId, resolvedId } = await resolveProjectIdForTable(['dataTable:update'], dataTableId, options);
                await dataTableService.deleteColumn(resolvedId, projectId, columnId);
            },
            async renameColumn(dataTableId, columnId, newName, options) {
                assertNotReadOnly();
                const { projectId, resolvedId } = await resolveProjectIdForTable(['dataTable:update'], dataTableId, options);
                await dataTableService.renameColumn(resolvedId, projectId, columnId, {
                    name: newName,
                });
            },
            async queryRows(dataTableId, options) {
                const { projectId, resolvedId } = await resolveProjectIdForTable(['dataTable:readRow'], dataTableId, options);
                return await dataTableService.getManyRowsAndCount(resolvedId, projectId, {
                    take: options?.limit ?? 50,
                    skip: options?.offset ?? 0,
                    filter: options?.filter,
                });
            },
            async insertRows(dataTableId, rows, options) {
                assertNotReadOnly();
                const { projectId, tableName, resolvedId } = await resolveTableMeta(['dataTable:writeRow'], dataTableId, options);
                const result = await dataTableService.insertRows(resolvedId, projectId, rows, 'count');
                return {
                    insertedCount: typeof result === 'number' ? result : rows.length,
                    dataTableId: resolvedId,
                    tableName,
                    projectId,
                };
            },
            async updateRows(dataTableId, filter, data, options) {
                assertNotReadOnly();
                const { projectId, tableName, resolvedId } = await resolveTableMeta(['dataTable:writeRow'], dataTableId, options);
                const result = await dataTableService.updateRows(resolvedId, projectId, { filter: filter, data: data }, true);
                return {
                    updatedCount: Array.isArray(result) ? result.length : 0,
                    dataTableId: resolvedId,
                    tableName,
                    projectId,
                };
            },
            async deleteRows(dataTableId, filter, options) {
                assertNotReadOnly();
                const { projectId, tableName, resolvedId } = await resolveTableMeta(['dataTable:writeRow'], dataTableId, options);
                const result = await dataTableService.deleteRows(resolvedId, projectId, { filter: filter }, true);
                return {
                    deletedCount: Array.isArray(result) ? result.length : 0,
                    dataTableId: resolvedId,
                    tableName,
                    projectId,
                };
            },
        };
    }
    createWebResearchAdapter(user, searchProxyConfig) {
        const fetchCache = this.webResearchCache;
        const searchCacheRef = this.searchCache;
        const settingsService = this.settingsService;
        const ssrf = this.ssrfProtectionService;
        const userId = user.id;
        let resolvedSearchMethod;
        let searchResolved = false;
        const lazySearch = async (query, options) => {
            if (!searchResolved) {
                const config = await settingsService.resolveSearchConfig(user);
                resolvedSearchMethod = this.buildSearchMethod(config.braveApiKey ?? '', config.searxngUrl ?? '', searchCacheRef, searchProxyConfig, userId);
                searchResolved = true;
            }
            if (!resolvedSearchMethod)
                return { query, results: [] };
            return await resolvedSearchMethod(query, options);
        };
        return {
            search: lazySearch,
            async fetchUrl(url, options) {
                const cacheKey = `${userId}:${url}`;
                const cached = fetchCache.get(cacheKey);
                if (cached) {
                    if (options?.authorizeUrl && cached.finalUrl) {
                        const origHost = new URL(url).hostname;
                        const finalHost = new URL(cached.finalUrl).hostname;
                        if (origHost !== finalHost) {
                            await options.authorizeUrl(cached.finalUrl);
                        }
                    }
                    return cached;
                }
                const page = await (0, web_research_1.fetchAndExtract)(url, {
                    maxContentLength: options?.maxContentLength,
                    maxResponseBytes: options?.maxResponseBytes,
                    timeoutMs: options?.timeoutMs,
                    authorizeUrl: options?.authorizeUrl,
                    ssrf,
                });
                const result = await (0, web_research_1.maybeSummarize)(page);
                fetchCache.set(cacheKey, result);
                return result;
            },
        };
    }
    buildSearchMethod(apiKey, searxngUrl, cache, searchProxyConfig, userId) {
        const keyPrefix = userId ? `${userId}:` : '';
        if (searchProxyConfig) {
            return async (query, options) => {
                const cacheKey = `${keyPrefix}${JSON.stringify([query, options ?? {}])}`;
                const cached = cache.get(cacheKey);
                if (cached)
                    return cached;
                const result = await (0, web_research_1.braveSearch)('', query, {
                    ...options,
                    proxyConfig: searchProxyConfig,
                });
                cache.set(cacheKey, result);
                return result;
            };
        }
        if (apiKey) {
            return async (query, options) => {
                const cacheKey = `${keyPrefix}${JSON.stringify([query, options ?? {}])}`;
                const cached = cache.get(cacheKey);
                if (cached)
                    return cached;
                const result = await (0, web_research_1.braveSearch)(apiKey, query, options ?? {});
                cache.set(cacheKey, result);
                return result;
            };
        }
        if (searxngUrl) {
            return async (query, options) => {
                const cacheKey = `${keyPrefix}${JSON.stringify([query, options ?? {}])}`;
                const cached = cache.get(cacheKey);
                if (cached)
                    return cached;
                const result = await (0, web_research_1.searxngSearch)(searxngUrl, query, options ?? {});
                cache.set(cacheKey, result);
                return result;
            };
        }
        return undefined;
    }
    getNodeDefinitionDirs() {
        if (!this._nodeDefinitionDirs) {
            this._nodeDefinitionDirs = (0, node_definition_resolver_1.resolveBuiltinNodeDefinitionDirs)();
        }
        return this._nodeDefinitionDirs;
    }
    createNodeAdapter(user) {
        const { dynamicNodeParametersService, projectRepository, credentialsFinderService } = this;
        const getNodes = async () => await this.getNodesFromCache();
        const findNodeByVersion = (nodes, nodeType, version) => {
            if (version !== undefined) {
                const exact = nodes.find((n) => {
                    if (n.name !== nodeType)
                        return false;
                    if (Array.isArray(n.version))
                        return n.version.includes(version);
                    return n.version === version;
                });
                if (exact)
                    return exact;
            }
            return nodes.find((n) => n.name === nodeType);
        };
        const normalizeNodeVersion = (version) => {
            if (!version)
                return undefined;
            const normalized = version.replace(/^v/i, '');
            if (!/^\d+$/.test(normalized))
                return Number(normalized);
            if (normalized.length === 2) {
                return Number(`${normalized[0]}.${normalized[1]}`);
            }
            return Number(normalized);
        };
        return {
            async listAvailable(options) {
                const nodes = await getNodes();
                let filtered = nodes;
                if (options?.query) {
                    const q = options.query.toLowerCase();
                    filtered = nodes.filter((n) => n.displayName.toLowerCase().includes(q) ||
                        n.name.toLowerCase().includes(q) ||
                        n.description?.toLowerCase().includes(q));
                }
                return filtered.map((n) => ({
                    name: n.name,
                    displayName: n.displayName,
                    description: n.description ?? '',
                    group: n.group ?? [],
                    version: Array.isArray(n.version) ? n.version[n.version.length - 1] : n.version,
                }));
            },
            async listSearchable() {
                const nodes = await getNodes();
                const toStringArray = (value) => {
                    if (typeof value === 'string')
                        return value;
                    return value.map((v) => (typeof v === 'string' ? v : v.type));
                };
                return nodes.map((n) => {
                    const result = {
                        name: n.name,
                        displayName: n.displayName,
                        description: n.description ?? '',
                        version: n.version,
                        inputs: toStringArray(n.inputs),
                        outputs: toStringArray(n.outputs),
                    };
                    if (n.codex?.alias) {
                        result.codex = { alias: n.codex.alias };
                    }
                    if (n.builderHint) {
                        result.builderHint = {};
                        if (n.builderHint.searchHint) {
                            result.builderHint.message = n.builderHint.searchHint;
                        }
                        if (n.builderHint.inputs) {
                            const inputs = {};
                            for (const [key, config] of Object.entries(n.builderHint.inputs)) {
                                inputs[key] = {
                                    required: config.required,
                                    ...(config.displayOptions
                                        ? { displayOptions: config.displayOptions }
                                        : {}),
                                };
                            }
                            result.builderHint.inputs = inputs;
                        }
                        if (n.builderHint.outputs) {
                            const outputs = {};
                            for (const [key, config] of Object.entries(n.builderHint.outputs)) {
                                outputs[key] = {
                                    ...(config.required !== undefined ? { required: config.required } : {}),
                                    ...(config.displayOptions
                                        ? { displayOptions: config.displayOptions }
                                        : {}),
                                };
                            }
                            result.builderHint.outputs = outputs;
                        }
                    }
                    return result;
                });
            },
            async getDescription(nodeType, version) {
                const nodes = await getNodes();
                let desc = version !== undefined
                    ? nodes.find((n) => {
                        if (n.name !== nodeType)
                            return false;
                        if (Array.isArray(n.version))
                            return n.version.includes(version);
                        return n.version === version;
                    })
                    : undefined;
                if (!desc) {
                    desc = nodes.find((n) => n.name === nodeType);
                }
                if (!desc) {
                    throw new Error(`Node type ${nodeType} not found`);
                }
                return {
                    name: desc.name,
                    displayName: desc.displayName,
                    description: desc.description ?? '',
                    group: desc.group ?? [],
                    version: Array.isArray(desc.version)
                        ? desc.version[desc.version.length - 1]
                        : desc.version,
                    properties: desc.properties.map((p) => ({
                        displayName: p.displayName,
                        name: p.name,
                        type: p.type,
                        required: p.required,
                        description: p.description,
                        default: p.default,
                        options: p.options
                            ?.filter((o) => typeof o === 'object' && o !== null && 'name' in o && 'value' in o)
                            .map((o) => ({
                            name: String(o.name),
                            value: o.value,
                        })),
                    })),
                    credentials: desc.credentials?.map((c) => ({
                        name: c.name,
                        required: c.required,
                        ...(c.displayOptions
                            ? { displayOptions: c.displayOptions }
                            : {}),
                    })),
                    inputs: Array.isArray(desc.inputs) ? desc.inputs.map(String) : [],
                    outputs: Array.isArray(desc.outputs) ? desc.outputs.map(String) : [],
                    ...(desc.webhooks ? { webhooks: desc.webhooks } : {}),
                    ...(desc.polling ? { polling: desc.polling } : {}),
                    ...(desc.triggerPanel !== undefined ? { triggerPanel: desc.triggerPanel } : {}),
                };
            },
            getNodeTypeDefinition: async (nodeType, options) => {
                const result = (0, node_definition_resolver_1.resolveNodeTypeDefinition)(nodeType, this.getNodeDefinitionDirs(), options);
                if (result.error) {
                    return { content: '', error: result.error };
                }
                const nodes = await getNodes();
                const nodeDesc = findNodeByVersion(nodes, nodeType, normalizeNodeVersion(result.version ?? options?.version));
                const builderHint = nodeDesc?.builderHint?.searchHint;
                return {
                    content: result.content,
                    version: result.version,
                    ...(builderHint ? { builderHint } : {}),
                };
            },
            listDiscriminators: async (nodeType) => {
                return (0, node_definition_resolver_1.listNodeDiscriminators)(nodeType, this.getNodeDefinitionDirs());
            },
            getParameterIssues: async (nodeType, typeVersion, parameters) => {
                const nodes = await getNodes();
                const desc = findNodeByVersion(nodes, nodeType, typeVersion);
                if (!desc)
                    return {};
                const nodeProperties = desc.properties;
                const paramsWithDefaults = resolveDisplayedDefaults(nodeProperties, parameters, nodeType, typeVersion, desc);
                const minimalNode = {
                    id: '',
                    name: '',
                    type: nodeType,
                    typeVersion,
                    parameters: paramsWithDefaults,
                    position: [0, 0],
                };
                const issues = n8n_workflow_1.NodeHelpers.getNodeParametersIssues(nodeProperties, minimalNode, desc);
                const allIssues = issues?.parameters ?? {};
                const topLevelPropsByName = new Map();
                for (const prop of nodeProperties) {
                    const existing = topLevelPropsByName.get(prop.name);
                    if (existing) {
                        existing.push(prop);
                    }
                    else {
                        topLevelPropsByName.set(prop.name, [prop]);
                    }
                }
                const filteredIssues = {};
                for (const [key, value] of Object.entries(allIssues)) {
                    const props = topLevelPropsByName.get(key);
                    if (!props)
                        continue;
                    const isDisplayed = props.some((prop) => {
                        if (prop.type === 'hidden')
                            return false;
                        if (prop.displayOptions &&
                            !n8n_workflow_1.NodeHelpers.displayParameter(paramsWithDefaults, prop, minimalNode, desc)) {
                            return false;
                        }
                        return true;
                    });
                    if (!isDisplayed)
                        continue;
                    filteredIssues[key] = value;
                }
                return filteredIssues;
            },
            getNodeCredentialTypes: async (nodeType, typeVersion, parameters, _existingCredentials) => {
                const nodes = await getNodes();
                const desc = findNodeByVersion(nodes, nodeType, typeVersion);
                if (!desc)
                    return [];
                const credentialTypes = new Set();
                const paramsWithDefaults = resolveDisplayedDefaults(desc.properties, parameters, nodeType, typeVersion, desc);
                const minimalNode = {
                    id: '',
                    name: '',
                    type: nodeType,
                    typeVersion,
                    parameters: paramsWithDefaults,
                    position: [0, 0],
                };
                const nodeCredentials = desc.credentials ?? [];
                for (const cred of nodeCredentials) {
                    if (cred.displayOptions) {
                        if (!n8n_workflow_1.NodeHelpers.displayParameter(paramsWithDefaults, cred, minimalNode, desc)) {
                            continue;
                        }
                    }
                    credentialTypes.add(cred.name);
                }
                const issues = n8n_workflow_1.NodeHelpers.getNodeParametersIssues(desc.properties, minimalNode, desc);
                const credentialIssues = issues?.credentials ?? {};
                for (const credType of Object.keys(credentialIssues)) {
                    credentialTypes.add(credType);
                }
                if (parameters.authentication === 'genericCredentialType' && parameters.genericAuthType) {
                    credentialTypes.add(parameters.genericAuthType);
                }
                else if (parameters.authentication === 'predefinedCredentialType' &&
                    parameters.nodeCredentialType) {
                    credentialTypes.add(parameters.nodeCredentialType);
                }
                return Array.from(credentialTypes);
            },
            getResolvedNodeInputs: async (workflowJson, nodeName) => {
                const nodeJson = workflowJson.nodes.find((n) => n.name === nodeName);
                if (!nodeJson)
                    return [];
                const nodeType = this.nodeTypes.getByNameAndVersion(nodeJson.type, nodeJson.typeVersion ?? 1);
                if (!nodeType)
                    return [];
                const workflow = new n8n_workflow_1.Workflow({
                    nodes: workflowJson.nodes,
                    connections: workflowJson.connections,
                    active: false,
                    nodeTypes: this.nodeTypes,
                });
                const workflowNode = workflow.getNode(nodeName);
                if (!workflowNode)
                    return [];
                return n8n_workflow_1.NodeHelpers.getNodeInputs(workflow, workflowNode, nodeType.description);
            },
            exploreResources: async (params) => {
                const credential = await credentialsFinderService.findCredentialForUser(params.credentialId, user, ['credential:read']);
                if (!credential || credential.type !== params.credentialType) {
                    throw new Error(`Credential ${params.credentialId} not found or not accessible`);
                }
                const nodeTypeAndVersion = {
                    name: params.nodeType,
                    version: params.version,
                };
                const currentNodeParameters = (params.currentNodeParameters ?? {});
                const credentials = {
                    [credential.type]: { id: credential.id, name: credential.name },
                };
                if (!currentNodeParameters.authentication) {
                    const nodes = await getNodes();
                    const nodeDesc = nodes.find((n) => n.name === params.nodeType);
                    if (nodeDesc) {
                        const authProp = nodeDesc.properties.find((p) => p.name === 'authentication');
                        if (authProp?.options) {
                            for (const opt of authProp.options) {
                                if (typeof opt === 'object' && 'value' in opt && typeof opt.value === 'string') {
                                    const credTypes = nodeDesc.credentials
                                        ?.filter((c) => {
                                        const show = c.displayOptions?.show?.authentication;
                                        return Array.isArray(show) && show.includes(opt.value);
                                    })
                                        .map((c) => c.name);
                                    if (credTypes?.includes(params.credentialType)) {
                                        currentNodeParameters.authentication = opt.value;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                const personalProject = await projectRepository.getPersonalProjectForUserOrFail(user.id);
                const additionalData = await (0, workflow_execute_additional_data_1.getBase)({
                    userId: user.id,
                    projectId: personalProject.id,
                    currentNodeParameters,
                });
                let builderHint;
                {
                    const nodes = await getNodes();
                    const nodeDesc = nodes.find((n) => n.name === params.nodeType);
                    if (nodeDesc) {
                        builderHint = findBuilderHintForMethod(nodeDesc, params.methodName, params.methodType);
                    }
                }
                try {
                    if (params.methodType === 'listSearch') {
                        const result = await dynamicNodeParametersService.getResourceLocatorResults(params.methodName, '', additionalData, nodeTypeAndVersion, currentNodeParameters, credentials, params.filter, params.paginationToken);
                        return {
                            results: (result.results ?? []).map((r) => ({
                                name: String(r.name),
                                value: r.value,
                                url: r.url,
                            })),
                            paginationToken: result.paginationToken,
                            ...(builderHint ? { builderHint } : {}),
                        };
                    }
                    const options = await dynamicNodeParametersService.getOptionsViaMethodName(params.methodName, '', additionalData, nodeTypeAndVersion, currentNodeParameters, credentials);
                    return {
                        results: options.map((o) => ({
                            name: String(o.name),
                            value: o.value,
                            description: o.description,
                        })),
                        ...(builderHint ? { builderHint } : {}),
                    };
                }
                catch (error) {
                    this.logger.error('Failed to load options for explore-resources', {
                        error: error instanceof Error ? error.message : String(error),
                    });
                    throw error;
                }
            },
        };
    }
    createWorkspaceAdapter(user) {
        const { projectService, folderService, tagService, workflowFinderService, workflowService, executionRepository, executionPersistence, eventService, } = this;
        const assertNotReadOnly = (resource) => this.assertInstanceNotReadOnly(resource);
        const { assertProjectScope } = this.createProjectScopeHelpers(user);
        const adapter = {
            async getProject(projectId) {
                const project = await projectService.getProjectWithScope(user, projectId, ['project:read']);
                if (!project)
                    return null;
                return { id: project.id, name: project.name, type: project.type };
            },
            async listProjects() {
                const projects = await projectService.getAccessibleProjects(user);
                return projects.map((p) => ({
                    id: p.id,
                    name: p.name,
                    type: p.type,
                }));
            },
            ...(this.license.isLicensed('feat:folders')
                ? {
                    async listFolders(projectId) {
                        await assertProjectScope(['folder:list'], projectId);
                        const [folders] = await folderService.getManyAndCount(projectId, { take: 100 });
                        return folders.map((f) => ({
                            id: f.id,
                            name: f.name,
                            parentFolderId: f.parentFolderId,
                        }));
                    },
                    async createFolder(name, projectId, parentFolderId) {
                        assertNotReadOnly('folders');
                        await assertProjectScope(['folder:create'], projectId);
                        const folder = await folderService.createFolder({ name, parentFolderId: parentFolderId ?? undefined }, projectId);
                        return {
                            id: folder.id,
                            name: folder.name,
                            parentFolderId: folder.parentFolderId ?? null,
                        };
                    },
                    async deleteFolder(folderId, projectId, transferToFolderId) {
                        assertNotReadOnly('folders');
                        await assertProjectScope(['folder:delete'], projectId);
                        await folderService.deleteFolder(user, folderId, projectId, {
                            transferToFolderId: transferToFolderId ?? undefined,
                        });
                    },
                    async moveWorkflowToFolder(workflowId, folderId) {
                        assertNotReadOnly('workflows');
                        const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                            'workflow:update',
                        ]);
                        if (!workflow) {
                            throw new Error(`Workflow ${workflowId} not found or not accessible`);
                        }
                        await workflowService.update(user, workflow, workflowId, {
                            parentFolderId: folderId,
                            source: 'n8n-ai',
                        });
                    },
                }
                : {}),
            async tagWorkflow(workflowId, tagNames) {
                assertNotReadOnly('workflows');
                const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:update',
                ]);
                if (!workflow) {
                    throw new Error(`Workflow ${workflowId} not found or not accessible`);
                }
                if (!(0, permissions_1.hasGlobalScope)(user, 'tag:list')) {
                    throw new Error('User does not have permission to list tags');
                }
                const existingTags = await tagService.getAll();
                const tagMap = new Map(existingTags.map((t) => [t.name.toLowerCase(), t]));
                const tagIds = [];
                for (const tagName of tagNames) {
                    const existing = tagMap.get(tagName.toLowerCase());
                    if (existing) {
                        tagIds.push(existing.id);
                    }
                    else {
                        if (!(0, permissions_1.hasGlobalScope)(user, 'tag:create')) {
                            throw new Error('User does not have permission to create tags');
                        }
                        const entity = tagService.toEntity({ name: tagName });
                        const saved = await tagService.save(entity, 'create');
                        tagIds.push(saved.id);
                    }
                }
                await workflowService.update(user, workflow, workflowId, { tagIds, source: 'n8n-ai' });
                return tagNames;
            },
            async listTags() {
                if (!(0, permissions_1.hasGlobalScope)(user, 'tag:list')) {
                    throw new Error('User does not have permission to list tags');
                }
                const tags = await tagService.getAll();
                return tags.map((t) => ({ id: t.id, name: t.name }));
            },
            async createTag(name) {
                if (!(0, permissions_1.hasGlobalScope)(user, 'tag:create')) {
                    throw new Error('User does not have permission to create tags');
                }
                const entity = tagService.toEntity({ name });
                const saved = await tagService.save(entity, 'create');
                return { id: saved.id, name: saved.name };
            },
            async cleanupTestExecutions(workflowId, options) {
                assertNotReadOnly('executions');
                const workflow = await workflowFinderService.findWorkflowForUser(workflowId, user, [
                    'workflow:execute',
                ]);
                if (!workflow) {
                    throw new Error(`Workflow ${workflowId} not found or not accessible`);
                }
                const olderThanHours = options?.olderThanHours ?? 1;
                const cutoff = new Date(Date.now() - olderThanHours * 60 * 60 * 1000);
                const executions = await executionRepository.find({
                    select: ['id'],
                    where: {
                        workflowId,
                        mode: 'manual',
                        startedAt: (0, typeorm_1.LessThan)(cutoff),
                    },
                });
                if (executions.length === 0) {
                    return { deletedCount: 0 };
                }
                const ids = executions.map((e) => e.id);
                await executionPersistence.hardDeleteBy({
                    filters: { workflowId, mode: 'manual' },
                    accessibleWorkflowIds: [workflowId],
                    deleteConditions: { deleteBefore: cutoff },
                });
                eventService.emit('execution-deleted', {
                    user: {
                        id: user.id,
                        email: user.email,
                        firstName: user.firstName,
                        lastName: user.lastName,
                        role: user.role,
                    },
                    executionIds: ids,
                    deleteBefore: cutoff,
                });
                return { deletedCount: ids.length };
            },
        };
        return adapter;
    }
};
exports.InstanceAiAdapterService = InstanceAiAdapterService;
exports.InstanceAiAdapterService = InstanceAiAdapterService = __decorate([
    (0, di_1.Service)(),
    __metadata("design:paramtypes", [backend_common_1.Logger,
        config_1.GlobalConfig,
        workflow_service_1.WorkflowService,
        workflow_finder_service_1.WorkflowFinderService,
        db_1.WorkflowRepository,
        db_1.SharedWorkflowRepository,
        db_1.ProjectRepository,
        db_1.ExecutionRepository,
        credentials_service_1.CredentialsService,
        credentials_finder_service_1.CredentialsFinderService,
        active_executions_1.ActiveExecutions,
        workflow_runner_1.WorkflowRunner,
        load_nodes_and_credentials_1.LoadNodesAndCredentials,
        node_types_1.NodeTypes,
        n8n_core_1.InstanceSettings,
        data_table_service_1.DataTableService,
        data_table_repository_1.DataTableRepository,
        dynamic_node_parameters_service_1.DynamicNodeParametersService,
        folder_service_1.FolderService,
        project_service_ee_1.ProjectService,
        tag_service_1.TagService,
        source_control_preferences_service_ee_1.SourceControlPreferencesService,
        instance_ai_settings_service_1.InstanceAiSettingsService,
        workflow_history_service_1.WorkflowHistoryService,
        workflow_service_ee_1.EnterpriseWorkflowService,
        license_1.License,
        execution_persistence_1.ExecutionPersistence,
        event_service_1.EventService,
        role_service_1.RoleService,
        telemetry_1.Telemetry,
        db_1.AiBuilderTemporaryWorkflowRepository,
        ssrf_protection_service_1.SsrfProtectionService])
], InstanceAiAdapterService);
const MAX_RESULT_CHARS = 20_000;
const MAX_NODE_OUTPUT_CHARS = 1_000;
async function resolveDataTableByIdOrName(repository, logger, idOrName, options) {
    const byId = await repository.findOneBy({ id: idOrName });
    if (byId) {
        if (options?.accessFilter && !(await options.accessFilter(byId.id))) {
            return { kind: 'miss' };
        }
        return { kind: 'hit', table: byId };
    }
    const candidates = await repository.findBy({
        name: idOrName,
        ...(options?.projectIdFilter ? { projectId: options.projectIdFilter } : {}),
    });
    let filtered = candidates;
    if (options?.accessFilter) {
        filtered = [];
        for (const c of candidates) {
            if (await options.accessFilter(c.id))
                filtered.push(c);
        }
    }
    if (filtered.length === 0)
        return { kind: 'miss' };
    if (filtered.length > 1)
        return { kind: 'ambiguous', candidates: filtered };
    const hit = filtered[0];
    logger.warn('data-tables tool called with table name instead of id — resolved by name fallback', {
        passedValue: idOrName,
        resolvedId: hit.id,
        projectId: hit.projectId,
    });
    return { kind: 'hit', table: hit };
}
function findBuilderHintForMethod(nodeDesc, methodName, methodType) {
    const referencesMethod = (prop) => {
        switch (methodType) {
            case 'loadOptions':
                return prop.typeOptions?.loadOptionsMethod === methodName;
            case 'listSearch': {
                const modes = prop.modes ?? [];
                return modes.some((mode) => mode.typeOptions?.searchListMethod === methodName);
            }
        }
    };
    const isCollection = (item) => 'values' in item;
    const isProperty = (item) => 'type' in item;
    const searchProps = (items) => {
        for (const item of items ?? []) {
            if (isCollection(item)) {
                const nested = searchProps(item.values);
                if (nested)
                    return nested;
                continue;
            }
            if (!isProperty(item))
                continue;
            if (referencesMethod(item) && item.builderHint?.propertyHint) {
                return item.builderHint.propertyHint;
            }
            const nested = searchProps(item.options);
            if (nested)
                return nested;
        }
        return undefined;
    };
    return searchProps(nodeDesc.properties);
}
function truncateResultData(resultData) {
    const serialized = JSON.stringify(resultData);
    if (serialized.length <= MAX_RESULT_CHARS)
        return resultData;
    const truncated = {};
    for (const [nodeName, items] of Object.entries(resultData)) {
        if (!Array.isArray(items) || items.length === 0) {
            truncated[nodeName] = items;
            continue;
        }
        const itemStr = JSON.stringify(items[0]);
        const preview = itemStr.length > MAX_NODE_OUTPUT_CHARS
            ? `${itemStr.slice(0, MAX_NODE_OUTPUT_CHARS)}…`
            : items[0];
        truncated[nodeName] = {
            _itemCount: items.length,
            _truncated: true,
            _firstItemPreview: preview,
        };
    }
    return truncated;
}
function wrapResultDataEntries(data) {
    const wrapped = {};
    for (const [nodeName, value] of Object.entries(data)) {
        wrapped[nodeName] = (0, instance_ai_1.wrapUntrustedData)(JSON.stringify(value, null, 2), 'execution-output', `node:${nodeName}`);
    }
    return wrapped;
}
async function extractExecutionResult(executionRepository, executionId, includeOutputData = true) {
    const execution = await executionRepository.findSingleExecution(executionId, {
        includeData: true,
        unflattenData: true,
    });
    if (!execution) {
        return { executionId, status: 'unknown' };
    }
    const status = execution.status === 'error' || execution.status === 'crashed'
        ? 'error'
        : execution.status === 'running' || execution.status === 'new'
            ? 'running'
            : execution.status === 'waiting'
                ? 'waiting'
                : 'success';
    const resultData = {};
    if (includeOutputData) {
        const runData = execution.data?.resultData?.runData;
        if (runData) {
            for (const [nodeName, nodeRuns] of Object.entries(runData)) {
                const lastRun = nodeRuns[nodeRuns.length - 1];
                if (lastRun?.data?.main) {
                    const outputItems = lastRun.data.main
                        .flat()
                        .filter((item) => item !== null && item !== undefined)
                        .map((item) => item.json);
                    if (outputItems.length > 0) {
                        resultData[nodeName] = truncateNodeOutput(outputItems);
                    }
                }
            }
        }
    }
    const error = execution.data?.resultData?.error;
    const errorMessage = error ? formatExecutionError(error, includeOutputData) : undefined;
    return {
        executionId,
        status,
        data: Object.keys(resultData).length > 0
            ? wrapResultDataEntries(truncateResultData(resultData))
            : undefined,
        error: errorMessage,
        startedAt: execution.startedAt?.toISOString(),
        finishedAt: execution.stoppedAt?.toISOString(),
    };
}
const MAX_ERROR_CHARS = 4_000;
function formatExecutionError(error, includeUpstreamDetails) {
    const parts = [];
    if (error.message)
        parts.push(error.message);
    if (includeUpstreamDetails) {
        if (error.description && error.description !== error.message) {
            parts.push(error.description);
        }
        if ('messages' in error && error.messages.length > 0) {
            parts.push(`Details: ${error.messages.join(' | ')}`);
        }
    }
    else {
        const hasDescription = !!error.description && error.description !== error.message;
        const hasMessages = 'messages' in error && error.messages.length > 0;
        if (hasDescription || hasMessages) {
            parts.push('(upstream error details suppressed by the instance AI privacy setting; ask the user to share the node error from the UI)');
        }
    }
    const combined = parts.join(' — ') || 'Unknown error';
    return combined.length > MAX_ERROR_CHARS ? `${combined.slice(0, MAX_ERROR_CHARS)}…` : combined;
}
const MAX_NODE_OUTPUT_BYTES = 5_000;
function truncateNodeOutput(items) {
    const serialized = JSON.stringify(items);
    if (serialized.length <= MAX_NODE_OUTPUT_BYTES)
        return items;
    const truncated = [];
    let size = 2;
    for (const item of items) {
        const itemStr = JSON.stringify(item);
        if (size + itemStr.length + 2 > MAX_NODE_OUTPUT_BYTES)
            break;
        truncated.push(item);
        size += itemStr.length + 1;
    }
    return {
        items: truncated,
        truncated: true,
        totalItems: items.length,
        shownItems: truncated.length,
        message: `Output truncated: showing ${truncated.length} of ${items.length} items. Use get-node-output to retrieve full data for this node.`,
    };
}
const MAX_ITEM_CHARS = 50_000;
async function extractNodeOutput(executionRepository, executionId, nodeName, options) {
    const execution = await executionRepository.findSingleExecution(executionId, {
        includeData: true,
        unflattenData: true,
    });
    if (!execution) {
        throw new Error(`Execution ${executionId} not found`);
    }
    const runData = execution.data?.resultData?.runData;
    if (!runData?.[nodeName]) {
        throw new Error(`Node "${nodeName}" not found in execution ${executionId}`);
    }
    const nodeRuns = runData[nodeName];
    const lastRun = nodeRuns[nodeRuns.length - 1];
    const startIndex = options?.startIndex ?? 0;
    const maxItems = Math.min(options?.maxItems ?? 10, 50);
    let index = 0;
    let totalItems = 0;
    const collected = [];
    for (const output of lastRun?.data?.main ?? []) {
        for (const item of output ?? []) {
            totalItems++;
            if (index >= startIndex && collected.length < maxItems) {
                collected.push(item.json);
            }
            index++;
        }
    }
    const capped = collected.map((item) => {
        const str = JSON.stringify(item);
        if (str.length > MAX_ITEM_CHARS) {
            return {
                _truncatedItem: true,
                preview: str.slice(0, MAX_ITEM_CHARS),
                originalLength: str.length,
            };
        }
        return item;
    });
    return {
        nodeName,
        items: capped.map((item, i) => (0, instance_ai_1.wrapUntrustedData)(JSON.stringify(item, null, 2), 'execution-output', `node:${nodeName}[${startIndex + i}]`)),
        totalItems,
        returned: { from: startIndex, to: startIndex + capped.length },
    };
}
const KNOWN_TRIGGER_TYPES = new Set([
    n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE,
    n8n_workflow_1.FORM_TRIGGER_NODE_TYPE,
    n8n_workflow_1.WEBHOOK_NODE_TYPE,
    n8n_workflow_1.SCHEDULE_TRIGGER_NODE_TYPE,
]);
function findTriggerNode(nodes) {
    const known = nodes.find((n) => KNOWN_TRIGGER_TYPES.has(n.type));
    if (known)
        return known;
    return nodes.find((n) => n.type.includes('Trigger') || n.type.includes('trigger') || n.type.includes('webhook'));
}
function getExecutionModeForTrigger(node) {
    switch (node.type) {
        case n8n_workflow_1.WEBHOOK_NODE_TYPE:
            return 'webhook';
        case n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE:
            return 'chat';
        case n8n_workflow_1.FORM_TRIGGER_NODE_TYPE:
        case n8n_workflow_1.SCHEDULE_TRIGGER_NODE_TYPE:
            return 'trigger';
        default:
            return 'manual';
    }
}
function validateInputDataShape(node, inputData) {
    if (node.type === n8n_workflow_1.FORM_TRIGGER_NODE_TYPE) {
        const formFieldsValue = inputData.formFields;
        const looksWrapped = typeof formFieldsValue === 'object' && formFieldsValue !== null;
        if (looksWrapped) {
            throw new Error('verify-built-workflow: inputData for a Form Trigger must be a flat field map ' +
                '(e.g. {name: "Alice", email: "a@b.c"}), NOT wrapped in `formFields`. ' +
                'The production Form Trigger emits fields directly on $json, so downstream ' +
                'expressions like $json.name are correct. Re-run with the flat shape.');
        }
    }
}
function getPinDataForTrigger(node, inputData) {
    validateInputDataShape(node, inputData);
    switch (node.type) {
        case n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE:
            return {
                [node.name]: [
                    {
                        json: {
                            sessionId: `instance-ai-${Date.now()}`,
                            action: 'sendMessage',
                            chatInput: typeof inputData.chatInput === 'string'
                                ? inputData.chatInput
                                : JSON.stringify(inputData),
                        },
                    },
                ],
            };
        case n8n_workflow_1.FORM_TRIGGER_NODE_TYPE:
            return {
                [node.name]: [
                    {
                        json: {
                            submittedAt: new Date().toISOString(),
                            formMode: 'instanceAi',
                            ...inputData,
                        },
                    },
                ],
            };
        case n8n_workflow_1.WEBHOOK_NODE_TYPE: {
            const envelopeKeys = new Set(['body', 'headers', 'query']);
            const inputKeys = Object.keys(inputData);
            const looksLikeEnvelope = inputKeys.length > 0 &&
                inputKeys.every((k) => envelopeKeys.has(k)) &&
                typeof inputData.body === 'object' &&
                inputData.body !== null;
            const body = looksLikeEnvelope ? inputData.body : inputData;
            const headers = looksLikeEnvelope && typeof inputData.headers === 'object' && inputData.headers !== null
                ? inputData.headers
                : {};
            const query = looksLikeEnvelope && typeof inputData.query === 'object' && inputData.query !== null
                ? inputData.query
                : {};
            return {
                [node.name]: [
                    {
                        json: { headers, query, body },
                    },
                ],
            };
        }
        case n8n_workflow_1.SCHEDULE_TRIGGER_NODE_TYPE: {
            const now = new Date();
            return {
                [node.name]: [
                    {
                        json: {
                            timestamp: now.toISOString(),
                            'Readable date': now.toLocaleString(),
                            'Day of week': now.toLocaleDateString('en-US', { weekday: 'long' }),
                            Year: String(now.getFullYear()),
                            Month: now.toLocaleDateString('en-US', { month: 'long' }),
                            'Day of month': String(now.getDate()).padStart(2, '0'),
                            Hour: String(now.getHours()).padStart(2, '0'),
                            Minute: String(now.getMinutes()).padStart(2, '0'),
                            Second: String(now.getSeconds()).padStart(2, '0'),
                        },
                    },
                ],
            };
        }
        default:
            return {
                [node.name]: [{ json: inputData }],
            };
    }
}
async function extractExecutionDebugInfo(executionRepository, executionId, includeOutputData = true) {
    const execution = await executionRepository.findSingleExecution(executionId, {
        includeData: true,
        unflattenData: true,
    });
    if (!execution) {
        return {
            executionId,
            status: 'unknown',
            nodeTrace: [],
        };
    }
    const baseResult = await extractExecutionResult(executionRepository, executionId, includeOutputData);
    const runData = execution.data?.resultData?.runData;
    const nodeTrace = [];
    let failedNode;
    if (runData) {
        const workflowNodes = execution.workflowData?.nodes ?? [];
        const nodeTypeMap = new Map(workflowNodes.map((n) => [n.name, n.type]));
        for (const [nodeName, nodeRuns] of Object.entries(runData)) {
            const lastRun = nodeRuns[nodeRuns.length - 1];
            if (!lastRun)
                continue;
            const nodeType = nodeTypeMap.get(nodeName) ?? 'unknown';
            nodeTrace.push({
                name: nodeName,
                type: nodeType,
                status: lastRun.error !== undefined ? 'error' : 'success',
                startedAt: lastRun.startTime !== undefined ? new Date(lastRun.startTime).toISOString() : undefined,
                finishedAt: lastRun.startTime !== undefined && lastRun.executionTime !== undefined
                    ? new Date(lastRun.startTime + lastRun.executionTime).toISOString()
                    : undefined,
            });
            if (lastRun.error !== undefined && !failedNode) {
                failedNode = {
                    name: nodeName,
                    type: nodeType,
                    error: formatExecutionError(lastRun.error, includeOutputData),
                    inputData: includeOutputData
                        ? (() => {
                            const inputItems = lastRun.data?.main
                                ?.flat()
                                .filter((item) => item !== null && item !== undefined)
                                .map((item) => item.json);
                            if (inputItems && inputItems.length > 0) {
                                const raw = inputItems[0];
                                return (0, instance_ai_1.wrapUntrustedData)(JSON.stringify(raw, null, 2), 'execution-output', `failed-node-input:${nodeName}`);
                            }
                            return undefined;
                        })()
                        : undefined,
                };
            }
        }
    }
    return {
        ...baseResult,
        failedNode,
        nodeTrace,
    };
}
function sdkPinDataToRuntime(pinData) {
    const result = {};
    if (!pinData)
        return result;
    for (const [nodeName, items] of Object.entries(pinData)) {
        result[nodeName] = items.map((item) => ({ json: (item ?? {}) }));
    }
    return result;
}
function toWorkflowJSON(workflow, options) {
    const redact = options?.redactParameters ?? false;
    return {
        id: workflow.id,
        name: workflow.name,
        nodes: (workflow.nodes ?? []).map((n) => ({
            id: n.id ?? '',
            name: n.name,
            type: n.type,
            typeVersion: n.typeVersion,
            position: n.position,
            parameters: redact ? {} : n.parameters,
            credentials: n.credentials,
            webhookId: n.webhookId,
            disabled: n.disabled,
            notes: n.notes,
            notesInFlow: n.notesInFlow,
            executeOnce: n.executeOnce,
            retryOnFail: n.retryOnFail,
            alwaysOutputData: n.alwaysOutputData,
            onError: n.onError,
        })),
        connections: workflow.connections,
        settings: workflow.settings,
    };
}
function toWorkflowDetail(workflow, options) {
    const redact = options?.redactParameters ?? false;
    return {
        id: workflow.id,
        name: workflow.name,
        versionId: workflow.versionId,
        activeVersionId: workflow.activeVersionId ?? null,
        isArchived: workflow.isArchived,
        createdAt: workflow.createdAt.toISOString(),
        updatedAt: workflow.updatedAt.toISOString(),
        nodes: (workflow.nodes ?? []).map((n) => ({
            name: n.name,
            type: n.type,
            parameters: redact ? undefined : n.parameters,
            position: n.position,
            webhookId: n.webhookId,
        })),
        connections: workflow.connections,
        settings: workflow.settings,
    };
}
//# sourceMappingURL=instance-ai.adapter.service.js.map