@atomoz/workflows-nodes 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,6 +1,8 @@
1
+ var __create = Object.create;
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __export = (target, all) => {
6
8
  for (var name in all)
@@ -14,6 +16,14 @@ var __copyProps = (to, from, except, desc) => {
14
16
  }
15
17
  return to;
16
18
  };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
17
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
28
 
19
29
  // index.ts
@@ -1088,7 +1098,7 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
1088
1098
  case "gemini":
1089
1099
  return new import_google_gauth.ChatGoogle({
1090
1100
  model: "gemini-flash-latest",
1091
- apiKey: "AIzaSyBzrL8Hx6dHhXgwc2HfLlQsf5Y-9pdtc9M",
1101
+ apiKey: "AIzaSyAWj0Al2sVJ8vqaRN4UY7c6imAg7a3NbEc",
1092
1102
  streaming
1093
1103
  });
1094
1104
  case "openai":
@@ -1114,6 +1124,50 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
1114
1124
  }
1115
1125
 
1116
1126
  // src/utils/guardrail-executor.ts
1127
+ async function loadChatHistory(context) {
1128
+ const checkpointer = context?.checkpointer;
1129
+ const sessionId = context?.sessionId;
1130
+ if (!checkpointer || !sessionId) {
1131
+ console.log(`[GUARDRAIL] checkpointer=${!!checkpointer}, sessionId=${sessionId}`);
1132
+ return null;
1133
+ }
1134
+ try {
1135
+ console.log(`[GUARDRAIL] Carregando hist\xF3rico da mem\xF3ria (session: ${sessionId})`);
1136
+ if (typeof checkpointer.setup === "function" && !checkpointer.isSetup) {
1137
+ await checkpointer.setup();
1138
+ }
1139
+ let checkpoint = null;
1140
+ if (typeof checkpointer.getTuple === "function") {
1141
+ const tuple = await checkpointer.getTuple({ configurable: { thread_id: sessionId } });
1142
+ checkpoint = tuple?.checkpoint;
1143
+ console.log(`[GUARDRAIL] getTuple result:`, tuple ? "found" : "null");
1144
+ }
1145
+ if (!checkpoint && typeof checkpointer.get === "function") {
1146
+ checkpoint = await checkpointer.get({ configurable: { thread_id: sessionId } });
1147
+ console.log(`[GUARDRAIL] get result:`, checkpoint ? "found" : "null");
1148
+ }
1149
+ if (!checkpoint) {
1150
+ console.log(`[GUARDRAIL] Nenhum checkpoint encontrado`);
1151
+ return null;
1152
+ }
1153
+ console.log(`[GUARDRAIL] Checkpoint keys:`, Object.keys(checkpoint));
1154
+ const messages = checkpoint?.channel_values?.messages || checkpoint?.values?.messages || checkpoint?.messages || [];
1155
+ if (!messages || messages.length === 0) {
1156
+ console.log(`[GUARDRAIL] Nenhuma mensagem no checkpoint`);
1157
+ return null;
1158
+ }
1159
+ const historyLines = messages.map((m) => {
1160
+ const role = m._getType?.() === "human" || m.type === "human" ? "Usu\xE1rio" : "Assistente";
1161
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
1162
+ return `${role}: ${content.slice(0, 200)}${content.length > 200 ? "..." : ""}`;
1163
+ });
1164
+ console.log(`[GUARDRAIL] Hist\xF3rico carregado: ${messages.length} mensagens`);
1165
+ return historyLines.join("\n");
1166
+ } catch (error) {
1167
+ console.error(`[GUARDRAIL] Erro ao carregar hist\xF3rico:`, error);
1168
+ return null;
1169
+ }
1170
+ }
1117
1171
  async function executeGuardrails(guardrails, content, mode = "fail-first", context = {}) {
1118
1172
  const list = Array.isArray(guardrails) ? guardrails : [guardrails];
1119
1173
  const result = {
@@ -1199,44 +1253,90 @@ async function executeGuardrails(guardrails, content, mode = "fail-first", conte
1199
1253
  }
1200
1254
  } else if (guard.type === "model") {
1201
1255
  const { model, evaluationPrompt, name, actionInstruction } = guard;
1256
+ console.log(`[GUARDRAIL LLM] Iniciando guardrail "${name}"`);
1257
+ console.log(`[GUARDRAIL LLM] Conte\xFAdo a avaliar (${content.length} chars):`, content.slice(0, 200) + (content.length > 200 ? "..." : ""));
1258
+ console.log(`[GUARDRAIL LLM] Action: ${guard.action}, Model dispon\xEDvel: ${!!model}`);
1259
+ const chatHistory = await loadChatHistory(context);
1260
+ if (chatHistory) {
1261
+ console.log(`[GUARDRAIL LLM] Hist\xF3rico inclu\xEDdo no contexto`);
1262
+ }
1202
1263
  if (model && typeof model.invoke === "function") {
1203
1264
  try {
1204
1265
  let prompt = evaluationPrompt;
1266
+ console.log(`[GUARDRAIL LLM] Evaluation prompt base:`, (prompt || "").slice(0, 200));
1205
1267
  if (actionInstruction) {
1206
1268
  prompt += `
1207
1269
 
1208
1270
  INSTRU\xC7\xC3O ADICIONAL DE A\xC7\xC3O:
1209
1271
  ${actionInstruction}`;
1272
+ }
1273
+ if (chatHistory) {
1274
+ prompt += `
1275
+
1276
+ HIST\xD3RICO DA CONVERSA:
1277
+ ${chatHistory}`;
1210
1278
  }
1211
1279
  if (prompt.includes("{{content}}")) {
1212
1280
  prompt = prompt.replace("{{content}}", content);
1213
1281
  } else {
1214
1282
  prompt = `${prompt}
1215
1283
 
1216
- CONTE\xDADO PARA AVALIAR:
1284
+ CONTE\xDADO A AVALIAR (resposta atual):
1217
1285
  ${content}`;
1218
1286
  }
1287
+ console.log(`[GUARDRAIL LLM] Chamando modelo...`);
1219
1288
  const response = await model.invoke(prompt);
1220
1289
  const resultText = typeof response.content === "string" ? response.content : String(response.content);
1290
+ console.log(`[GUARDRAIL LLM] Resposta do modelo:`, resultText.slice(0, 300));
1291
+ const isUnsafe = resultText.toUpperCase().includes("UNSAFE");
1292
+ const isSafe = resultText.toUpperCase().startsWith("SAFE") || resultText.toUpperCase().includes("SAFE") && !isUnsafe;
1221
1293
  if (guard.action === "fix") {
1222
- if (resultText && resultText.trim() !== content.trim() && !resultText.includes("SAFE")) {
1223
- result.modifiedContent = resultText;
1224
- violationMessage = `Conte\xFAdo reescrito pelo guardrail "${name}".`;
1225
- result.violations.push({
1226
- name: guard.name,
1227
- message: violationMessage,
1228
- action: "fix"
1229
- });
1294
+ if (isUnsafe) {
1295
+ console.log(`[GUARDRAIL LLM] Modo FIX - Detectado UNSAFE, chamando modelo para corrigir...`);
1296
+ const fixPrompt = `Voc\xEA \xE9 um corretor de conte\xFAdo. O texto abaixo foi marcado como UNSAFE por violar uma regra.
1297
+
1298
+ REGRA VIOLADA:
1299
+ ${evaluationPrompt}
1300
+
1301
+ ${actionInstruction ? `INSTRU\xC7\xC3O DE CORRE\xC7\xC3O:
1302
+ ${actionInstruction}
1303
+ ` : ""}
1304
+
1305
+ CONTE\xDADO ORIGINAL:
1306
+ ${content}
1307
+
1308
+ Reescreva o conte\xFAdo corrigindo a viola\xE7\xE3o. Retorne APENAS o texto corrigido, sem explica\xE7\xF5es ou prefixos.`;
1309
+ try {
1310
+ const fixResponse = await model.invoke(fixPrompt);
1311
+ const fixedContent = typeof fixResponse.content === "string" ? fixResponse.content : String(fixResponse.content);
1312
+ console.log(`[GUARDRAIL LLM] Conte\xFAdo corrigido:`, fixedContent.slice(0, 300));
1313
+ result.modifiedContent = fixedContent;
1314
+ violationMessage = `Conte\xFAdo reescrito pelo guardrail "${name}".`;
1315
+ result.violations.push({
1316
+ name: guard.name,
1317
+ message: violationMessage,
1318
+ action: "fix"
1319
+ });
1320
+ } catch (fixError) {
1321
+ console.error(`[GUARDRAIL LLM] Erro ao corrigir conte\xFAdo:`, fixError);
1322
+ }
1323
+ } else {
1324
+ console.log(`[GUARDRAIL LLM] Modo FIX - Conte\xFAdo aprovado (SAFE)`);
1230
1325
  }
1231
1326
  } else {
1232
- if (resultText.toUpperCase().includes("UNSAFE")) {
1327
+ if (isUnsafe) {
1233
1328
  guardPassed = false;
1234
1329
  violationMessage = actionInstruction || `Modelo de avalia\xE7\xE3o "${name}" detectou viola\xE7\xE3o de seguran\xE7a/pol\xEDtica.`;
1330
+ console.log(`[GUARDRAIL LLM] Resultado: UNSAFE - Bloqueado`);
1331
+ } else {
1332
+ console.log(`[GUARDRAIL LLM] Resultado: SAFE - Aprovado`);
1235
1333
  }
1236
1334
  }
1237
1335
  } catch (error) {
1238
- console.error(`Error executing model guardrail "${name}":`, error);
1336
+ console.error(`[GUARDRAIL LLM] Erro ao executar guardrail "${name}":`, error);
1239
1337
  }
1338
+ } else {
1339
+ console.log(`[GUARDRAIL LLM] Model n\xE3o dispon\xEDvel ou n\xE3o \xE9 invoc\xE1vel para guardrail "${name}"`);
1240
1340
  }
1241
1341
  }
1242
1342
  if (!guardPassed) {
@@ -1300,7 +1400,7 @@ var IaAgentNodeFunction = async (inputs) => {
1300
1400
  let guardrailViolations = [];
1301
1401
  if (message && beforeGuardrails) {
1302
1402
  const preparedBefore = await prepareGuardrails(beforeGuardrails);
1303
- const context = { ...inputs, input: message };
1403
+ const context = { ...inputs, input: message, checkpointer, sessionId };
1304
1404
  const beforeResults = await executeGuardrails(preparedBefore, message, guardrailMode, context);
1305
1405
  if (beforeResults.blocked) {
1306
1406
  console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Blocked by before-agent guardrail:`, beforeResults.message);
@@ -1502,7 +1602,7 @@ IMPORTANT: You must base your response on the last message in the conversation h
1502
1602
  }
1503
1603
  if (output && afterGuardrails) {
1504
1604
  const preparedAfter = await prepareGuardrails(afterGuardrails);
1505
- const context = { ...inputs, input: output };
1605
+ const context = { ...inputs, input: output, checkpointer, sessionId };
1506
1606
  const afterResults = await executeGuardrails(preparedAfter, output, guardrailMode, context);
1507
1607
  if (afterResults.blocked) {
1508
1608
  console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Response blocked by after-agent guardrail:`, afterResults.message);
@@ -2391,8 +2491,271 @@ var WhatsappMessageTriggerNode = {
2391
2491
  ]
2392
2492
  };
2393
2493
 
2494
+ // src/utils/sandbox-executor.ts
2495
+ var ivmModule = null;
2496
+ async function getIVM() {
2497
+ if (ivmModule !== null) return ivmModule;
2498
+ try {
2499
+ const mod = await import("isolated-vm");
2500
+ ivmModule = {
2501
+ Isolate: mod.Isolate || mod.default?.Isolate,
2502
+ ExternalCopy: mod.ExternalCopy || mod.default?.ExternalCopy,
2503
+ Reference: mod.Reference || mod.default?.Reference
2504
+ };
2505
+ if (!ivmModule.Isolate) {
2506
+ console.warn("[Sandbox] isolated-vm Isolate n\xE3o encontrado");
2507
+ return null;
2508
+ }
2509
+ return ivmModule;
2510
+ } catch (e) {
2511
+ console.warn("[Sandbox] isolated-vm n\xE3o dispon\xEDvel, usando fallback inseguro");
2512
+ return null;
2513
+ }
2514
+ }
2515
+ var DEFAULT_OPTIONS = {
2516
+ timeout: 1e4,
2517
+ memoryLimit: 128,
2518
+ allowFetch: true
2519
+ };
2520
+ async function executeSandboxed(code, sandboxContext, options = {}) {
2521
+ const opts = { ...DEFAULT_OPTIONS, ...options };
2522
+ const ivm = await getIVM();
2523
+ if (!ivm) {
2524
+ return executeFallback(code, sandboxContext);
2525
+ }
2526
+ let isolate = null;
2527
+ let context = null;
2528
+ try {
2529
+ isolate = new ivm.Isolate({ memoryLimit: opts.memoryLimit });
2530
+ context = await isolate.createContext();
2531
+ const jail = context.global;
2532
+ await jail.set("globalThis", jail.derefInto());
2533
+ await injectContextVariables(jail, sandboxContext, ivm);
2534
+ await jail.set("__logRef", new ivm.Reference(function(...args) {
2535
+ console.log("[Sandbox]", ...args);
2536
+ }));
2537
+ if (opts.allowFetch) {
2538
+ await jail.set("__fetchRef", new ivm.Reference(async function(url, optionsJson) {
2539
+ try {
2540
+ const options2 = optionsJson ? JSON.parse(optionsJson) : {};
2541
+ const res = await fetch(url, {
2542
+ method: options2?.method || "GET",
2543
+ headers: options2?.headers,
2544
+ body: options2?.body
2545
+ });
2546
+ const contentType = res.headers.get("content-type") || "";
2547
+ let data;
2548
+ if (contentType.includes("application/json")) {
2549
+ data = await res.json();
2550
+ } else {
2551
+ data = await res.text();
2552
+ }
2553
+ return JSON.stringify({
2554
+ ok: res.ok,
2555
+ status: res.status,
2556
+ statusText: res.statusText,
2557
+ data
2558
+ });
2559
+ } catch (error) {
2560
+ return JSON.stringify({
2561
+ ok: false,
2562
+ status: 0,
2563
+ statusText: "Network Error",
2564
+ error: error.message
2565
+ });
2566
+ }
2567
+ }));
2568
+ }
2569
+ const bootstrapCode = `
2570
+ // Console
2571
+ const console = {
2572
+ log: (...args) => __logRef.applySync(undefined, args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))),
2573
+ warn: (...args) => __logRef.applySync(undefined, ['[WARN]', ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))]),
2574
+ error: (...args) => __logRef.applySync(undefined, ['[ERROR]', ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))]),
2575
+ info: (...args) => __logRef.applySync(undefined, args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))),
2576
+ };
2577
+ globalThis.console = console;
2578
+
2579
+ ${opts.allowFetch ? `
2580
+ // Fetch
2581
+ globalThis.fetch = async (url, options = {}) => {
2582
+ const optionsJson = JSON.stringify(options);
2583
+ const resultJson = await __fetchRef.apply(undefined, [url, optionsJson], { result: { promise: true } });
2584
+ const result = JSON.parse(resultJson);
2585
+
2586
+ if (!result.ok && result.error) {
2587
+ throw new Error(result.error);
2588
+ }
2589
+
2590
+ return {
2591
+ ok: result.ok,
2592
+ status: result.status,
2593
+ statusText: result.statusText,
2594
+ json: async () => result.data,
2595
+ text: async () => typeof result.data === 'string' ? result.data : JSON.stringify(result.data),
2596
+ };
2597
+ };
2598
+ ` : ""}
2599
+
2600
+ // Base64
2601
+ globalThis.atob = (str) => {
2602
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
2603
+ let output = '';
2604
+ str = String(str).replace(/=+$/, '');
2605
+ for (let i = 0; i < str.length; i += 4) {
2606
+ const enc1 = chars.indexOf(str.charAt(i));
2607
+ const enc2 = chars.indexOf(str.charAt(i + 1));
2608
+ const enc3 = chars.indexOf(str.charAt(i + 2));
2609
+ const enc4 = chars.indexOf(str.charAt(i + 3));
2610
+ const chr1 = (enc1 << 2) | (enc2 >> 4);
2611
+ const chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
2612
+ const chr3 = ((enc3 & 3) << 6) | enc4;
2613
+ output += String.fromCharCode(chr1);
2614
+ if (enc3 !== 64) output += String.fromCharCode(chr2);
2615
+ if (enc4 !== 64) output += String.fromCharCode(chr3);
2616
+ }
2617
+ return output;
2618
+ };
2619
+
2620
+ globalThis.btoa = (str) => {
2621
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
2622
+ str = String(str);
2623
+ let output = '';
2624
+ for (let i = 0; i < str.length; i += 3) {
2625
+ const chr1 = str.charCodeAt(i);
2626
+ const chr2 = str.charCodeAt(i + 1);
2627
+ const chr3 = str.charCodeAt(i + 2);
2628
+ const enc1 = chr1 >> 2;
2629
+ const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
2630
+ const enc3 = isNaN(chr2) ? 64 : ((chr2 & 15) << 2) | (chr3 >> 6);
2631
+ const enc4 = isNaN(chr3) ? 64 : chr3 & 63;
2632
+ output += chars.charAt(enc1) + chars.charAt(enc2) + chars.charAt(enc3) + chars.charAt(enc4);
2633
+ }
2634
+ return output;
2635
+ };
2636
+ `;
2637
+ const bootstrapScript = await isolate.compileScript(bootstrapCode);
2638
+ await bootstrapScript.run(context);
2639
+ const wrappedCode = wrapUserCode(code);
2640
+ const finalCode = `
2641
+ (async () => {
2642
+ const __userResult = await (${wrappedCode});
2643
+ // Serializa o resultado para poder transferir
2644
+ if (__userResult === undefined) return '__UNDEFINED__';
2645
+ if (__userResult === null) return 'null';
2646
+ if (typeof __userResult === 'object') {
2647
+ return JSON.stringify({ __type: 'object', value: __userResult });
2648
+ }
2649
+ return JSON.stringify({ __type: typeof __userResult, value: __userResult });
2650
+ })()
2651
+ `;
2652
+ const script = await isolate.compileScript(finalCode);
2653
+ const rawResult = await script.run(context, {
2654
+ timeout: opts.timeout,
2655
+ promise: true
2656
+ });
2657
+ if (rawResult === "__UNDEFINED__") return void 0;
2658
+ if (rawResult === "null") return null;
2659
+ try {
2660
+ const parsed = JSON.parse(rawResult);
2661
+ return parsed.value;
2662
+ } catch {
2663
+ return rawResult;
2664
+ }
2665
+ } catch (error) {
2666
+ if (error.message?.includes("Script execution timed out")) {
2667
+ throw new Error("Tempo de execu\xE7\xE3o excedido (timeout)");
2668
+ }
2669
+ if (error.message?.includes("Isolate was disposed")) {
2670
+ throw new Error("Execu\xE7\xE3o cancelada: limite de mem\xF3ria excedido");
2671
+ }
2672
+ throw new Error(`Erro na execu\xE7\xE3o do c\xF3digo: ${error.message || "Erro desconhecido"}`);
2673
+ } finally {
2674
+ if (context) {
2675
+ try {
2676
+ context.release();
2677
+ } catch {
2678
+ }
2679
+ }
2680
+ if (isolate) {
2681
+ try {
2682
+ isolate.dispose();
2683
+ } catch {
2684
+ }
2685
+ }
2686
+ }
2687
+ }
2688
+ async function injectContextVariables(jail, ctx, ivm) {
2689
+ const safeClone = (value) => {
2690
+ if (value === void 0 || value === null) {
2691
+ return value;
2692
+ }
2693
+ try {
2694
+ if (typeof value !== "object") {
2695
+ return value;
2696
+ }
2697
+ return new ivm.ExternalCopy(value).copyInto();
2698
+ } catch {
2699
+ try {
2700
+ return JSON.parse(JSON.stringify(value));
2701
+ } catch {
2702
+ return void 0;
2703
+ }
2704
+ }
2705
+ };
2706
+ await jail.set("input", safeClone(ctx.input));
2707
+ await jail.set("context", safeClone(ctx.context ?? {}));
2708
+ await jail.set("$inputs", safeClone(ctx.$inputs ?? {}));
2709
+ await jail.set("$vars", safeClone(ctx.$vars ?? {}));
2710
+ await jail.set("$field", safeClone(ctx.$field ?? {}));
2711
+ await jail.set("request", safeClone(ctx.request));
2712
+ await jail.set("params", safeClone(ctx.params));
2713
+ await jail.set("__placeholders", safeClone(ctx.__placeholders ?? {}));
2714
+ }
2715
+ function wrapUserCode(code) {
2716
+ const trimmed = code.trim();
2717
+ if (trimmed.startsWith("return ")) {
2718
+ return `(async function() { ${code} })()`;
2719
+ }
2720
+ const isSimpleExpression = !trimmed.includes("{") && !trimmed.includes("if ") && !trimmed.includes("for ") && !trimmed.includes("while ") && !trimmed.includes("function ") && !trimmed.includes("const ") && !trimmed.includes("let ") && !trimmed.includes("var ");
2721
+ if (isSimpleExpression) {
2722
+ return `(async function() { return ${code} })()`;
2723
+ }
2724
+ return `(async function() { ${code} })()`;
2725
+ }
2726
+ function executeFallback(code, ctx) {
2727
+ console.warn("[Sandbox] Usando execu\xE7\xE3o INSEGURA (new Function). Instale isolated-vm para seguran\xE7a.");
2728
+ const customFunction = new Function(
2729
+ "input",
2730
+ "context",
2731
+ "request",
2732
+ "params",
2733
+ "$inputs",
2734
+ "$vars",
2735
+ "$field",
2736
+ "__placeholders",
2737
+ code
2738
+ );
2739
+ return customFunction(
2740
+ ctx.input,
2741
+ ctx.context,
2742
+ ctx.request,
2743
+ ctx.params,
2744
+ ctx.$inputs,
2745
+ ctx.$vars,
2746
+ ctx.$field,
2747
+ ctx.__placeholders
2748
+ );
2749
+ }
2750
+ function looksLikeCode(code) {
2751
+ if (typeof code !== "string") return false;
2752
+ const c = code.trim();
2753
+ if (c.length < 3) return false;
2754
+ return /(return\s+|=>|function\s*\(|;|\n|\{|\})/.test(c);
2755
+ }
2756
+
2394
2757
  // src/nodes/processors/custom-code.ts
2395
- var NodeFunction = (params) => {
2758
+ var NodeFunction = async (params) => {
2396
2759
  let input = params?.inputValue ?? params?.input;
2397
2760
  const context = params && params.fieldValues ? params.fieldValues : params || {};
2398
2761
  let customCode = context?.customCode ?? params?.customCode;
@@ -2405,12 +2768,6 @@ var NodeFunction = (params) => {
2405
2768
  else if (firstInputField.value !== void 0) input = firstInputField.value;
2406
2769
  }
2407
2770
  }
2408
- const looksLikeCode = (code) => {
2409
- if (typeof code !== "string") return false;
2410
- const c = code.trim();
2411
- if (c.length < 3) return false;
2412
- return /(return\s+|=>|function\s*\(|;|\n|\{|\})/.test(c);
2413
- };
2414
2771
  if (typeof customCode === "string" && !looksLikeCode(customCode)) {
2415
2772
  if (input === void 0) input = customCode;
2416
2773
  customCode = "";
@@ -2418,18 +2775,25 @@ var NodeFunction = (params) => {
2418
2775
  if (!customCode || typeof customCode === "string" && customCode.trim() === "") {
2419
2776
  return input;
2420
2777
  }
2421
- try {
2422
- const $inputs = params?.results || {};
2423
- const $vars = context && context.variables || params?.variables || void 0;
2424
- const __placeholders = params?.__codePlaceholders ?? context?.__codePlaceholders;
2425
- const customFunction = new Function("input", "context", "request", "params", "$inputs", "$vars", "__placeholders", customCode);
2426
- const result = customFunction(input, context, params?.request, params, $inputs, $vars, __placeholders);
2427
- return result;
2428
- } catch (error) {
2429
- throw new Error(`Erro ao executar c\xF3digo customizado: ${error instanceof Error ? error.message : "Erro desconhecido"}`);
2430
- }
2778
+ const sandboxContext = {
2779
+ input,
2780
+ context,
2781
+ $inputs: params?.results || {},
2782
+ $vars: context && context.variables || params?.variables || void 0,
2783
+ request: params?.request,
2784
+ params,
2785
+ __placeholders: params?.__codePlaceholders ?? context?.__codePlaceholders
2786
+ };
2787
+ const result = await executeSandboxed(customCode, sandboxContext, {
2788
+ timeout: 3e4,
2789
+ // 30 segundos
2790
+ memoryLimit: 128,
2791
+ // 128MB
2792
+ allowFetch: true
2793
+ });
2794
+ return result;
2431
2795
  };
2432
- var CustomNodeFunction = (params) => {
2796
+ var CustomNodeFunction = async (params) => {
2433
2797
  const context = params && params.fieldValues ? params.fieldValues : params || {};
2434
2798
  const customCode = context?.customCode;
2435
2799
  if (!customCode || typeof customCode === "string" && customCode.trim() === "") {
@@ -2452,29 +2816,36 @@ var CustomNodeFunction = (params) => {
2452
2816
  }
2453
2817
  $field[fieldId] = value;
2454
2818
  });
2455
- try {
2456
- const $inputs = params?.results || {};
2457
- const $vars = context && context.variables || params?.variables || void 0;
2458
- const customFunction = new Function("$field", "context", "request", "params", "$inputs", "$vars", customCode);
2459
- const result = customFunction($field, context, params?.request, params, $inputs, $vars);
2460
- return result;
2461
- } catch (error) {
2462
- throw new Error(`Erro ao executar CustomNode: ${error instanceof Error ? error.message : "Erro desconhecido"}`);
2463
- }
2819
+ const sandboxContext = {
2820
+ $field,
2821
+ context,
2822
+ $inputs: params?.results || {},
2823
+ $vars: context && context.variables || params?.variables || void 0,
2824
+ request: params?.request,
2825
+ params
2826
+ };
2827
+ const result = await executeSandboxed(customCode, sandboxContext, {
2828
+ timeout: 3e4,
2829
+ // 30 segundos
2830
+ memoryLimit: 128,
2831
+ // 128MB
2832
+ allowFetch: true
2833
+ });
2834
+ return result;
2464
2835
  };
2465
2836
  var CustomCodeNode = {
2466
2837
  label: "Custom Code",
2467
2838
  type: "CustomCodeNode",
2468
2839
  category: "step",
2469
2840
  icon: "\u{1F4BB}",
2470
- description: "Node para executar c\xF3digo JavaScript customizado",
2841
+ description: "Node para executar c\xF3digo JavaScript customizado (sandbox segura)",
2471
2842
  fields: [
2472
2843
  {
2473
2844
  id: "customCode",
2474
2845
  label: "C\xF3digo Customizado",
2475
2846
  type: "code",
2476
2847
  required: false,
2477
- placeholder: '// Seu c\xF3digo JavaScript aqui\n// Use "input" para acessar o valor de entrada\n// Use "context" para acessar os dados do n\xF3\nreturn input;'
2848
+ placeholder: '// Seu c\xF3digo JavaScript aqui\n// Use "input" para acessar o valor de entrada\n// Use "context" para acessar os dados do n\xF3\n// fetch(), JSON, Math, Date est\xE3o dispon\xEDveis\nreturn input;'
2478
2849
  },
2479
2850
  {
2480
2851
  id: "input",
@@ -2751,7 +3122,7 @@ var FunctionGuardrailNode = {
2751
3122
  label: "Fun\xE7\xE3o",
2752
3123
  name: "function",
2753
3124
  fieldType: "function",
2754
- acceptTypes: ["code", "http", "any"],
3125
+ acceptTypes: ["function", "code", "http", "any"],
2755
3126
  maxConnections: 1
2756
3127
  }
2757
3128
  },
package/dist/index.d.cts CHANGED
@@ -183,8 +183,8 @@ declare const nodeFunctions: {
183
183
  AiSupervisorNode: (params: any) => Promise<any>;
184
184
  WhatsappNode: (fieldValues: any) => Promise<any>;
185
185
  WhatsappSendMessageNode: (fieldValues: any) => Promise<any>;
186
- CustomCodeNode: (params: any) => any;
187
- CustomNode: (params: any) => any;
186
+ CustomCodeNode: (params: any) => Promise<any>;
187
+ CustomNode: (params: any) => Promise<any>;
188
188
  PostgresMemoryNode: (inputs: any) => Promise<any>;
189
189
  RedisMemoryNode: (inputs: any) => Promise<any>;
190
190
  PromptGuardrailNode: (inputs: any) => Promise<any>;
package/dist/index.d.ts CHANGED
@@ -183,8 +183,8 @@ declare const nodeFunctions: {
183
183
  AiSupervisorNode: (params: any) => Promise<any>;
184
184
  WhatsappNode: (fieldValues: any) => Promise<any>;
185
185
  WhatsappSendMessageNode: (fieldValues: any) => Promise<any>;
186
- CustomCodeNode: (params: any) => any;
187
- CustomNode: (params: any) => any;
186
+ CustomCodeNode: (params: any) => Promise<any>;
187
+ CustomNode: (params: any) => Promise<any>;
188
188
  PostgresMemoryNode: (inputs: any) => Promise<any>;
189
189
  RedisMemoryNode: (inputs: any) => Promise<any>;
190
190
  PromptGuardrailNode: (inputs: any) => Promise<any>;
package/dist/index.js CHANGED
@@ -1008,7 +1008,7 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
1008
1008
  case "gemini":
1009
1009
  return new ChatGoogle({
1010
1010
  model: "gemini-flash-latest",
1011
- apiKey: "AIzaSyBzrL8Hx6dHhXgwc2HfLlQsf5Y-9pdtc9M",
1011
+ apiKey: "AIzaSyAWj0Al2sVJ8vqaRN4UY7c6imAg7a3NbEc",
1012
1012
  streaming
1013
1013
  });
1014
1014
  case "openai":
@@ -1034,6 +1034,50 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
1034
1034
  }
1035
1035
 
1036
1036
  // src/utils/guardrail-executor.ts
1037
+ async function loadChatHistory(context) {
1038
+ const checkpointer = context?.checkpointer;
1039
+ const sessionId = context?.sessionId;
1040
+ if (!checkpointer || !sessionId) {
1041
+ console.log(`[GUARDRAIL] checkpointer=${!!checkpointer}, sessionId=${sessionId}`);
1042
+ return null;
1043
+ }
1044
+ try {
1045
+ console.log(`[GUARDRAIL] Carregando hist\xF3rico da mem\xF3ria (session: ${sessionId})`);
1046
+ if (typeof checkpointer.setup === "function" && !checkpointer.isSetup) {
1047
+ await checkpointer.setup();
1048
+ }
1049
+ let checkpoint = null;
1050
+ if (typeof checkpointer.getTuple === "function") {
1051
+ const tuple = await checkpointer.getTuple({ configurable: { thread_id: sessionId } });
1052
+ checkpoint = tuple?.checkpoint;
1053
+ console.log(`[GUARDRAIL] getTuple result:`, tuple ? "found" : "null");
1054
+ }
1055
+ if (!checkpoint && typeof checkpointer.get === "function") {
1056
+ checkpoint = await checkpointer.get({ configurable: { thread_id: sessionId } });
1057
+ console.log(`[GUARDRAIL] get result:`, checkpoint ? "found" : "null");
1058
+ }
1059
+ if (!checkpoint) {
1060
+ console.log(`[GUARDRAIL] Nenhum checkpoint encontrado`);
1061
+ return null;
1062
+ }
1063
+ console.log(`[GUARDRAIL] Checkpoint keys:`, Object.keys(checkpoint));
1064
+ const messages = checkpoint?.channel_values?.messages || checkpoint?.values?.messages || checkpoint?.messages || [];
1065
+ if (!messages || messages.length === 0) {
1066
+ console.log(`[GUARDRAIL] Nenhuma mensagem no checkpoint`);
1067
+ return null;
1068
+ }
1069
+ const historyLines = messages.map((m) => {
1070
+ const role = m._getType?.() === "human" || m.type === "human" ? "Usu\xE1rio" : "Assistente";
1071
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
1072
+ return `${role}: ${content.slice(0, 200)}${content.length > 200 ? "..." : ""}`;
1073
+ });
1074
+ console.log(`[GUARDRAIL] Hist\xF3rico carregado: ${messages.length} mensagens`);
1075
+ return historyLines.join("\n");
1076
+ } catch (error) {
1077
+ console.error(`[GUARDRAIL] Erro ao carregar hist\xF3rico:`, error);
1078
+ return null;
1079
+ }
1080
+ }
1037
1081
  async function executeGuardrails(guardrails, content, mode = "fail-first", context = {}) {
1038
1082
  const list = Array.isArray(guardrails) ? guardrails : [guardrails];
1039
1083
  const result = {
@@ -1119,44 +1163,90 @@ async function executeGuardrails(guardrails, content, mode = "fail-first", conte
1119
1163
  }
1120
1164
  } else if (guard.type === "model") {
1121
1165
  const { model, evaluationPrompt, name, actionInstruction } = guard;
1166
+ console.log(`[GUARDRAIL LLM] Iniciando guardrail "${name}"`);
1167
+ console.log(`[GUARDRAIL LLM] Conte\xFAdo a avaliar (${content.length} chars):`, content.slice(0, 200) + (content.length > 200 ? "..." : ""));
1168
+ console.log(`[GUARDRAIL LLM] Action: ${guard.action}, Model dispon\xEDvel: ${!!model}`);
1169
+ const chatHistory = await loadChatHistory(context);
1170
+ if (chatHistory) {
1171
+ console.log(`[GUARDRAIL LLM] Hist\xF3rico inclu\xEDdo no contexto`);
1172
+ }
1122
1173
  if (model && typeof model.invoke === "function") {
1123
1174
  try {
1124
1175
  let prompt = evaluationPrompt;
1176
+ console.log(`[GUARDRAIL LLM] Evaluation prompt base:`, (prompt || "").slice(0, 200));
1125
1177
  if (actionInstruction) {
1126
1178
  prompt += `
1127
1179
 
1128
1180
  INSTRU\xC7\xC3O ADICIONAL DE A\xC7\xC3O:
1129
1181
  ${actionInstruction}`;
1182
+ }
1183
+ if (chatHistory) {
1184
+ prompt += `
1185
+
1186
+ HIST\xD3RICO DA CONVERSA:
1187
+ ${chatHistory}`;
1130
1188
  }
1131
1189
  if (prompt.includes("{{content}}")) {
1132
1190
  prompt = prompt.replace("{{content}}", content);
1133
1191
  } else {
1134
1192
  prompt = `${prompt}
1135
1193
 
1136
- CONTE\xDADO PARA AVALIAR:
1194
+ CONTE\xDADO A AVALIAR (resposta atual):
1137
1195
  ${content}`;
1138
1196
  }
1197
+ console.log(`[GUARDRAIL LLM] Chamando modelo...`);
1139
1198
  const response = await model.invoke(prompt);
1140
1199
  const resultText = typeof response.content === "string" ? response.content : String(response.content);
1200
+ console.log(`[GUARDRAIL LLM] Resposta do modelo:`, resultText.slice(0, 300));
1201
+ const isUnsafe = resultText.toUpperCase().includes("UNSAFE");
1202
+ const isSafe = resultText.toUpperCase().startsWith("SAFE") || resultText.toUpperCase().includes("SAFE") && !isUnsafe;
1141
1203
  if (guard.action === "fix") {
1142
- if (resultText && resultText.trim() !== content.trim() && !resultText.includes("SAFE")) {
1143
- result.modifiedContent = resultText;
1144
- violationMessage = `Conte\xFAdo reescrito pelo guardrail "${name}".`;
1145
- result.violations.push({
1146
- name: guard.name,
1147
- message: violationMessage,
1148
- action: "fix"
1149
- });
1204
+ if (isUnsafe) {
1205
+ console.log(`[GUARDRAIL LLM] Modo FIX - Detectado UNSAFE, chamando modelo para corrigir...`);
1206
+ const fixPrompt = `Voc\xEA \xE9 um corretor de conte\xFAdo. O texto abaixo foi marcado como UNSAFE por violar uma regra.
1207
+
1208
+ REGRA VIOLADA:
1209
+ ${evaluationPrompt}
1210
+
1211
+ ${actionInstruction ? `INSTRU\xC7\xC3O DE CORRE\xC7\xC3O:
1212
+ ${actionInstruction}
1213
+ ` : ""}
1214
+
1215
+ CONTE\xDADO ORIGINAL:
1216
+ ${content}
1217
+
1218
+ Reescreva o conte\xFAdo corrigindo a viola\xE7\xE3o. Retorne APENAS o texto corrigido, sem explica\xE7\xF5es ou prefixos.`;
1219
+ try {
1220
+ const fixResponse = await model.invoke(fixPrompt);
1221
+ const fixedContent = typeof fixResponse.content === "string" ? fixResponse.content : String(fixResponse.content);
1222
+ console.log(`[GUARDRAIL LLM] Conte\xFAdo corrigido:`, fixedContent.slice(0, 300));
1223
+ result.modifiedContent = fixedContent;
1224
+ violationMessage = `Conte\xFAdo reescrito pelo guardrail "${name}".`;
1225
+ result.violations.push({
1226
+ name: guard.name,
1227
+ message: violationMessage,
1228
+ action: "fix"
1229
+ });
1230
+ } catch (fixError) {
1231
+ console.error(`[GUARDRAIL LLM] Erro ao corrigir conte\xFAdo:`, fixError);
1232
+ }
1233
+ } else {
1234
+ console.log(`[GUARDRAIL LLM] Modo FIX - Conte\xFAdo aprovado (SAFE)`);
1150
1235
  }
1151
1236
  } else {
1152
- if (resultText.toUpperCase().includes("UNSAFE")) {
1237
+ if (isUnsafe) {
1153
1238
  guardPassed = false;
1154
1239
  violationMessage = actionInstruction || `Modelo de avalia\xE7\xE3o "${name}" detectou viola\xE7\xE3o de seguran\xE7a/pol\xEDtica.`;
1240
+ console.log(`[GUARDRAIL LLM] Resultado: UNSAFE - Bloqueado`);
1241
+ } else {
1242
+ console.log(`[GUARDRAIL LLM] Resultado: SAFE - Aprovado`);
1155
1243
  }
1156
1244
  }
1157
1245
  } catch (error) {
1158
- console.error(`Error executing model guardrail "${name}":`, error);
1246
+ console.error(`[GUARDRAIL LLM] Erro ao executar guardrail "${name}":`, error);
1159
1247
  }
1248
+ } else {
1249
+ console.log(`[GUARDRAIL LLM] Model n\xE3o dispon\xEDvel ou n\xE3o \xE9 invoc\xE1vel para guardrail "${name}"`);
1160
1250
  }
1161
1251
  }
1162
1252
  if (!guardPassed) {
@@ -1220,7 +1310,7 @@ var IaAgentNodeFunction = async (inputs) => {
1220
1310
  let guardrailViolations = [];
1221
1311
  if (message && beforeGuardrails) {
1222
1312
  const preparedBefore = await prepareGuardrails(beforeGuardrails);
1223
- const context = { ...inputs, input: message };
1313
+ const context = { ...inputs, input: message, checkpointer, sessionId };
1224
1314
  const beforeResults = await executeGuardrails(preparedBefore, message, guardrailMode, context);
1225
1315
  if (beforeResults.blocked) {
1226
1316
  console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Blocked by before-agent guardrail:`, beforeResults.message);
@@ -1422,7 +1512,7 @@ IMPORTANT: You must base your response on the last message in the conversation h
1422
1512
  }
1423
1513
  if (output && afterGuardrails) {
1424
1514
  const preparedAfter = await prepareGuardrails(afterGuardrails);
1425
- const context = { ...inputs, input: output };
1515
+ const context = { ...inputs, input: output, checkpointer, sessionId };
1426
1516
  const afterResults = await executeGuardrails(preparedAfter, output, guardrailMode, context);
1427
1517
  if (afterResults.blocked) {
1428
1518
  console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Response blocked by after-agent guardrail:`, afterResults.message);
@@ -2311,8 +2401,271 @@ var WhatsappMessageTriggerNode = {
2311
2401
  ]
2312
2402
  };
2313
2403
 
2404
+ // src/utils/sandbox-executor.ts
2405
+ var ivmModule = null;
2406
+ async function getIVM() {
2407
+ if (ivmModule !== null) return ivmModule;
2408
+ try {
2409
+ const mod = await import("isolated-vm");
2410
+ ivmModule = {
2411
+ Isolate: mod.Isolate || mod.default?.Isolate,
2412
+ ExternalCopy: mod.ExternalCopy || mod.default?.ExternalCopy,
2413
+ Reference: mod.Reference || mod.default?.Reference
2414
+ };
2415
+ if (!ivmModule.Isolate) {
2416
+ console.warn("[Sandbox] isolated-vm Isolate n\xE3o encontrado");
2417
+ return null;
2418
+ }
2419
+ return ivmModule;
2420
+ } catch (e) {
2421
+ console.warn("[Sandbox] isolated-vm n\xE3o dispon\xEDvel, usando fallback inseguro");
2422
+ return null;
2423
+ }
2424
+ }
2425
+ var DEFAULT_OPTIONS = {
2426
+ timeout: 1e4,
2427
+ memoryLimit: 128,
2428
+ allowFetch: true
2429
+ };
2430
+ async function executeSandboxed(code, sandboxContext, options = {}) {
2431
+ const opts = { ...DEFAULT_OPTIONS, ...options };
2432
+ const ivm = await getIVM();
2433
+ if (!ivm) {
2434
+ return executeFallback(code, sandboxContext);
2435
+ }
2436
+ let isolate = null;
2437
+ let context = null;
2438
+ try {
2439
+ isolate = new ivm.Isolate({ memoryLimit: opts.memoryLimit });
2440
+ context = await isolate.createContext();
2441
+ const jail = context.global;
2442
+ await jail.set("globalThis", jail.derefInto());
2443
+ await injectContextVariables(jail, sandboxContext, ivm);
2444
+ await jail.set("__logRef", new ivm.Reference(function(...args) {
2445
+ console.log("[Sandbox]", ...args);
2446
+ }));
2447
+ if (opts.allowFetch) {
2448
+ await jail.set("__fetchRef", new ivm.Reference(async function(url, optionsJson) {
2449
+ try {
2450
+ const options2 = optionsJson ? JSON.parse(optionsJson) : {};
2451
+ const res = await fetch(url, {
2452
+ method: options2?.method || "GET",
2453
+ headers: options2?.headers,
2454
+ body: options2?.body
2455
+ });
2456
+ const contentType = res.headers.get("content-type") || "";
2457
+ let data;
2458
+ if (contentType.includes("application/json")) {
2459
+ data = await res.json();
2460
+ } else {
2461
+ data = await res.text();
2462
+ }
2463
+ return JSON.stringify({
2464
+ ok: res.ok,
2465
+ status: res.status,
2466
+ statusText: res.statusText,
2467
+ data
2468
+ });
2469
+ } catch (error) {
2470
+ return JSON.stringify({
2471
+ ok: false,
2472
+ status: 0,
2473
+ statusText: "Network Error",
2474
+ error: error.message
2475
+ });
2476
+ }
2477
+ }));
2478
+ }
2479
+ const bootstrapCode = `
2480
+ // Console
2481
+ const console = {
2482
+ log: (...args) => __logRef.applySync(undefined, args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))),
2483
+ warn: (...args) => __logRef.applySync(undefined, ['[WARN]', ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))]),
2484
+ error: (...args) => __logRef.applySync(undefined, ['[ERROR]', ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))]),
2485
+ info: (...args) => __logRef.applySync(undefined, args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))),
2486
+ };
2487
+ globalThis.console = console;
2488
+
2489
+ ${opts.allowFetch ? `
2490
+ // Fetch
2491
+ globalThis.fetch = async (url, options = {}) => {
2492
+ const optionsJson = JSON.stringify(options);
2493
+ const resultJson = await __fetchRef.apply(undefined, [url, optionsJson], { result: { promise: true } });
2494
+ const result = JSON.parse(resultJson);
2495
+
2496
+ if (!result.ok && result.error) {
2497
+ throw new Error(result.error);
2498
+ }
2499
+
2500
+ return {
2501
+ ok: result.ok,
2502
+ status: result.status,
2503
+ statusText: result.statusText,
2504
+ json: async () => result.data,
2505
+ text: async () => typeof result.data === 'string' ? result.data : JSON.stringify(result.data),
2506
+ };
2507
+ };
2508
+ ` : ""}
2509
+
2510
+ // Base64
2511
+ globalThis.atob = (str) => {
2512
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
2513
+ let output = '';
2514
+ str = String(str).replace(/=+$/, '');
2515
+ for (let i = 0; i < str.length; i += 4) {
2516
+ const enc1 = chars.indexOf(str.charAt(i));
2517
+ const enc2 = chars.indexOf(str.charAt(i + 1));
2518
+ const enc3 = chars.indexOf(str.charAt(i + 2));
2519
+ const enc4 = chars.indexOf(str.charAt(i + 3));
2520
+ const chr1 = (enc1 << 2) | (enc2 >> 4);
2521
+ const chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
2522
+ const chr3 = ((enc3 & 3) << 6) | enc4;
2523
+ output += String.fromCharCode(chr1);
2524
+ if (enc3 !== 64) output += String.fromCharCode(chr2);
2525
+ if (enc4 !== 64) output += String.fromCharCode(chr3);
2526
+ }
2527
+ return output;
2528
+ };
2529
+
2530
+ globalThis.btoa = (str) => {
2531
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
2532
+ str = String(str);
2533
+ let output = '';
2534
+ for (let i = 0; i < str.length; i += 3) {
2535
+ const chr1 = str.charCodeAt(i);
2536
+ const chr2 = str.charCodeAt(i + 1);
2537
+ const chr3 = str.charCodeAt(i + 2);
2538
+ const enc1 = chr1 >> 2;
2539
+ const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
2540
+ const enc3 = isNaN(chr2) ? 64 : ((chr2 & 15) << 2) | (chr3 >> 6);
2541
+ const enc4 = isNaN(chr3) ? 64 : chr3 & 63;
2542
+ output += chars.charAt(enc1) + chars.charAt(enc2) + chars.charAt(enc3) + chars.charAt(enc4);
2543
+ }
2544
+ return output;
2545
+ };
2546
+ `;
2547
+ const bootstrapScript = await isolate.compileScript(bootstrapCode);
2548
+ await bootstrapScript.run(context);
2549
+ const wrappedCode = wrapUserCode(code);
2550
+ const finalCode = `
2551
+ (async () => {
2552
+ const __userResult = await (${wrappedCode});
2553
+ // Serializa o resultado para poder transferir
2554
+ if (__userResult === undefined) return '__UNDEFINED__';
2555
+ if (__userResult === null) return 'null';
2556
+ if (typeof __userResult === 'object') {
2557
+ return JSON.stringify({ __type: 'object', value: __userResult });
2558
+ }
2559
+ return JSON.stringify({ __type: typeof __userResult, value: __userResult });
2560
+ })()
2561
+ `;
2562
+ const script = await isolate.compileScript(finalCode);
2563
+ const rawResult = await script.run(context, {
2564
+ timeout: opts.timeout,
2565
+ promise: true
2566
+ });
2567
+ if (rawResult === "__UNDEFINED__") return void 0;
2568
+ if (rawResult === "null") return null;
2569
+ try {
2570
+ const parsed = JSON.parse(rawResult);
2571
+ return parsed.value;
2572
+ } catch {
2573
+ return rawResult;
2574
+ }
2575
+ } catch (error) {
2576
+ if (error.message?.includes("Script execution timed out")) {
2577
+ throw new Error("Tempo de execu\xE7\xE3o excedido (timeout)");
2578
+ }
2579
+ if (error.message?.includes("Isolate was disposed")) {
2580
+ throw new Error("Execu\xE7\xE3o cancelada: limite de mem\xF3ria excedido");
2581
+ }
2582
+ throw new Error(`Erro na execu\xE7\xE3o do c\xF3digo: ${error.message || "Erro desconhecido"}`);
2583
+ } finally {
2584
+ if (context) {
2585
+ try {
2586
+ context.release();
2587
+ } catch {
2588
+ }
2589
+ }
2590
+ if (isolate) {
2591
+ try {
2592
+ isolate.dispose();
2593
+ } catch {
2594
+ }
2595
+ }
2596
+ }
2597
+ }
2598
+ async function injectContextVariables(jail, ctx, ivm) {
2599
+ const safeClone = (value) => {
2600
+ if (value === void 0 || value === null) {
2601
+ return value;
2602
+ }
2603
+ try {
2604
+ if (typeof value !== "object") {
2605
+ return value;
2606
+ }
2607
+ return new ivm.ExternalCopy(value).copyInto();
2608
+ } catch {
2609
+ try {
2610
+ return JSON.parse(JSON.stringify(value));
2611
+ } catch {
2612
+ return void 0;
2613
+ }
2614
+ }
2615
+ };
2616
+ await jail.set("input", safeClone(ctx.input));
2617
+ await jail.set("context", safeClone(ctx.context ?? {}));
2618
+ await jail.set("$inputs", safeClone(ctx.$inputs ?? {}));
2619
+ await jail.set("$vars", safeClone(ctx.$vars ?? {}));
2620
+ await jail.set("$field", safeClone(ctx.$field ?? {}));
2621
+ await jail.set("request", safeClone(ctx.request));
2622
+ await jail.set("params", safeClone(ctx.params));
2623
+ await jail.set("__placeholders", safeClone(ctx.__placeholders ?? {}));
2624
+ }
2625
+ function wrapUserCode(code) {
2626
+ const trimmed = code.trim();
2627
+ if (trimmed.startsWith("return ")) {
2628
+ return `(async function() { ${code} })()`;
2629
+ }
2630
+ const isSimpleExpression = !trimmed.includes("{") && !trimmed.includes("if ") && !trimmed.includes("for ") && !trimmed.includes("while ") && !trimmed.includes("function ") && !trimmed.includes("const ") && !trimmed.includes("let ") && !trimmed.includes("var ");
2631
+ if (isSimpleExpression) {
2632
+ return `(async function() { return ${code} })()`;
2633
+ }
2634
+ return `(async function() { ${code} })()`;
2635
+ }
2636
+ function executeFallback(code, ctx) {
2637
+ console.warn("[Sandbox] Usando execu\xE7\xE3o INSEGURA (new Function). Instale isolated-vm para seguran\xE7a.");
2638
+ const customFunction = new Function(
2639
+ "input",
2640
+ "context",
2641
+ "request",
2642
+ "params",
2643
+ "$inputs",
2644
+ "$vars",
2645
+ "$field",
2646
+ "__placeholders",
2647
+ code
2648
+ );
2649
+ return customFunction(
2650
+ ctx.input,
2651
+ ctx.context,
2652
+ ctx.request,
2653
+ ctx.params,
2654
+ ctx.$inputs,
2655
+ ctx.$vars,
2656
+ ctx.$field,
2657
+ ctx.__placeholders
2658
+ );
2659
+ }
2660
+ function looksLikeCode(code) {
2661
+ if (typeof code !== "string") return false;
2662
+ const c = code.trim();
2663
+ if (c.length < 3) return false;
2664
+ return /(return\s+|=>|function\s*\(|;|\n|\{|\})/.test(c);
2665
+ }
2666
+
2314
2667
  // src/nodes/processors/custom-code.ts
2315
- var NodeFunction = (params) => {
2668
+ var NodeFunction = async (params) => {
2316
2669
  let input = params?.inputValue ?? params?.input;
2317
2670
  const context = params && params.fieldValues ? params.fieldValues : params || {};
2318
2671
  let customCode = context?.customCode ?? params?.customCode;
@@ -2325,12 +2678,6 @@ var NodeFunction = (params) => {
2325
2678
  else if (firstInputField.value !== void 0) input = firstInputField.value;
2326
2679
  }
2327
2680
  }
2328
- const looksLikeCode = (code) => {
2329
- if (typeof code !== "string") return false;
2330
- const c = code.trim();
2331
- if (c.length < 3) return false;
2332
- return /(return\s+|=>|function\s*\(|;|\n|\{|\})/.test(c);
2333
- };
2334
2681
  if (typeof customCode === "string" && !looksLikeCode(customCode)) {
2335
2682
  if (input === void 0) input = customCode;
2336
2683
  customCode = "";
@@ -2338,18 +2685,25 @@ var NodeFunction = (params) => {
2338
2685
  if (!customCode || typeof customCode === "string" && customCode.trim() === "") {
2339
2686
  return input;
2340
2687
  }
2341
- try {
2342
- const $inputs = params?.results || {};
2343
- const $vars = context && context.variables || params?.variables || void 0;
2344
- const __placeholders = params?.__codePlaceholders ?? context?.__codePlaceholders;
2345
- const customFunction = new Function("input", "context", "request", "params", "$inputs", "$vars", "__placeholders", customCode);
2346
- const result = customFunction(input, context, params?.request, params, $inputs, $vars, __placeholders);
2347
- return result;
2348
- } catch (error) {
2349
- throw new Error(`Erro ao executar c\xF3digo customizado: ${error instanceof Error ? error.message : "Erro desconhecido"}`);
2350
- }
2688
+ const sandboxContext = {
2689
+ input,
2690
+ context,
2691
+ $inputs: params?.results || {},
2692
+ $vars: context && context.variables || params?.variables || void 0,
2693
+ request: params?.request,
2694
+ params,
2695
+ __placeholders: params?.__codePlaceholders ?? context?.__codePlaceholders
2696
+ };
2697
+ const result = await executeSandboxed(customCode, sandboxContext, {
2698
+ timeout: 3e4,
2699
+ // 30 segundos
2700
+ memoryLimit: 128,
2701
+ // 128MB
2702
+ allowFetch: true
2703
+ });
2704
+ return result;
2351
2705
  };
2352
- var CustomNodeFunction = (params) => {
2706
+ var CustomNodeFunction = async (params) => {
2353
2707
  const context = params && params.fieldValues ? params.fieldValues : params || {};
2354
2708
  const customCode = context?.customCode;
2355
2709
  if (!customCode || typeof customCode === "string" && customCode.trim() === "") {
@@ -2372,29 +2726,36 @@ var CustomNodeFunction = (params) => {
2372
2726
  }
2373
2727
  $field[fieldId] = value;
2374
2728
  });
2375
- try {
2376
- const $inputs = params?.results || {};
2377
- const $vars = context && context.variables || params?.variables || void 0;
2378
- const customFunction = new Function("$field", "context", "request", "params", "$inputs", "$vars", customCode);
2379
- const result = customFunction($field, context, params?.request, params, $inputs, $vars);
2380
- return result;
2381
- } catch (error) {
2382
- throw new Error(`Erro ao executar CustomNode: ${error instanceof Error ? error.message : "Erro desconhecido"}`);
2383
- }
2729
+ const sandboxContext = {
2730
+ $field,
2731
+ context,
2732
+ $inputs: params?.results || {},
2733
+ $vars: context && context.variables || params?.variables || void 0,
2734
+ request: params?.request,
2735
+ params
2736
+ };
2737
+ const result = await executeSandboxed(customCode, sandboxContext, {
2738
+ timeout: 3e4,
2739
+ // 30 segundos
2740
+ memoryLimit: 128,
2741
+ // 128MB
2742
+ allowFetch: true
2743
+ });
2744
+ return result;
2384
2745
  };
2385
2746
  var CustomCodeNode = {
2386
2747
  label: "Custom Code",
2387
2748
  type: "CustomCodeNode",
2388
2749
  category: "step",
2389
2750
  icon: "\u{1F4BB}",
2390
- description: "Node para executar c\xF3digo JavaScript customizado",
2751
+ description: "Node para executar c\xF3digo JavaScript customizado (sandbox segura)",
2391
2752
  fields: [
2392
2753
  {
2393
2754
  id: "customCode",
2394
2755
  label: "C\xF3digo Customizado",
2395
2756
  type: "code",
2396
2757
  required: false,
2397
- placeholder: '// Seu c\xF3digo JavaScript aqui\n// Use "input" para acessar o valor de entrada\n// Use "context" para acessar os dados do n\xF3\nreturn input;'
2758
+ placeholder: '// Seu c\xF3digo JavaScript aqui\n// Use "input" para acessar o valor de entrada\n// Use "context" para acessar os dados do n\xF3\n// fetch(), JSON, Math, Date est\xE3o dispon\xEDveis\nreturn input;'
2398
2759
  },
2399
2760
  {
2400
2761
  id: "input",
@@ -2671,7 +3032,7 @@ var FunctionGuardrailNode = {
2671
3032
  label: "Fun\xE7\xE3o",
2672
3033
  name: "function",
2673
3034
  fieldType: "function",
2674
- acceptTypes: ["code", "http", "any"],
3035
+ acceptTypes: ["function", "code", "http", "any"],
2675
3036
  maxConnections: 1
2676
3037
  }
2677
3038
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomoz/workflows-nodes",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "Atomoz Workflows - Node Library",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -40,6 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@langchain/core": "^0.3.66",
43
+ "isolated-vm": "^6.0.2",
43
44
  "@langchain/google-gauth": "^0.2.16",
44
45
  "@langchain/langgraph": "^0.4.3",
45
46
  "@langchain/langgraph-checkpoint-postgres": "^1.0.0",