@atomoz/workflows-nodes 0.1.26 → 0.1.28
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 +792 -59
- package/dist/index.d.cts +42 -4
- package/dist/index.d.ts +42 -4
- package/dist/index.js +776 -59
- package/package.json +2 -1
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
|
|
@@ -26,7 +36,13 @@ __export(index_exports, {
|
|
|
26
36
|
AiToolNodeFunction: () => AiToolNodeFunction,
|
|
27
37
|
AiToolNodeSchema: () => AiToolNodeSchema,
|
|
28
38
|
BodyFieldSchema: () => BodyFieldSchema,
|
|
39
|
+
ChatLogNode: () => ChatLogNode,
|
|
40
|
+
ChatLogNodeFunction: () => ChatLogNodeFunction,
|
|
41
|
+
ChatLogSchema: () => ChatLogSchema,
|
|
29
42
|
CustomToolSchema: () => CustomToolSchema,
|
|
43
|
+
GetOrCreateThreadNode: () => GetOrCreateThreadNode,
|
|
44
|
+
GetOrCreateThreadNodeFunction: () => GetOrCreateThreadNodeFunction,
|
|
45
|
+
GetOrCreateThreadSchema: () => GetOrCreateThreadSchema,
|
|
30
46
|
HTTP_METHODS: () => HTTP_METHODS,
|
|
31
47
|
HTTP_NODE_TYPES: () => HTTP_NODE_TYPES,
|
|
32
48
|
HeaderSchema: () => HeaderSchema,
|
|
@@ -1088,7 +1104,7 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
|
|
|
1088
1104
|
case "gemini":
|
|
1089
1105
|
return new import_google_gauth.ChatGoogle({
|
|
1090
1106
|
model: "gemini-flash-latest",
|
|
1091
|
-
apiKey: "
|
|
1107
|
+
apiKey: "AIzaSyAWj0Al2sVJ8vqaRN4UY7c6imAg7a3NbEc",
|
|
1092
1108
|
streaming
|
|
1093
1109
|
});
|
|
1094
1110
|
case "openai":
|
|
@@ -1114,6 +1130,62 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
|
|
|
1114
1130
|
}
|
|
1115
1131
|
|
|
1116
1132
|
// src/utils/guardrail-executor.ts
|
|
1133
|
+
function emitGuardrailEvent(context, name, passed, action, details) {
|
|
1134
|
+
if (context?.emitter?.emitThought) {
|
|
1135
|
+
context.emitter.emitThought({
|
|
1136
|
+
content: `guardrail:${passed ? "passed" : "failed"}:${name}`,
|
|
1137
|
+
type: "guardrail",
|
|
1138
|
+
name,
|
|
1139
|
+
passed,
|
|
1140
|
+
action: action || "evaluate",
|
|
1141
|
+
details
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
async function loadChatHistory(context) {
|
|
1146
|
+
const checkpointer = context?.checkpointer;
|
|
1147
|
+
const sessionId = context?.sessionId;
|
|
1148
|
+
if (!checkpointer || !sessionId) {
|
|
1149
|
+
console.log(`[GUARDRAIL] checkpointer=${!!checkpointer}, sessionId=${sessionId}`);
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
try {
|
|
1153
|
+
console.log(`[GUARDRAIL] Carregando hist\xF3rico da mem\xF3ria (session: ${sessionId})`);
|
|
1154
|
+
if (typeof checkpointer.setup === "function" && !checkpointer.isSetup) {
|
|
1155
|
+
await checkpointer.setup();
|
|
1156
|
+
}
|
|
1157
|
+
let checkpoint = null;
|
|
1158
|
+
if (typeof checkpointer.getTuple === "function") {
|
|
1159
|
+
const tuple = await checkpointer.getTuple({ configurable: { thread_id: sessionId } });
|
|
1160
|
+
checkpoint = tuple?.checkpoint;
|
|
1161
|
+
console.log(`[GUARDRAIL] getTuple result:`, tuple ? "found" : "null");
|
|
1162
|
+
}
|
|
1163
|
+
if (!checkpoint && typeof checkpointer.get === "function") {
|
|
1164
|
+
checkpoint = await checkpointer.get({ configurable: { thread_id: sessionId } });
|
|
1165
|
+
console.log(`[GUARDRAIL] get result:`, checkpoint ? "found" : "null");
|
|
1166
|
+
}
|
|
1167
|
+
if (!checkpoint) {
|
|
1168
|
+
console.log(`[GUARDRAIL] Nenhum checkpoint encontrado`);
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
console.log(`[GUARDRAIL] Checkpoint keys:`, Object.keys(checkpoint));
|
|
1172
|
+
const messages = checkpoint?.channel_values?.messages || checkpoint?.values?.messages || checkpoint?.messages || [];
|
|
1173
|
+
if (!messages || messages.length === 0) {
|
|
1174
|
+
console.log(`[GUARDRAIL] Nenhuma mensagem no checkpoint`);
|
|
1175
|
+
return null;
|
|
1176
|
+
}
|
|
1177
|
+
const historyLines = messages.map((m) => {
|
|
1178
|
+
const role = m._getType?.() === "human" || m.type === "human" ? "Usu\xE1rio" : "Assistente";
|
|
1179
|
+
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
1180
|
+
return `${role}: ${content.slice(0, 200)}${content.length > 200 ? "..." : ""}`;
|
|
1181
|
+
});
|
|
1182
|
+
console.log(`[GUARDRAIL] Hist\xF3rico carregado: ${messages.length} mensagens`);
|
|
1183
|
+
return historyLines.join("\n");
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
console.error(`[GUARDRAIL] Erro ao carregar hist\xF3rico:`, error);
|
|
1186
|
+
return null;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1117
1189
|
async function executeGuardrails(guardrails, content, mode = "fail-first", context = {}) {
|
|
1118
1190
|
const list = Array.isArray(guardrails) ? guardrails : [guardrails];
|
|
1119
1191
|
const result = {
|
|
@@ -1164,6 +1236,7 @@ async function executeGuardrails(guardrails, content, mode = "fail-first", conte
|
|
|
1164
1236
|
if (!guardPassed && actionInstruction) {
|
|
1165
1237
|
violationMessage = actionInstruction;
|
|
1166
1238
|
}
|
|
1239
|
+
emitGuardrailEvent(context, guard.name, guardPassed, guard.action, violationMessage || "Rule passed");
|
|
1167
1240
|
} else if (guard.type === "function") {
|
|
1168
1241
|
const { nodeFunction, name, actionInstruction } = guard;
|
|
1169
1242
|
if (typeof nodeFunction === "function") {
|
|
@@ -1196,47 +1269,97 @@ async function executeGuardrails(guardrails, content, mode = "fail-first", conte
|
|
|
1196
1269
|
guardPassed = false;
|
|
1197
1270
|
violationMessage = `Erro na fun\xE7\xE3o guardrail: ${error instanceof Error ? error.message : String(error)}`;
|
|
1198
1271
|
}
|
|
1272
|
+
emitGuardrailEvent(context, name, guardPassed, guard.action, violationMessage || "Function passed");
|
|
1199
1273
|
}
|
|
1200
1274
|
} else if (guard.type === "model") {
|
|
1201
1275
|
const { model, evaluationPrompt, name, actionInstruction } = guard;
|
|
1276
|
+
console.log(`[GUARDRAIL LLM] Iniciando guardrail "${name}"`);
|
|
1277
|
+
console.log(`[GUARDRAIL LLM] Conte\xFAdo a avaliar (${content.length} chars):`, content.slice(0, 200) + (content.length > 200 ? "..." : ""));
|
|
1278
|
+
console.log(`[GUARDRAIL LLM] Action: ${guard.action}, Model dispon\xEDvel: ${!!model}`);
|
|
1279
|
+
const chatHistory = await loadChatHistory(context);
|
|
1280
|
+
if (chatHistory) {
|
|
1281
|
+
console.log(`[GUARDRAIL LLM] Hist\xF3rico inclu\xEDdo no contexto`);
|
|
1282
|
+
}
|
|
1202
1283
|
if (model && typeof model.invoke === "function") {
|
|
1203
1284
|
try {
|
|
1204
1285
|
let prompt = evaluationPrompt;
|
|
1286
|
+
console.log(`[GUARDRAIL LLM] Evaluation prompt base:`, (prompt || "").slice(0, 200));
|
|
1205
1287
|
if (actionInstruction) {
|
|
1206
1288
|
prompt += `
|
|
1207
1289
|
|
|
1208
1290
|
INSTRU\xC7\xC3O ADICIONAL DE A\xC7\xC3O:
|
|
1209
1291
|
${actionInstruction}`;
|
|
1292
|
+
}
|
|
1293
|
+
if (chatHistory) {
|
|
1294
|
+
prompt += `
|
|
1295
|
+
|
|
1296
|
+
HIST\xD3RICO DA CONVERSA:
|
|
1297
|
+
${chatHistory}`;
|
|
1210
1298
|
}
|
|
1211
1299
|
if (prompt.includes("{{content}}")) {
|
|
1212
1300
|
prompt = prompt.replace("{{content}}", content);
|
|
1213
1301
|
} else {
|
|
1214
1302
|
prompt = `${prompt}
|
|
1215
1303
|
|
|
1216
|
-
CONTE\xDADO
|
|
1304
|
+
CONTE\xDADO A AVALIAR (resposta atual):
|
|
1217
1305
|
${content}`;
|
|
1218
1306
|
}
|
|
1307
|
+
console.log(`[GUARDRAIL LLM] Chamando modelo...`);
|
|
1219
1308
|
const response = await model.invoke(prompt);
|
|
1220
1309
|
const resultText = typeof response.content === "string" ? response.content : String(response.content);
|
|
1310
|
+
console.log(`[GUARDRAIL LLM] Resposta do modelo:`, resultText.slice(0, 300));
|
|
1311
|
+
const isUnsafe = resultText.toUpperCase().includes("UNSAFE");
|
|
1312
|
+
const isSafe = resultText.toUpperCase().startsWith("SAFE") || resultText.toUpperCase().includes("SAFE") && !isUnsafe;
|
|
1221
1313
|
if (guard.action === "fix") {
|
|
1222
|
-
if (
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1314
|
+
if (isUnsafe) {
|
|
1315
|
+
console.log(`[GUARDRAIL LLM] Modo FIX - Detectado UNSAFE, chamando modelo para corrigir...`);
|
|
1316
|
+
const fixPrompt = `Voc\xEA \xE9 um corretor de conte\xFAdo. O texto abaixo foi marcado como UNSAFE por violar uma regra.
|
|
1317
|
+
|
|
1318
|
+
REGRA VIOLADA:
|
|
1319
|
+
${evaluationPrompt}
|
|
1320
|
+
|
|
1321
|
+
${actionInstruction ? `INSTRU\xC7\xC3O DE CORRE\xC7\xC3O:
|
|
1322
|
+
${actionInstruction}
|
|
1323
|
+
` : ""}
|
|
1324
|
+
|
|
1325
|
+
CONTE\xDADO ORIGINAL:
|
|
1326
|
+
${content}
|
|
1327
|
+
|
|
1328
|
+
Reescreva o conte\xFAdo corrigindo a viola\xE7\xE3o. Retorne APENAS o texto corrigido, sem explica\xE7\xF5es ou prefixos.`;
|
|
1329
|
+
try {
|
|
1330
|
+
const fixResponse = await model.invoke(fixPrompt);
|
|
1331
|
+
const fixedContent = typeof fixResponse.content === "string" ? fixResponse.content : String(fixResponse.content);
|
|
1332
|
+
console.log(`[GUARDRAIL LLM] Conte\xFAdo corrigido:`, fixedContent.slice(0, 300));
|
|
1333
|
+
result.modifiedContent = fixedContent;
|
|
1334
|
+
violationMessage = `Conte\xFAdo reescrito pelo guardrail "${name}".`;
|
|
1335
|
+
result.violations.push({
|
|
1336
|
+
name: guard.name,
|
|
1337
|
+
message: violationMessage,
|
|
1338
|
+
action: "fix"
|
|
1339
|
+
});
|
|
1340
|
+
emitGuardrailEvent(context, name, false, "fix", `Content fixed: ${fixedContent.slice(0, 100)}...`);
|
|
1341
|
+
} catch (fixError) {
|
|
1342
|
+
console.error(`[GUARDRAIL LLM] Erro ao corrigir conte\xFAdo:`, fixError);
|
|
1343
|
+
}
|
|
1344
|
+
} else {
|
|
1345
|
+
console.log(`[GUARDRAIL LLM] Modo FIX - Conte\xFAdo aprovado (SAFE)`);
|
|
1230
1346
|
}
|
|
1231
1347
|
} else {
|
|
1232
|
-
if (
|
|
1348
|
+
if (isUnsafe) {
|
|
1233
1349
|
guardPassed = false;
|
|
1234
1350
|
violationMessage = actionInstruction || `Modelo de avalia\xE7\xE3o "${name}" detectou viola\xE7\xE3o de seguran\xE7a/pol\xEDtica.`;
|
|
1351
|
+
console.log(`[GUARDRAIL LLM] Resultado: UNSAFE - Bloqueado`);
|
|
1352
|
+
emitGuardrailEvent(context, name, false, guard.action, violationMessage);
|
|
1353
|
+
} else {
|
|
1354
|
+
console.log(`[GUARDRAIL LLM] Resultado: SAFE - Aprovado`);
|
|
1355
|
+
emitGuardrailEvent(context, name, true, "approve", "Content passed evaluation");
|
|
1235
1356
|
}
|
|
1236
1357
|
}
|
|
1237
1358
|
} catch (error) {
|
|
1238
|
-
console.error(`
|
|
1359
|
+
console.error(`[GUARDRAIL LLM] Erro ao executar guardrail "${name}":`, error);
|
|
1239
1360
|
}
|
|
1361
|
+
} else {
|
|
1362
|
+
console.log(`[GUARDRAIL LLM] Model n\xE3o dispon\xEDvel ou n\xE3o \xE9 invoc\xE1vel para guardrail "${name}"`);
|
|
1240
1363
|
}
|
|
1241
1364
|
}
|
|
1242
1365
|
if (!guardPassed) {
|
|
@@ -1300,7 +1423,7 @@ var IaAgentNodeFunction = async (inputs) => {
|
|
|
1300
1423
|
let guardrailViolations = [];
|
|
1301
1424
|
if (message && beforeGuardrails) {
|
|
1302
1425
|
const preparedBefore = await prepareGuardrails(beforeGuardrails);
|
|
1303
|
-
const context = { ...inputs, input: message };
|
|
1426
|
+
const context = { ...inputs, input: message, checkpointer, sessionId };
|
|
1304
1427
|
const beforeResults = await executeGuardrails(preparedBefore, message, guardrailMode, context);
|
|
1305
1428
|
if (beforeResults.blocked) {
|
|
1306
1429
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Blocked by before-agent guardrail:`, beforeResults.message);
|
|
@@ -1502,7 +1625,7 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1502
1625
|
}
|
|
1503
1626
|
if (output && afterGuardrails) {
|
|
1504
1627
|
const preparedAfter = await prepareGuardrails(afterGuardrails);
|
|
1505
|
-
const context = { ...inputs, input: output };
|
|
1628
|
+
const context = { ...inputs, input: output, checkpointer, sessionId };
|
|
1506
1629
|
const afterResults = await executeGuardrails(preparedAfter, output, guardrailMode, context);
|
|
1507
1630
|
if (afterResults.blocked) {
|
|
1508
1631
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Response blocked by after-agent guardrail:`, afterResults.message);
|
|
@@ -2391,8 +2514,271 @@ var WhatsappMessageTriggerNode = {
|
|
|
2391
2514
|
]
|
|
2392
2515
|
};
|
|
2393
2516
|
|
|
2517
|
+
// src/utils/sandbox-executor.ts
|
|
2518
|
+
var ivmModule = null;
|
|
2519
|
+
async function getIVM() {
|
|
2520
|
+
if (ivmModule !== null) return ivmModule;
|
|
2521
|
+
try {
|
|
2522
|
+
const mod = await import("isolated-vm");
|
|
2523
|
+
ivmModule = {
|
|
2524
|
+
Isolate: mod.Isolate || mod.default?.Isolate,
|
|
2525
|
+
ExternalCopy: mod.ExternalCopy || mod.default?.ExternalCopy,
|
|
2526
|
+
Reference: mod.Reference || mod.default?.Reference
|
|
2527
|
+
};
|
|
2528
|
+
if (!ivmModule.Isolate) {
|
|
2529
|
+
console.warn("[Sandbox] isolated-vm Isolate n\xE3o encontrado");
|
|
2530
|
+
return null;
|
|
2531
|
+
}
|
|
2532
|
+
return ivmModule;
|
|
2533
|
+
} catch (e) {
|
|
2534
|
+
console.warn("[Sandbox] isolated-vm n\xE3o dispon\xEDvel, usando fallback inseguro");
|
|
2535
|
+
return null;
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
var DEFAULT_OPTIONS = {
|
|
2539
|
+
timeout: 1e4,
|
|
2540
|
+
memoryLimit: 128,
|
|
2541
|
+
allowFetch: true
|
|
2542
|
+
};
|
|
2543
|
+
async function executeSandboxed(code, sandboxContext, options = {}) {
|
|
2544
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2545
|
+
const ivm = await getIVM();
|
|
2546
|
+
if (!ivm) {
|
|
2547
|
+
return executeFallback(code, sandboxContext);
|
|
2548
|
+
}
|
|
2549
|
+
let isolate = null;
|
|
2550
|
+
let context = null;
|
|
2551
|
+
try {
|
|
2552
|
+
isolate = new ivm.Isolate({ memoryLimit: opts.memoryLimit });
|
|
2553
|
+
context = await isolate.createContext();
|
|
2554
|
+
const jail = context.global;
|
|
2555
|
+
await jail.set("globalThis", jail.derefInto());
|
|
2556
|
+
await injectContextVariables(jail, sandboxContext, ivm);
|
|
2557
|
+
await jail.set("__logRef", new ivm.Reference(function(...args) {
|
|
2558
|
+
console.log("[Sandbox]", ...args);
|
|
2559
|
+
}));
|
|
2560
|
+
if (opts.allowFetch) {
|
|
2561
|
+
await jail.set("__fetchRef", new ivm.Reference(async function(url, optionsJson) {
|
|
2562
|
+
try {
|
|
2563
|
+
const options2 = optionsJson ? JSON.parse(optionsJson) : {};
|
|
2564
|
+
const res = await fetch(url, {
|
|
2565
|
+
method: options2?.method || "GET",
|
|
2566
|
+
headers: options2?.headers,
|
|
2567
|
+
body: options2?.body
|
|
2568
|
+
});
|
|
2569
|
+
const contentType = res.headers.get("content-type") || "";
|
|
2570
|
+
let data;
|
|
2571
|
+
if (contentType.includes("application/json")) {
|
|
2572
|
+
data = await res.json();
|
|
2573
|
+
} else {
|
|
2574
|
+
data = await res.text();
|
|
2575
|
+
}
|
|
2576
|
+
return JSON.stringify({
|
|
2577
|
+
ok: res.ok,
|
|
2578
|
+
status: res.status,
|
|
2579
|
+
statusText: res.statusText,
|
|
2580
|
+
data
|
|
2581
|
+
});
|
|
2582
|
+
} catch (error) {
|
|
2583
|
+
return JSON.stringify({
|
|
2584
|
+
ok: false,
|
|
2585
|
+
status: 0,
|
|
2586
|
+
statusText: "Network Error",
|
|
2587
|
+
error: error.message
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2590
|
+
}));
|
|
2591
|
+
}
|
|
2592
|
+
const bootstrapCode = `
|
|
2593
|
+
// Console
|
|
2594
|
+
const console = {
|
|
2595
|
+
log: (...args) => __logRef.applySync(undefined, args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))),
|
|
2596
|
+
warn: (...args) => __logRef.applySync(undefined, ['[WARN]', ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))]),
|
|
2597
|
+
error: (...args) => __logRef.applySync(undefined, ['[ERROR]', ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))]),
|
|
2598
|
+
info: (...args) => __logRef.applySync(undefined, args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a))),
|
|
2599
|
+
};
|
|
2600
|
+
globalThis.console = console;
|
|
2601
|
+
|
|
2602
|
+
${opts.allowFetch ? `
|
|
2603
|
+
// Fetch
|
|
2604
|
+
globalThis.fetch = async (url, options = {}) => {
|
|
2605
|
+
const optionsJson = JSON.stringify(options);
|
|
2606
|
+
const resultJson = await __fetchRef.apply(undefined, [url, optionsJson], { result: { promise: true } });
|
|
2607
|
+
const result = JSON.parse(resultJson);
|
|
2608
|
+
|
|
2609
|
+
if (!result.ok && result.error) {
|
|
2610
|
+
throw new Error(result.error);
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
return {
|
|
2614
|
+
ok: result.ok,
|
|
2615
|
+
status: result.status,
|
|
2616
|
+
statusText: result.statusText,
|
|
2617
|
+
json: async () => result.data,
|
|
2618
|
+
text: async () => typeof result.data === 'string' ? result.data : JSON.stringify(result.data),
|
|
2619
|
+
};
|
|
2620
|
+
};
|
|
2621
|
+
` : ""}
|
|
2622
|
+
|
|
2623
|
+
// Base64
|
|
2624
|
+
globalThis.atob = (str) => {
|
|
2625
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
2626
|
+
let output = '';
|
|
2627
|
+
str = String(str).replace(/=+$/, '');
|
|
2628
|
+
for (let i = 0; i < str.length; i += 4) {
|
|
2629
|
+
const enc1 = chars.indexOf(str.charAt(i));
|
|
2630
|
+
const enc2 = chars.indexOf(str.charAt(i + 1));
|
|
2631
|
+
const enc3 = chars.indexOf(str.charAt(i + 2));
|
|
2632
|
+
const enc4 = chars.indexOf(str.charAt(i + 3));
|
|
2633
|
+
const chr1 = (enc1 << 2) | (enc2 >> 4);
|
|
2634
|
+
const chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
|
2635
|
+
const chr3 = ((enc3 & 3) << 6) | enc4;
|
|
2636
|
+
output += String.fromCharCode(chr1);
|
|
2637
|
+
if (enc3 !== 64) output += String.fromCharCode(chr2);
|
|
2638
|
+
if (enc4 !== 64) output += String.fromCharCode(chr3);
|
|
2639
|
+
}
|
|
2640
|
+
return output;
|
|
2641
|
+
};
|
|
2642
|
+
|
|
2643
|
+
globalThis.btoa = (str) => {
|
|
2644
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
2645
|
+
str = String(str);
|
|
2646
|
+
let output = '';
|
|
2647
|
+
for (let i = 0; i < str.length; i += 3) {
|
|
2648
|
+
const chr1 = str.charCodeAt(i);
|
|
2649
|
+
const chr2 = str.charCodeAt(i + 1);
|
|
2650
|
+
const chr3 = str.charCodeAt(i + 2);
|
|
2651
|
+
const enc1 = chr1 >> 2;
|
|
2652
|
+
const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
|
2653
|
+
const enc3 = isNaN(chr2) ? 64 : ((chr2 & 15) << 2) | (chr3 >> 6);
|
|
2654
|
+
const enc4 = isNaN(chr3) ? 64 : chr3 & 63;
|
|
2655
|
+
output += chars.charAt(enc1) + chars.charAt(enc2) + chars.charAt(enc3) + chars.charAt(enc4);
|
|
2656
|
+
}
|
|
2657
|
+
return output;
|
|
2658
|
+
};
|
|
2659
|
+
`;
|
|
2660
|
+
const bootstrapScript = await isolate.compileScript(bootstrapCode);
|
|
2661
|
+
await bootstrapScript.run(context);
|
|
2662
|
+
const wrappedCode = wrapUserCode(code);
|
|
2663
|
+
const finalCode = `
|
|
2664
|
+
(async () => {
|
|
2665
|
+
const __userResult = await (${wrappedCode});
|
|
2666
|
+
// Serializa o resultado para poder transferir
|
|
2667
|
+
if (__userResult === undefined) return '__UNDEFINED__';
|
|
2668
|
+
if (__userResult === null) return 'null';
|
|
2669
|
+
if (typeof __userResult === 'object') {
|
|
2670
|
+
return JSON.stringify({ __type: 'object', value: __userResult });
|
|
2671
|
+
}
|
|
2672
|
+
return JSON.stringify({ __type: typeof __userResult, value: __userResult });
|
|
2673
|
+
})()
|
|
2674
|
+
`;
|
|
2675
|
+
const script = await isolate.compileScript(finalCode);
|
|
2676
|
+
const rawResult = await script.run(context, {
|
|
2677
|
+
timeout: opts.timeout,
|
|
2678
|
+
promise: true
|
|
2679
|
+
});
|
|
2680
|
+
if (rawResult === "__UNDEFINED__") return void 0;
|
|
2681
|
+
if (rawResult === "null") return null;
|
|
2682
|
+
try {
|
|
2683
|
+
const parsed = JSON.parse(rawResult);
|
|
2684
|
+
return parsed.value;
|
|
2685
|
+
} catch {
|
|
2686
|
+
return rawResult;
|
|
2687
|
+
}
|
|
2688
|
+
} catch (error) {
|
|
2689
|
+
if (error.message?.includes("Script execution timed out")) {
|
|
2690
|
+
throw new Error("Tempo de execu\xE7\xE3o excedido (timeout)");
|
|
2691
|
+
}
|
|
2692
|
+
if (error.message?.includes("Isolate was disposed")) {
|
|
2693
|
+
throw new Error("Execu\xE7\xE3o cancelada: limite de mem\xF3ria excedido");
|
|
2694
|
+
}
|
|
2695
|
+
throw new Error(`Erro na execu\xE7\xE3o do c\xF3digo: ${error.message || "Erro desconhecido"}`);
|
|
2696
|
+
} finally {
|
|
2697
|
+
if (context) {
|
|
2698
|
+
try {
|
|
2699
|
+
context.release();
|
|
2700
|
+
} catch {
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
if (isolate) {
|
|
2704
|
+
try {
|
|
2705
|
+
isolate.dispose();
|
|
2706
|
+
} catch {
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
async function injectContextVariables(jail, ctx, ivm) {
|
|
2712
|
+
const safeClone = (value) => {
|
|
2713
|
+
if (value === void 0 || value === null) {
|
|
2714
|
+
return value;
|
|
2715
|
+
}
|
|
2716
|
+
try {
|
|
2717
|
+
if (typeof value !== "object") {
|
|
2718
|
+
return value;
|
|
2719
|
+
}
|
|
2720
|
+
return new ivm.ExternalCopy(value).copyInto();
|
|
2721
|
+
} catch {
|
|
2722
|
+
try {
|
|
2723
|
+
return JSON.parse(JSON.stringify(value));
|
|
2724
|
+
} catch {
|
|
2725
|
+
return void 0;
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
};
|
|
2729
|
+
await jail.set("input", safeClone(ctx.input));
|
|
2730
|
+
await jail.set("context", safeClone(ctx.context ?? {}));
|
|
2731
|
+
await jail.set("$inputs", safeClone(ctx.$inputs ?? {}));
|
|
2732
|
+
await jail.set("$vars", safeClone(ctx.$vars ?? {}));
|
|
2733
|
+
await jail.set("$field", safeClone(ctx.$field ?? {}));
|
|
2734
|
+
await jail.set("request", safeClone(ctx.request));
|
|
2735
|
+
await jail.set("params", safeClone(ctx.params));
|
|
2736
|
+
await jail.set("__placeholders", safeClone(ctx.__placeholders ?? {}));
|
|
2737
|
+
}
|
|
2738
|
+
function wrapUserCode(code) {
|
|
2739
|
+
const trimmed = code.trim();
|
|
2740
|
+
if (trimmed.startsWith("return ")) {
|
|
2741
|
+
return `(async function() { ${code} })()`;
|
|
2742
|
+
}
|
|
2743
|
+
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 ");
|
|
2744
|
+
if (isSimpleExpression) {
|
|
2745
|
+
return `(async function() { return ${code} })()`;
|
|
2746
|
+
}
|
|
2747
|
+
return `(async function() { ${code} })()`;
|
|
2748
|
+
}
|
|
2749
|
+
function executeFallback(code, ctx) {
|
|
2750
|
+
console.warn("[Sandbox] Usando execu\xE7\xE3o INSEGURA (new Function). Instale isolated-vm para seguran\xE7a.");
|
|
2751
|
+
const customFunction = new Function(
|
|
2752
|
+
"input",
|
|
2753
|
+
"context",
|
|
2754
|
+
"request",
|
|
2755
|
+
"params",
|
|
2756
|
+
"$inputs",
|
|
2757
|
+
"$vars",
|
|
2758
|
+
"$field",
|
|
2759
|
+
"__placeholders",
|
|
2760
|
+
code
|
|
2761
|
+
);
|
|
2762
|
+
return customFunction(
|
|
2763
|
+
ctx.input,
|
|
2764
|
+
ctx.context,
|
|
2765
|
+
ctx.request,
|
|
2766
|
+
ctx.params,
|
|
2767
|
+
ctx.$inputs,
|
|
2768
|
+
ctx.$vars,
|
|
2769
|
+
ctx.$field,
|
|
2770
|
+
ctx.__placeholders
|
|
2771
|
+
);
|
|
2772
|
+
}
|
|
2773
|
+
function looksLikeCode(code) {
|
|
2774
|
+
if (typeof code !== "string") return false;
|
|
2775
|
+
const c = code.trim();
|
|
2776
|
+
if (c.length < 3) return false;
|
|
2777
|
+
return /(return\s+|=>|function\s*\(|;|\n|\{|\})/.test(c);
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2394
2780
|
// src/nodes/processors/custom-code.ts
|
|
2395
|
-
var NodeFunction = (params) => {
|
|
2781
|
+
var NodeFunction = async (params) => {
|
|
2396
2782
|
let input = params?.inputValue ?? params?.input;
|
|
2397
2783
|
const context = params && params.fieldValues ? params.fieldValues : params || {};
|
|
2398
2784
|
let customCode = context?.customCode ?? params?.customCode;
|
|
@@ -2405,12 +2791,6 @@ var NodeFunction = (params) => {
|
|
|
2405
2791
|
else if (firstInputField.value !== void 0) input = firstInputField.value;
|
|
2406
2792
|
}
|
|
2407
2793
|
}
|
|
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
2794
|
if (typeof customCode === "string" && !looksLikeCode(customCode)) {
|
|
2415
2795
|
if (input === void 0) input = customCode;
|
|
2416
2796
|
customCode = "";
|
|
@@ -2418,18 +2798,25 @@ var NodeFunction = (params) => {
|
|
|
2418
2798
|
if (!customCode || typeof customCode === "string" && customCode.trim() === "") {
|
|
2419
2799
|
return input;
|
|
2420
2800
|
}
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2801
|
+
const sandboxContext = {
|
|
2802
|
+
input,
|
|
2803
|
+
context,
|
|
2804
|
+
$inputs: params?.results || {},
|
|
2805
|
+
$vars: context && context.variables || params?.variables || void 0,
|
|
2806
|
+
request: params?.request,
|
|
2807
|
+
params,
|
|
2808
|
+
__placeholders: params?.__codePlaceholders ?? context?.__codePlaceholders
|
|
2809
|
+
};
|
|
2810
|
+
const result = await executeSandboxed(customCode, sandboxContext, {
|
|
2811
|
+
timeout: 3e4,
|
|
2812
|
+
// 30 segundos
|
|
2813
|
+
memoryLimit: 128,
|
|
2814
|
+
// 128MB
|
|
2815
|
+
allowFetch: true
|
|
2816
|
+
});
|
|
2817
|
+
return result;
|
|
2431
2818
|
};
|
|
2432
|
-
var CustomNodeFunction = (params) => {
|
|
2819
|
+
var CustomNodeFunction = async (params) => {
|
|
2433
2820
|
const context = params && params.fieldValues ? params.fieldValues : params || {};
|
|
2434
2821
|
const customCode = context?.customCode;
|
|
2435
2822
|
if (!customCode || typeof customCode === "string" && customCode.trim() === "") {
|
|
@@ -2452,29 +2839,36 @@ var CustomNodeFunction = (params) => {
|
|
|
2452
2839
|
}
|
|
2453
2840
|
$field[fieldId] = value;
|
|
2454
2841
|
});
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2842
|
+
const sandboxContext = {
|
|
2843
|
+
$field,
|
|
2844
|
+
context,
|
|
2845
|
+
$inputs: params?.results || {},
|
|
2846
|
+
$vars: context && context.variables || params?.variables || void 0,
|
|
2847
|
+
request: params?.request,
|
|
2848
|
+
params
|
|
2849
|
+
};
|
|
2850
|
+
const result = await executeSandboxed(customCode, sandboxContext, {
|
|
2851
|
+
timeout: 3e4,
|
|
2852
|
+
// 30 segundos
|
|
2853
|
+
memoryLimit: 128,
|
|
2854
|
+
// 128MB
|
|
2855
|
+
allowFetch: true
|
|
2856
|
+
});
|
|
2857
|
+
return result;
|
|
2464
2858
|
};
|
|
2465
2859
|
var CustomCodeNode = {
|
|
2466
2860
|
label: "Custom Code",
|
|
2467
2861
|
type: "CustomCodeNode",
|
|
2468
2862
|
category: "step",
|
|
2469
2863
|
icon: "\u{1F4BB}",
|
|
2470
|
-
description: "Node para executar c\xF3digo JavaScript customizado",
|
|
2864
|
+
description: "Node para executar c\xF3digo JavaScript customizado (sandbox segura)",
|
|
2471
2865
|
fields: [
|
|
2472
2866
|
{
|
|
2473
2867
|
id: "customCode",
|
|
2474
2868
|
label: "C\xF3digo Customizado",
|
|
2475
2869
|
type: "code",
|
|
2476
2870
|
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;'
|
|
2871
|
+
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
2872
|
},
|
|
2479
2873
|
{
|
|
2480
2874
|
id: "input",
|
|
@@ -2936,6 +3330,335 @@ var ModelGuardrailNodeFunction = async (inputs) => {
|
|
|
2936
3330
|
};
|
|
2937
3331
|
};
|
|
2938
3332
|
|
|
3333
|
+
// src/nodes/chat/getOrCreateThread/data.ts
|
|
3334
|
+
var import_zod17 = require("zod");
|
|
3335
|
+
var GetOrCreateThreadSchema = import_zod17.z.object({
|
|
3336
|
+
source: import_zod17.z.string().describe("Channel source: whatsapp, telegram, web, etc."),
|
|
3337
|
+
identifier: import_zod17.z.string().describe("User identifier: phone number, chat_id, userId"),
|
|
3338
|
+
timeoutMinutes: import_zod17.z.number().optional().describe("Inactivity timeout in minutes (default: 1440 = 24h)")
|
|
3339
|
+
});
|
|
3340
|
+
var GetOrCreateThreadNode = {
|
|
3341
|
+
label: "Get or Create Thread",
|
|
3342
|
+
type: "GetOrCreateThreadNode",
|
|
3343
|
+
category: "chat",
|
|
3344
|
+
description: "Generates or retrieves a threadId based on identifier + timeout. Ensures tenant isolation.",
|
|
3345
|
+
icon: "\u{1F9F5}",
|
|
3346
|
+
group: "Chat",
|
|
3347
|
+
tags: {
|
|
3348
|
+
execution: "async",
|
|
3349
|
+
group: "Chat"
|
|
3350
|
+
},
|
|
3351
|
+
fields: [
|
|
3352
|
+
{
|
|
3353
|
+
id: "source",
|
|
3354
|
+
label: "Source",
|
|
3355
|
+
type: "string",
|
|
3356
|
+
required: true,
|
|
3357
|
+
defaultValue: "whatsapp",
|
|
3358
|
+
placeholder: "whatsapp, telegram, web",
|
|
3359
|
+
handle: {
|
|
3360
|
+
type: "input",
|
|
3361
|
+
label: "Source",
|
|
3362
|
+
name: "source",
|
|
3363
|
+
fieldType: "string"
|
|
3364
|
+
}
|
|
3365
|
+
},
|
|
3366
|
+
{
|
|
3367
|
+
id: "identifier",
|
|
3368
|
+
label: "Identifier",
|
|
3369
|
+
type: "string",
|
|
3370
|
+
required: true,
|
|
3371
|
+
placeholder: "+5511999999999",
|
|
3372
|
+
handle: {
|
|
3373
|
+
type: "input",
|
|
3374
|
+
label: "Identifier",
|
|
3375
|
+
name: "identifier",
|
|
3376
|
+
fieldType: "string"
|
|
3377
|
+
}
|
|
3378
|
+
},
|
|
3379
|
+
{
|
|
3380
|
+
id: "timeoutMinutes",
|
|
3381
|
+
label: "Timeout (minutes)",
|
|
3382
|
+
type: "number",
|
|
3383
|
+
required: false,
|
|
3384
|
+
defaultValue: "1440",
|
|
3385
|
+
placeholder: "1440 (24 hours)"
|
|
3386
|
+
},
|
|
3387
|
+
{
|
|
3388
|
+
id: "threadId",
|
|
3389
|
+
label: "Thread ID",
|
|
3390
|
+
type: "string",
|
|
3391
|
+
required: true,
|
|
3392
|
+
typeable: false,
|
|
3393
|
+
handle: {
|
|
3394
|
+
type: "output",
|
|
3395
|
+
label: "Thread ID",
|
|
3396
|
+
name: "threadId",
|
|
3397
|
+
fieldType: "string"
|
|
3398
|
+
}
|
|
3399
|
+
},
|
|
3400
|
+
{
|
|
3401
|
+
id: "isNew",
|
|
3402
|
+
label: "Is New Thread",
|
|
3403
|
+
type: "boolean",
|
|
3404
|
+
required: true,
|
|
3405
|
+
typeable: false,
|
|
3406
|
+
handle: {
|
|
3407
|
+
type: "output",
|
|
3408
|
+
label: "Is New",
|
|
3409
|
+
name: "isNew",
|
|
3410
|
+
fieldType: "boolean"
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
]
|
|
3414
|
+
};
|
|
3415
|
+
|
|
3416
|
+
// src/nodes/chat/getOrCreateThread/function.ts
|
|
3417
|
+
var GetOrCreateThreadNodeFunction = async (inputs) => {
|
|
3418
|
+
const fieldValues = inputs.fieldValues || {};
|
|
3419
|
+
const context = inputs.context || {};
|
|
3420
|
+
const source = fieldValues.source;
|
|
3421
|
+
const identifier = fieldValues.identifier;
|
|
3422
|
+
const timeoutMinutes = Number(fieldValues.timeoutMinutes) || 1440;
|
|
3423
|
+
const companyId = context?.companyId;
|
|
3424
|
+
if (!companyId) {
|
|
3425
|
+
throw new Error("GetOrCreateThread requires companyId in context for tenant isolation");
|
|
3426
|
+
}
|
|
3427
|
+
if (!source || !identifier) {
|
|
3428
|
+
throw new Error("GetOrCreateThread requires source and identifier");
|
|
3429
|
+
}
|
|
3430
|
+
const timeoutMs = timeoutMinutes * 60 * 1e3;
|
|
3431
|
+
const thresholdDate = new Date(Date.now() - timeoutMs);
|
|
3432
|
+
const db = context?.db;
|
|
3433
|
+
if (!db) {
|
|
3434
|
+
throw new Error("GetOrCreateThread requires database connection in context");
|
|
3435
|
+
}
|
|
3436
|
+
try {
|
|
3437
|
+
const existingSessions = await db.query(`
|
|
3438
|
+
SELECT id, session_id, updated_at
|
|
3439
|
+
FROM chat_sessions
|
|
3440
|
+
WHERE company_id = $1
|
|
3441
|
+
AND metadata->>'source' = $2
|
|
3442
|
+
AND metadata->>'identifier' = $3
|
|
3443
|
+
AND updated_at > $4
|
|
3444
|
+
ORDER BY updated_at DESC
|
|
3445
|
+
LIMIT 1
|
|
3446
|
+
`, [companyId, source, identifier, thresholdDate]);
|
|
3447
|
+
if (existingSessions.rows && existingSessions.rows.length > 0) {
|
|
3448
|
+
const session = existingSessions.rows[0];
|
|
3449
|
+
await db.query(`
|
|
3450
|
+
UPDATE chat_sessions
|
|
3451
|
+
SET updated_at = NOW()
|
|
3452
|
+
WHERE id = $1 AND company_id = $2
|
|
3453
|
+
`, [session.id, companyId]);
|
|
3454
|
+
return {
|
|
3455
|
+
threadId: session.session_id,
|
|
3456
|
+
isNew: false
|
|
3457
|
+
};
|
|
3458
|
+
}
|
|
3459
|
+
const newSessionId = crypto.randomUUID();
|
|
3460
|
+
const workflowId = context?.workflowId;
|
|
3461
|
+
await db.query(`
|
|
3462
|
+
INSERT INTO chat_sessions (workflow_id, company_id, chat_id, session_id, metadata)
|
|
3463
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
3464
|
+
`, [
|
|
3465
|
+
workflowId || companyId,
|
|
3466
|
+
// fallback to companyId if no workflowId
|
|
3467
|
+
companyId,
|
|
3468
|
+
`${source}:${identifier}`,
|
|
3469
|
+
// chatId as composite key
|
|
3470
|
+
newSessionId,
|
|
3471
|
+
JSON.stringify({ source, identifier })
|
|
3472
|
+
]);
|
|
3473
|
+
return {
|
|
3474
|
+
threadId: newSessionId,
|
|
3475
|
+
isNew: true
|
|
3476
|
+
};
|
|
3477
|
+
} catch (error) {
|
|
3478
|
+
console.error("[GetOrCreateThread] Error:", error);
|
|
3479
|
+
throw error;
|
|
3480
|
+
}
|
|
3481
|
+
};
|
|
3482
|
+
|
|
3483
|
+
// src/nodes/chat/chatLog/data.ts
|
|
3484
|
+
var import_zod18 = require("zod");
|
|
3485
|
+
var ChatLogSchema = import_zod18.z.object({
|
|
3486
|
+
threadId: import_zod18.z.string().describe("Thread ID from GetOrCreateThread"),
|
|
3487
|
+
direction: import_zod18.z.enum(["inbound", "outbound"]).describe("Message direction"),
|
|
3488
|
+
content: import_zod18.z.string().describe("Message content"),
|
|
3489
|
+
source: import_zod18.z.string().optional().describe("Channel source"),
|
|
3490
|
+
identifier: import_zod18.z.string().optional().describe("User identifier"),
|
|
3491
|
+
metadata: import_zod18.z.record(import_zod18.z.string(), import_zod18.z.any()).optional().describe("Additional metadata")
|
|
3492
|
+
});
|
|
3493
|
+
var ChatLogNode = {
|
|
3494
|
+
label: "Chat Log",
|
|
3495
|
+
type: "ChatLogNode",
|
|
3496
|
+
category: "chat",
|
|
3497
|
+
description: "Saves a message to the chat history. Supports inbound (user) and outbound (assistant) messages.",
|
|
3498
|
+
icon: "\u{1F4AC}",
|
|
3499
|
+
group: "Chat",
|
|
3500
|
+
tags: {
|
|
3501
|
+
execution: "async",
|
|
3502
|
+
group: "Chat"
|
|
3503
|
+
},
|
|
3504
|
+
fields: [
|
|
3505
|
+
{
|
|
3506
|
+
id: "threadId",
|
|
3507
|
+
label: "Thread ID",
|
|
3508
|
+
type: "string",
|
|
3509
|
+
required: true,
|
|
3510
|
+
placeholder: "From GetOrCreateThread node",
|
|
3511
|
+
handle: {
|
|
3512
|
+
type: "input",
|
|
3513
|
+
label: "Thread ID",
|
|
3514
|
+
name: "threadId",
|
|
3515
|
+
fieldType: "string"
|
|
3516
|
+
}
|
|
3517
|
+
},
|
|
3518
|
+
{
|
|
3519
|
+
id: "direction",
|
|
3520
|
+
label: "Direction",
|
|
3521
|
+
type: "select",
|
|
3522
|
+
required: true,
|
|
3523
|
+
defaultValue: "inbound",
|
|
3524
|
+
options: [
|
|
3525
|
+
{ label: "Inbound (User \u2192 System)", value: "inbound" },
|
|
3526
|
+
{ label: "Outbound (System \u2192 User)", value: "outbound" }
|
|
3527
|
+
]
|
|
3528
|
+
},
|
|
3529
|
+
{
|
|
3530
|
+
id: "content",
|
|
3531
|
+
label: "Content",
|
|
3532
|
+
type: "string",
|
|
3533
|
+
required: true,
|
|
3534
|
+
placeholder: "Message content",
|
|
3535
|
+
handle: {
|
|
3536
|
+
type: "input",
|
|
3537
|
+
label: "Content",
|
|
3538
|
+
name: "content",
|
|
3539
|
+
fieldType: "string"
|
|
3540
|
+
}
|
|
3541
|
+
},
|
|
3542
|
+
{
|
|
3543
|
+
id: "source",
|
|
3544
|
+
label: "Source",
|
|
3545
|
+
type: "string",
|
|
3546
|
+
required: false,
|
|
3547
|
+
defaultValue: "whatsapp",
|
|
3548
|
+
placeholder: "whatsapp, telegram, web",
|
|
3549
|
+
handle: {
|
|
3550
|
+
type: "input",
|
|
3551
|
+
label: "Source",
|
|
3552
|
+
name: "source",
|
|
3553
|
+
fieldType: "string"
|
|
3554
|
+
}
|
|
3555
|
+
},
|
|
3556
|
+
{
|
|
3557
|
+
id: "identifier",
|
|
3558
|
+
label: "Identifier",
|
|
3559
|
+
type: "string",
|
|
3560
|
+
required: false,
|
|
3561
|
+
placeholder: "+5511999999999",
|
|
3562
|
+
handle: {
|
|
3563
|
+
type: "input",
|
|
3564
|
+
label: "Identifier",
|
|
3565
|
+
name: "identifier",
|
|
3566
|
+
fieldType: "string"
|
|
3567
|
+
}
|
|
3568
|
+
},
|
|
3569
|
+
{
|
|
3570
|
+
id: "messageId",
|
|
3571
|
+
label: "Message ID",
|
|
3572
|
+
type: "string",
|
|
3573
|
+
required: true,
|
|
3574
|
+
typeable: false,
|
|
3575
|
+
handle: {
|
|
3576
|
+
type: "output",
|
|
3577
|
+
label: "Message ID",
|
|
3578
|
+
name: "messageId",
|
|
3579
|
+
fieldType: "string"
|
|
3580
|
+
}
|
|
3581
|
+
},
|
|
3582
|
+
{
|
|
3583
|
+
id: "success",
|
|
3584
|
+
label: "Success",
|
|
3585
|
+
type: "boolean",
|
|
3586
|
+
required: true,
|
|
3587
|
+
typeable: false,
|
|
3588
|
+
handle: {
|
|
3589
|
+
type: "output",
|
|
3590
|
+
label: "Success",
|
|
3591
|
+
name: "success",
|
|
3592
|
+
fieldType: "boolean"
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
]
|
|
3596
|
+
};
|
|
3597
|
+
|
|
3598
|
+
// src/nodes/chat/chatLog/function.ts
|
|
3599
|
+
var ChatLogNodeFunction = async (inputs) => {
|
|
3600
|
+
const fieldValues = inputs.fieldValues || {};
|
|
3601
|
+
const context = inputs.context || {};
|
|
3602
|
+
const threadId = fieldValues.threadId;
|
|
3603
|
+
const direction = fieldValues.direction;
|
|
3604
|
+
const content = fieldValues.content;
|
|
3605
|
+
const source = fieldValues.source;
|
|
3606
|
+
const identifier = fieldValues.identifier;
|
|
3607
|
+
const companyId = context?.companyId;
|
|
3608
|
+
if (!companyId) {
|
|
3609
|
+
throw new Error("ChatLog requires companyId in context for tenant isolation");
|
|
3610
|
+
}
|
|
3611
|
+
if (!threadId || !content) {
|
|
3612
|
+
throw new Error("ChatLog requires threadId and content");
|
|
3613
|
+
}
|
|
3614
|
+
const role = direction === "inbound" ? "user" : "assistant";
|
|
3615
|
+
const db = context?.db;
|
|
3616
|
+
if (!db) {
|
|
3617
|
+
throw new Error("ChatLog requires database connection in context");
|
|
3618
|
+
}
|
|
3619
|
+
try {
|
|
3620
|
+
const sessionResult = await db.query(`
|
|
3621
|
+
SELECT id FROM chat_sessions
|
|
3622
|
+
WHERE session_id = $1 AND company_id = $2
|
|
3623
|
+
LIMIT 1
|
|
3624
|
+
`, [threadId, companyId]);
|
|
3625
|
+
if (!sessionResult.rows || sessionResult.rows.length === 0) {
|
|
3626
|
+
throw new Error(`Session not found for threadId: ${threadId}`);
|
|
3627
|
+
}
|
|
3628
|
+
const sessionId = sessionResult.rows[0].id;
|
|
3629
|
+
const messageId = crypto.randomUUID();
|
|
3630
|
+
await db.query(`
|
|
3631
|
+
INSERT INTO chat_messages (id, session_id, role, content, metadata)
|
|
3632
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
3633
|
+
`, [
|
|
3634
|
+
messageId,
|
|
3635
|
+
sessionId,
|
|
3636
|
+
role,
|
|
3637
|
+
content,
|
|
3638
|
+
JSON.stringify({
|
|
3639
|
+
direction,
|
|
3640
|
+
source: source || null,
|
|
3641
|
+
identifier: identifier || null
|
|
3642
|
+
})
|
|
3643
|
+
]);
|
|
3644
|
+
await db.query(`
|
|
3645
|
+
UPDATE chat_sessions
|
|
3646
|
+
SET updated_at = NOW()
|
|
3647
|
+
WHERE id = $1 AND company_id = $2
|
|
3648
|
+
`, [sessionId, companyId]);
|
|
3649
|
+
return {
|
|
3650
|
+
messageId,
|
|
3651
|
+
success: true
|
|
3652
|
+
};
|
|
3653
|
+
} catch (error) {
|
|
3654
|
+
console.error("[ChatLog] Error:", error);
|
|
3655
|
+
return {
|
|
3656
|
+
messageId: null,
|
|
3657
|
+
success: false
|
|
3658
|
+
};
|
|
3659
|
+
}
|
|
3660
|
+
};
|
|
3661
|
+
|
|
2939
3662
|
// src/nodes/consts/nodes.ts
|
|
2940
3663
|
var nodes = [
|
|
2941
3664
|
ChatInputNode,
|
|
@@ -2962,7 +3685,9 @@ var nodes = [
|
|
|
2962
3685
|
PromptGuardrailNode,
|
|
2963
3686
|
RuleGuardrailNode,
|
|
2964
3687
|
FunctionGuardrailNode,
|
|
2965
|
-
ModelGuardrailNode
|
|
3688
|
+
ModelGuardrailNode,
|
|
3689
|
+
GetOrCreateThreadNode,
|
|
3690
|
+
ChatLogNode
|
|
2966
3691
|
];
|
|
2967
3692
|
var nodes_default = nodes;
|
|
2968
3693
|
|
|
@@ -3084,7 +3809,9 @@ var nodeFunctions = {
|
|
|
3084
3809
|
PromptGuardrailNode: PromptGuardrailNodeFunction,
|
|
3085
3810
|
RuleGuardrailNode: RuleGuardrailNodeFunction,
|
|
3086
3811
|
FunctionGuardrailNode: FunctionGuardrailNodeFunction,
|
|
3087
|
-
ModelGuardrailNode: ModelGuardrailNodeFunction
|
|
3812
|
+
ModelGuardrailNode: ModelGuardrailNodeFunction,
|
|
3813
|
+
GetOrCreateThreadNode: GetOrCreateThreadNodeFunction,
|
|
3814
|
+
ChatLogNode: ChatLogNodeFunction
|
|
3088
3815
|
};
|
|
3089
3816
|
var node_functions_default = nodeFunctions;
|
|
3090
3817
|
|
|
@@ -3198,12 +3925,12 @@ var HttpPutInputNodeFunction = (params) => {
|
|
|
3198
3925
|
};
|
|
3199
3926
|
|
|
3200
3927
|
// src/nodes/inputs/http/put/schema.ts
|
|
3201
|
-
var
|
|
3202
|
-
var HttpPutInputNodeSchema =
|
|
3928
|
+
var import_zod19 = require("zod");
|
|
3929
|
+
var HttpPutInputNodeSchema = import_zod19.z.object({
|
|
3203
3930
|
route: RouteSchema,
|
|
3204
|
-
queryParams:
|
|
3205
|
-
headers:
|
|
3206
|
-
body:
|
|
3931
|
+
queryParams: import_zod19.z.array(QueryParamSchema).optional().describe("Query parameters configuration"),
|
|
3932
|
+
headers: import_zod19.z.array(HeaderSchema).optional().describe("Headers configuration"),
|
|
3933
|
+
body: import_zod19.z.array(BodyFieldSchema).optional().describe("Body fields configuration")
|
|
3207
3934
|
});
|
|
3208
3935
|
|
|
3209
3936
|
// src/nodes/inputs/http/delete/data.ts
|
|
@@ -3295,11 +4022,11 @@ var HttpDeleteInputNodeFunction = async (params) => {
|
|
|
3295
4022
|
};
|
|
3296
4023
|
|
|
3297
4024
|
// src/nodes/inputs/http/delete/schema.ts
|
|
3298
|
-
var
|
|
3299
|
-
var HttpDeleteInputNodeSchema =
|
|
4025
|
+
var import_zod20 = require("zod");
|
|
4026
|
+
var HttpDeleteInputNodeSchema = import_zod20.z.object({
|
|
3300
4027
|
route: RouteSchema,
|
|
3301
|
-
queryParams:
|
|
3302
|
-
headers:
|
|
4028
|
+
queryParams: import_zod20.z.array(QueryParamSchema).optional().describe("Query parameters configuration"),
|
|
4029
|
+
headers: import_zod20.z.array(HeaderSchema).optional().describe("Headers configuration")
|
|
3303
4030
|
});
|
|
3304
4031
|
|
|
3305
4032
|
// src/nodes/inputs/http/patch/data.ts
|
|
@@ -3412,12 +4139,12 @@ var HttpPatchInputNodeFunction = (params) => {
|
|
|
3412
4139
|
};
|
|
3413
4140
|
|
|
3414
4141
|
// src/nodes/inputs/http/patch/schema.ts
|
|
3415
|
-
var
|
|
3416
|
-
var HttpPatchInputNodeSchema =
|
|
4142
|
+
var import_zod21 = require("zod");
|
|
4143
|
+
var HttpPatchInputNodeSchema = import_zod21.z.object({
|
|
3417
4144
|
route: RouteSchema,
|
|
3418
|
-
queryParams:
|
|
3419
|
-
headers:
|
|
3420
|
-
body:
|
|
4145
|
+
queryParams: import_zod21.z.array(QueryParamSchema).optional().describe("Query parameters configuration"),
|
|
4146
|
+
headers: import_zod21.z.array(HeaderSchema).optional().describe("Headers configuration"),
|
|
4147
|
+
body: import_zod21.z.array(BodyFieldSchema).optional().describe("Body fields configuration")
|
|
3421
4148
|
});
|
|
3422
4149
|
|
|
3423
4150
|
// src/nodes/inputs/http/utils.ts
|
|
@@ -3477,7 +4204,13 @@ var getHttpMethodFromFriendlyId = (friendlyId) => {
|
|
|
3477
4204
|
AiToolNodeFunction,
|
|
3478
4205
|
AiToolNodeSchema,
|
|
3479
4206
|
BodyFieldSchema,
|
|
4207
|
+
ChatLogNode,
|
|
4208
|
+
ChatLogNodeFunction,
|
|
4209
|
+
ChatLogSchema,
|
|
3480
4210
|
CustomToolSchema,
|
|
4211
|
+
GetOrCreateThreadNode,
|
|
4212
|
+
GetOrCreateThreadNodeFunction,
|
|
4213
|
+
GetOrCreateThreadSchema,
|
|
3481
4214
|
HTTP_METHODS,
|
|
3482
4215
|
HTTP_NODE_TYPES,
|
|
3483
4216
|
HeaderSchema,
|