UNPKG

n8n

Version:

n8n Workflow Automation Tool

281 lines (274 loc) 11 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.AgentSecureRuntime = void 0; const backend_common_1 = require("@n8n/backend-common"); const di_1 = require("@n8n/di"); const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const sucrase_1 = require("sucrase"); const agent_isolate_pool_1 = require("./agent-isolate-pool"); const LIBRARY_BUNDLE_PATH = path_1.default.resolve(__dirname, '../../../../dist/agent-library-bundle.js'); let AgentSecureRuntime = class AgentSecureRuntime { constructor(logger) { this.logger = logger; this.pool = null; this.poolInitPromise = null; this.disposed = false; this.libraryBundle = null; } async getPool() { if (this.pool) return this.pool; this.poolInitPromise ??= (async () => { try { const ivmModule = (await Promise.resolve().then(() => __importStar(require('isolated-vm')))).default; const libraryBundle = this.getLibraryBundle(); const pool = new agent_isolate_pool_1.AgentIsolatePool(ivmModule, libraryBundle, { logger: this.logger, }); await pool.initialize(); if (this.disposed) { void pool.dispose(); throw new Error('AgentSecureRuntime was disposed during pool initialization'); } this.pool = pool; return pool; } catch (e) { this.poolInitPromise = null; throw e; } })(); return await this.poolInitPromise; } async withIsolate(fn) { const pool = await this.getPool(); const slot = await pool.acquire(); try { return await fn(slot); } catch (error) { if (!slot.isHealthy) { this.logger.warn('[AgentSecureRuntime] Isolate OOM during execution — retrying with fresh slot'); const freshSlot = await pool.acquire(); try { return await fn(freshSlot); } catch (retryError) { if (!freshSlot.isHealthy) { this.logger.error('[AgentSecureRuntime] Retry slot also OOM — both isolates will be recycled; not retrying further'); } throw retryError; } finally { pool.release(freshSlot); } } throw error; } finally { pool.release(slot); } } getLibraryBundle() { if (this.libraryBundle) return this.libraryBundle; try { this.libraryBundle = (0, fs_1.readFileSync)(LIBRARY_BUNDLE_PATH, 'utf8'); } catch (e) { throw new Error(`Agent library bundle not found at ${LIBRARY_BUNDLE_PATH}. ` + "Run 'pnpm build' in packages/cli to generate it.", { cause: e instanceof Error ? e : undefined }); } return this.libraryBundle; } compileTs(tsCode) { const { code } = (0, sucrase_1.transform)(tsCode, { transforms: ['typescript', 'imports'], }); return code; } parseSandboxJson(raw, context) { if (typeof raw !== 'string') { throw new Error(`Sandbox (${context}) produced a non-string result: ${typeof raw}. Expected JSON string.`); } try { return JSON.parse(raw, (key, value) => (key === '__proto__' ? undefined : value)); } catch (e) { throw new Error(`Sandbox (${context}) produced invalid JSON: ${e instanceof Error ? e.message : String(e)}`); } } runInContext(context, slot, code) { context.evalSync('var module = { exports: {} }; var exports = module.exports;', { timeout: 5000, }); const script = slot.isolate.compileScriptSync(code); script.runSync(context, { timeout: 5000 }); script.release(); const ref = context.global.getSync('module', { reference: true }); const moduleObj = ref.copySync(); ref.release(); return moduleObj.exports; } async describeToolSecurely(tsCode) { const jsCode = this.compileTs(tsCode); return await this.withIsolate(async (slot) => { const context = slot.createContext(); try { const wrapper = ` var __me = {}; var __mod = { exports: __me }; (function(exports, require, module) { ${jsCode} })(__me, require, __mod); var __exported = __mod.exports.default || __mod.exports; if (!__exported || typeof __exported !== 'object' || typeof __exported.describe !== 'function') { throw new Error('No Tool found. Export a Tool as default: export default new Tool(...);'); } module.exports = JSON.stringify(__exported.describe()); `; const resultJson = this.runInContext(context, slot, wrapper); return this.parseSandboxJson(resultJson, 'describeToolSecurely'); } finally { context.release(); } }); } async executeToolInIsolate(toolCode, input, ctx) { const jsCode = this.compileTs(toolCode); return await this.withIsolate(async (slot) => { const context = slot.createContext(); try { const setupCode = ` var __me = {}; var __mod = { exports: __me }; (function(exports, require, module) { ${jsCode} })(__me, require, __mod); globalThis.__exportedTool = __mod.exports.default || __mod.exports; if (!globalThis.__exportedTool || typeof globalThis.__exportedTool !== 'object') { throw new Error('No Tool found'); } `; const setupScript = slot.isolate.compileScriptSync(setupCode); setupScript.runSync(context, { timeout: 5000 }); setupScript.release(); const serializedArgs = JSON.stringify({ input, ctx }); const invokeCode = ` (async function() { var tool = globalThis.__exportedTool; var handlerFn = tool.handlerFn || (tool.build ? tool.build().handler : null); if (!handlerFn) { var built = tool.build ? tool.build() : tool; handlerFn = built.handler; } if (!handlerFn) throw new Error('Tool has no handler'); var args = ${serializedArgs}; var suspendPayload = { called: false, data: null }; var ctx = Object.assign({}, args.ctx || {}, { suspend: function(payload) { suspendPayload.called = true; suspendPayload.data = payload; return Promise.resolve({ suspended: true }); }, }); var result = await handlerFn(args.input, ctx); if (suspendPayload.called) { return JSON.stringify({ __suspend: true, payload: suspendPayload.data }); } return JSON.stringify(result); })() `; const resultJson = (await context.eval(invokeCode, { timeout: 5000, promise: true, copy: true, })); const parsed = this.parseSandboxJson(resultJson, 'executeToolInIsolate'); if (parsed?.__suspend) { return { [Symbol.for('n8n.agent.suspend')]: true, payload: parsed.payload, }; } return parsed; } finally { context.release(); } }); } createToolExecutor(toolsByName) { return { executeTool: async (toolName, input, ctx) => { const toolCode = toolsByName[toolName]; if (!toolCode) { throw new Error(`Tool "${toolName}" not found in tools map`); } return await this.executeToolInIsolate(toolCode, input, ctx); }, }; } dispose() { this.disposed = true; const poolPromise = this.poolInitPromise; this.pool = null; this.poolInitPromise = null; if (poolPromise) { void poolPromise.then(async (pool) => await pool.dispose()).catch(() => { }); } } }; exports.AgentSecureRuntime = AgentSecureRuntime; exports.AgentSecureRuntime = AgentSecureRuntime = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.Logger]) ], AgentSecureRuntime); //# sourceMappingURL=agent-secure-runtime.js.map