@inteli.city/node-red-contrib-exec-collection 1.0.4 → 1.0.5

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/async.gpt.js DELETED
@@ -1,615 +0,0 @@
1
- /** ////
2
- * Copyright JS Foundation and other contributors, http://js.foundation
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- **///
16
- module.exports = function(RED) {
17
- //* libraries
18
- "use strict";
19
- var mustache = require("mustache");
20
- var fs = require('fs');
21
- var tmp = require('tmp-promise');
22
- var fsPromises = require('fs').promises;
23
- var terminate = require("terminate");
24
- var yaml = require("js-yaml");
25
- var convertXML = require('xml-js');
26
- var exec = require('child_process').exec;
27
- var spawn = require('child_process').spawn;
28
- var Queue = require('queue');
29
- //* auxiliary functions
30
- //** function: extractTokens
31
- function extractTokens(tokens, set) {
32
- set = set || new Set();
33
- tokens.forEach(function(token) {
34
- if (token[0] !== 'text') {
35
- set.add(token[1]);
36
- if (token.length > 4) {
37
- extractTokens(token[4], set);
38
- }
39
- }
40
- });
41
- return set;
42
- }
43
-
44
- //** function: parseContext
45
- function parseContext(key) {
46
- var match = /^(flow|global)(\[(\w+)\])?\.(.+)/.exec(key);
47
- if (match) {
48
- var parts = {};
49
- parts.type = match[1];
50
- parts.store = (match[3] === '') ? "default" : match[3];
51
- parts.field = match[4];
52
- return parts;
53
- }
54
- return undefined;
55
- }
56
-
57
- //** function: parseEnv
58
- function parseEnv(key) {
59
- var match = /^env\.(.+)/.exec(key);
60
- if (match) {
61
- return match[1];
62
- }
63
- return undefined;
64
- }
65
-
66
-
67
- /**
68
- * Custom Mustache Context capable to collect message property and node
69
- * flow and global context
70
- */
71
-
72
- //** function: remove_by_value (prototype)
73
- Array.prototype.remove_by_value = function(val) {
74
- for (var i = 0; i < this.length; i++) {
75
- if (this[i] === val) {
76
- this.splice(i, 1);
77
- i--;
78
- }
79
- }
80
- return this;
81
- }
82
- //** function: NodeContext
83
- class NodeContext extends mustache.Context {
84
- constructor(msg, nodeContext, parent, escapeStrings, cachedContextTokens) {
85
- super(msg, parent);
86
- this.nodeContext = nodeContext;
87
- this.escapeStrings = escapeStrings;
88
- this.cachedContextTokens = cachedContextTokens;
89
- }
90
-
91
- lookup(name) {
92
- try {
93
- var value = super.lookup(name);
94
- if (value !== undefined) {
95
- if (typeof value === "object") {
96
- value = JSON.stringify(value);
97
- }
98
- if (this.escapeStrings && typeof value === "string") {
99
- value = value.replace(/\\/g, "\\\\")
100
- .replace(/\n/g, "\\n")
101
- .replace(/\t/g, "\\t")
102
- .replace(/\r/g, "\\r")
103
- .replace(/\f/g, "\\f")
104
- .replace(/[\b]/g, "\\b");
105
- }
106
- return value;
107
- }
108
-
109
- if (parseEnv(name)) {
110
- return this.cachedContextTokens[name];
111
- }
112
-
113
- var context = parseContext(name);
114
- if (context) {
115
- var target = this.nodeContext[context.type];
116
- if (target) {
117
- return this.cachedContextTokens[name];
118
- }
119
- }
120
- return '';
121
- } catch (err) {
122
- throw err;
123
- }
124
- }
125
-
126
- push(view) {
127
- return new NodeContext(view, this.nodeContext, this, undefined, this.cachedContextTokens);
128
- }
129
- }
130
-
131
- //* ExecQueueNode
132
- function ExecQueueNode(n) {
133
- //** setting values
134
- RED.nodes.createNode(this, n);
135
- this.name = n.name;
136
- this.field = n.field || "payload";
137
- // this.template = n.template;
138
- this.template = n.template === "" ? " " : n.template; // gambiarra (arrumar)
139
- this.syntax = n.syntax || "javascript";
140
- this.fieldType = n.fieldType || "msg";
141
- this.outputFormat = n.output || "str";
142
-
143
- this.cmd = (n.command || "").trim();
144
- this.append = (n.append || "").trim();
145
- this.useSpawn = n.useSpawn
146
- this.count = 0
147
- this.state = ""
148
- this.queue = n.queue
149
- this.executingCode = 0
150
- this.waitingForExecuting = 0
151
- this.processKilled = false
152
- this.statusTimerUp = new Date()
153
- this.statusTimerDown = new Date()
154
- // this.addpayCB = n.addpayCB
155
- this.cmdTemplate = n.cmdTemplate
156
- this.cmd = (n.command || "").trim();
157
- // this.debugMode = n.debugMode
158
- //this.parsedJSON = n.parsedJSON
159
- this.splitLine = n.splitLine
160
- this.cleanQueue = n.cleanQueue
161
-
162
- if (n.addpay === undefined) { n.addpay = true; }
163
- this.addpay = n.addpay;
164
- this.append = (n.append || "").trim();
165
- this.useSpawn = (n.useSpawn == "true");
166
- this.timer = Number(n.timer || 0) * 1000;
167
- this.activeProcesses = {};
168
- this.tempFiles = []
169
- this.oldrc = (n.oldrc || false).toString();
170
- //this.execOpt = {maxBuffer:50000000, windowsHide: (n.winHide === true)};
171
- //this.execOpt = {encoding:'binary', maxBuffer:50000000, windowsHide: (n.winHide === true)};
172
- this.execOpt = { maxBuffer: 50000000, windowsHide: (n.winHide === true), detached: true };
173
- this.spawnOpt = { windowsHide: (n.winHide === true), detached: true }
174
-
175
- // this.timer = Number(n.timer || 0)*1000;
176
- // this.activeProcesses = {};
177
- // this.oldrc = (n.oldrc || false).toString();
178
- // this.execOpt = {encoding:'binary', maxBuffer:50000000, windowsHide: (n.winHide === true)};
179
- // this.spawnOpt = {windowsHide: (n.winHide === true) }
180
- var node = this;
181
- //** node initialization setup
182
- if (process.platform === 'linux' && fs.existsSync('/bin/bash')) { node.execOpt.shell = '/bin/bash'; }
183
-
184
- var queue = Queue({ results: [], concurrency: node.queue, autostart: true })
185
- node.status({ fill: "blue", shape: "ring", text: `0 (0/${node.queue})` });
186
-
187
- setInterval(() => {
188
- if (node.executingCode !== 0 || node.waitingForExecuting !== 0) {
189
- node.status({ fill: "blue", shape: "ring", text: `${node.waitingForExecuting} (${node.executingCode}/${node.queue})` });
190
- }
191
- }, 1000)
192
-
193
- //** node.on('input')
194
- node.on("input", async function(msg, send, done) {
195
- try {
196
-
197
- if ( msg.stop === true ){
198
- await cleanupNode()
199
- return
200
- }
201
- msg.nodeName = node.name;
202
-
203
- if (msg._msgid === undefined) {
204
- output(msg, msg.message, send, done);
205
- delete msg.message;
206
- return;
207
- }
208
-
209
- const millisecondsUp = ((new Date()).getTime() - node.statusTimerUp.getTime());
210
- node.statusTimerUp = new Date();
211
-
212
- // resolving templates
213
- const resolvedTokens = await resolveTemplate(msg);
214
-
215
- // queue logic
216
- if (node.useSpawn === false) {
217
- msg.lastMessage = false;
218
- }
219
- if (node.executingCode < node.queue) {
220
- node.executingCode++;
221
- } else {
222
- node.waitingForExecuting++;
223
- }
224
- if (millisecondsUp > 100) {
225
- node.status({ fill: "blue", shape: "ring", text: `${node.waitingForExecuting} (${node.executingCode}/${node.queue})` });
226
- }
227
-
228
- await new Promise(resolve => {
229
- queue.push(async () => {
230
- try {
231
- const millisecondsDown = ((new Date()).getTime() - node.statusTimerDown.getTime());
232
- await executeCode(msg, send, done, node, resolvedTokens);
233
- node.statusTimerDown = new Date();
234
-
235
- if (node.waitingForExecuting > 0) {
236
- node.waitingForExecuting--;
237
- } else if (node.executingCode > 0) {
238
- node.executingCode--;
239
- if (node.executingCode === 0 && node.useSpawn === false) {
240
- msg.lastMessage = true;
241
- }
242
- }
243
- if (node.processKilled === false && (millisecondsDown > 100 || node.executingCode === 0)) {
244
- node.status({ fill: "blue", shape: "ring", text: `${node.waitingForExecuting} (${node.executingCode}/${node.queue})` });
245
- }
246
- resolve();
247
- } catch (error) {
248
- reject(error);
249
- }
250
- });
251
- });
252
-
253
- } catch (err) {
254
- done(err.message);
255
- }
256
- });
257
-
258
-
259
- //** node.on('close')
260
- node.on('close', async function() {
261
- cleanupNode();
262
- });
263
-
264
- //** cleanUpNode
265
- async function cleanupNode() {
266
- //// KILL PROCESSES AND ERASE FILES
267
- try {
268
- queue.end();
269
- node.executingCode = 0;
270
- node.waitingForExecuting = 0;
271
- node.processKilled = true;
272
-
273
- if (node.activeProcesses !== undefined ){
274
- for (var pid in node.activeProcesses) {
275
- if (node.activeProcesses.hasOwnProperty(pid)) {
276
- terminate(pid);
277
- node.activeProcesses[pid] = null;
278
- node.warn(`Killing pid ${pid}`);
279
- }
280
- }
281
- }
282
-
283
- if ( node.tempFiles !== undefined ){
284
- for (let i = 0, len = node.tempFiles.length; i < len; i++) {
285
- await fsPromises.unlink(file);
286
- }
287
- }
288
- node.activeProcesses = {};
289
- node.status({ fill: "blue", shape: "ring", text: `0 (0/${node.queue})` });
290
- } catch (error) {
291
- node.warn("An error occurred: " + error.message);
292
- }
293
- }
294
-
295
- //** executeCode
296
- async function executeCode(msg, send, done, node, resolvedTokens) {
297
-
298
- // Create a temporary file asynchronously
299
- const is_json = (node.outputFormat === "parsedJSON");
300
- let template = node.template || msg.template;
301
- const value = mustache.render(template, new NodeContext(msg, node.context(), null, is_json, resolvedTokens));
302
- const tmpObj = await tmp.file();
303
- await fsPromises.writeFile(tmpObj.path, value, 'utf8');
304
-
305
- let shellcode;
306
- let resolvedCommand
307
- if ( node.cmdTemplate ){
308
- resolvedCommand = await resolveCommand(node.cmd, msg);
309
- } else {
310
- resolvedCommand = node.cmd
311
- }
312
- if (process.platform === 'win32') { // For Windows
313
- shellcode = `
314
- file="${tmpObj.path}"
315
- ${resolvedCommand}
316
- `;
317
- } else { // For Linux and macOS
318
- shellcode = `
319
- export NODE_PATH="$NODE_PATH:/usr/local/lib/node_modules"
320
- file="${tmpObj.path}"
321
- ${resolvedCommand}
322
- `;
323
- }
324
-
325
- try {
326
- if (!node.useSpawn) {
327
- await executeWithExec(shellcode, node, msg, send, done);
328
- } else {
329
- await executeWithSpawn(shellcode, node, msg, send, done);
330
- }
331
- } finally {
332
- await tmpObj.cleanup();
333
- }
334
- }
335
-
336
- async function executeWithExec(shellcode, node, msg, send, done) {
337
- return new Promise((resolve) => {
338
- const child = exec(shellcode, node.execOpt, (err, stdout, stderr) => {
339
- if (err) {
340
- const error = {
341
- type: 'error',
342
- code: err.code,
343
- killed: err.killed
344
- };
345
- msg.error_info = error;
346
- node.error(`error (${msg.nodeName})\n\n${stderr}`, msg);
347
- } else {
348
- if (stderr) {
349
- node.error(`warning (${msg.nodeName})\n\n${stderr}`, msg);
350
- }
351
-
352
- if (stdout) {
353
- stdout = stdout.trim();
354
- if (node.splitLine === false) {
355
- output(msg, stdout, send, done);
356
- } else {
357
- stdout = stdout.split('\n');
358
- for (let i = 0; i < stdout.length; i++) {
359
- node.emit("input", { "message": stdout[i] });
360
- }
361
- }
362
- // if (node.debugMode === true) {
363
- // node.warn(stdout);
364
- // }
365
- }
366
- }
367
- delete node.activeProcesses[child.pid];
368
- resolve();
369
- });
370
- node.activeProcesses[child.pid] = child.pid;
371
- });
372
- }
373
-
374
- async function executeWithSpawn(shellcode, node, msg, send, done) {
375
- return new Promise((resolve, reject) => {
376
- const child = spawn('/bin/bash', ['-c', shellcode], node.spawnOpt);
377
- node.activeProcesses[child.pid] = child.pid;
378
-
379
- child.stdout.on('data', (data) => {
380
- data = data.toString();
381
- if (node.splitLine === false) {
382
- output(msg, data, send, done);
383
- } else {
384
- const lines = data.split('\n');
385
- for (let line of lines) {
386
- if (line) {
387
- node.emit("input", { "message": line });
388
- }
389
- }
390
- }
391
- // if (node.debugMode === true) {
392
- // node.warn(data);
393
- // }
394
- });
395
-
396
- child.stderr.on('data', (data) => {
397
- node.error(`warning (${msg.nodeName})\n\n${data.toString()}`, msg);
398
- });
399
-
400
- child.on('close', (code) => {
401
- if (code !== 0) {
402
- const error = {
403
- type: 'error',
404
- code: code
405
- };
406
- msg.error_info = error;
407
- node.error(`error (${msg.nodeName}): The node hasn't finished its execution`, msg);
408
- reject(new Error(`Child process exited with code ${code}`));
409
- }
410
- delete node.activeProcesses[child.pid];
411
- resolve();
412
- });
413
- });
414
- }
415
-
416
- //** resolveTemplate
417
- async function resolveTemplate(msg) {
418
- var template = node.template;
419
- if (msg.hasOwnProperty("template")) {
420
- if (template == "" || template === null) {
421
- template = msg.template;
422
- }
423
- }
424
-
425
- var resolvedTokens = {};
426
- var tokens = extractTokens(mustache.parse(template));
427
-
428
- // Iterate over the extracted tokens to resolve their values.
429
- for (let name of tokens) {
430
- let env_name = parseEnv(name);
431
- if (env_name) {
432
- resolvedTokens[name] = RED.util.evaluateNodeProperty(env_name, 'env', node);
433
- continue;
434
- }
435
-
436
- // Check if the token refers to a flow or global context variable.
437
- let context = parseContext(name);
438
- if (context) {
439
- let type = context.type;
440
- let store = context.store;
441
- let field = context.field;
442
- let target = node.context()[type];
443
- if (target) {
444
- resolvedTokens[name] = await new Promise((resolve, reject) => {
445
- target.get(field, store, (err, val) => {
446
- if (err) reject(err);
447
- else resolve(val);
448
- });
449
- });
450
- }
451
- }
452
- }
453
-
454
- return resolvedTokens;
455
- }
456
-
457
- //** resolveCommand
458
- async function resolveCommand(command, msg) {
459
- return new Promise(async (resolve, reject) => {
460
- try {
461
- // Extract tokens from the command string
462
- var tokens = extractTokens(mustache.parse(command));
463
-
464
- var resolvedTokens = {};
465
-
466
- // Iterate over the extracted tokens to resolve their values.
467
- for (let name of tokens) {
468
- let env_name = parseEnv(name);
469
- if (env_name) {
470
- resolvedTokens[name] = RED.util.evaluateNodeProperty(env_name, 'env', node);
471
- continue;
472
- }
473
-
474
- // Check if the token refers to a flow or global context variable.
475
- let context = parseContext(name);
476
- if (context) {
477
- let type = context.type;
478
- let store = context.store;
479
- let field = context.field;
480
- let target = node.context()[type];
481
- if (target) {
482
- resolvedTokens[name] = await new Promise((innerResolve, innerReject) => {
483
- target.get(field, store, (err, val) => {
484
- if (err) innerReject(err);
485
- else innerResolve(val);
486
- });
487
- });
488
- }
489
- }
490
- }
491
-
492
- var parsedCommand = mustache.parse(command);
493
-
494
- // Extract the variable names from the parsed tokens.
495
- var variableNames = parsedCommand
496
- .filter(token => token[0] === '&')
497
- .map(token => token[1]);
498
-
499
- var resolvedValues = {};
500
-
501
- // Resolve the values for each variable.
502
- for (let variableName of variableNames) {
503
- let env_name = parseEnv(variableName);
504
- if (env_name) {
505
- resolvedValues[variableName] = RED.util.evaluateNodeProperty(env_name, 'env', node);
506
- continue;
507
- }
508
-
509
- // Check if the variable refers to a flow or global context variable.
510
- let context = parseContext(variableName);
511
- if (context) {
512
- let type = context.type;
513
- let store = context.store;
514
- let field = context.field;
515
- let target = node.context()[type];
516
- if (target) {
517
- resolvedValues[variableName] = await new Promise((innerResolve, innerReject) => {
518
- target.get(field, store, (err, val) => {
519
- if (err) innerReject(err);
520
- else innerResolve(val);
521
- });
522
- });
523
- }
524
- } else {
525
- // If the variable is not in the context or an env variable, try to get it from the msg object.
526
- resolvedValues[variableName] = msg[variableName];
527
- }
528
- }
529
-
530
- // Replace the variables in the original command with their resolved values.
531
- for (let variableName in resolvedValues) {
532
- command = command.replace(`{{{${variableName}}}}`, resolvedValues[variableName]);
533
- }
534
-
535
- console.log(command);
536
- resolve(command); // Use the resolve here
537
- } catch (error) {
538
- console.error("Error in resolveCommand:", error);
539
- reject(error); // Use the reject here
540
- }
541
- });
542
- }
543
-
544
- //** function: output
545
- function output(msg, value, send, done) {
546
- /* istanbul ignore else */
547
- let parseError = false
548
- //*** parse json
549
- if (node.outputFormat === "parsedJSON") {
550
- try {
551
- value = JSON.parse(value);
552
- if (typeof value === 'number') {
553
- parseError = true
554
- node.error('Error parsing JSON: \n\n' + error)
555
- }
556
- } catch (error) {
557
- parseError = true
558
- node.error('Error parsing JSON: \n\n' + error)
559
- }
560
- }
561
-
562
- //*** parse xml
563
- if (node.outputFormat === "parsedXML") {
564
- try {
565
- //value = JSON.parse(convertXML.xml2json(value, {compact: true, spaces: 4}))
566
- value = convertXML.xml2js(value, { compact: true, spaces: 4 })
567
- } catch (error) {
568
- parseError = true
569
- node.error('Error parsing XML: \n\n' + error)
570
- }
571
- }
572
-
573
- //*** parse yaml
574
- if (node.outputFormat === "parsedYAML") {
575
- try {
576
- value = yaml.load(value);
577
- if (typeof value === 'number') {
578
- parseError = true
579
- node.error('Error parsing YAML: \n\n' + error)
580
- }
581
- } catch (error) {
582
- parseError = true
583
- node.error('Error parsing YAML: \n\n' + error)
584
- }
585
- }
586
-
587
- //*** parse error
588
- if (parseError === false) {
589
- if (node.fieldType === 'msg') {
590
- RED.util.setMessageProperty(msg, node.field, value);
591
- send(msg);
592
- done();
593
- } else if ((node.fieldType === 'flow') ||
594
- (node.fieldType === 'global')) {
595
- var context = RED.util.parseContextStore(node.field);
596
- var target = node.context()[node.fieldType];
597
- target.set(context.key, value, context.store, function(err) {
598
- if (err) {
599
- done(err);
600
- } else {
601
- send(msg);
602
- done();
603
- }
604
- });
605
- }
606
- }
607
- }
608
-
609
- }
610
-
611
- //* end
612
- RED.nodes.registerType("async.gpt", ExecQueueNode);
613
- RED.library.register("templates");
614
- }
615
-