node-red-contrib-iit-execwin 0.1.0

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/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # node-red-contrib-iit-execwin
2
+
3
+ Nodo Node-RED para ejecutar comandos de PowerShell o CMD en Windows, con salida estructurada (stdout, stderr, exitCode).
4
+
5
+ Diferencias frente al nodo `exec` incluido en Node-RED:
6
+ - Por defecto usa PowerShell en vez de CMD (configurable).
7
+ - Devuelve `stdout`, `stderr` y `exitCode` en propiedades separadas de `msg`, en vez de tres salidas del nodo.
8
+ - Corta la ejecución sola si se pasa de un timeout configurable (por defecto 30s).
9
+ - `windowsHide: true` — no abre una ventana de consola visible al ejecutar.
10
+
11
+ ## Instalación
12
+
13
+ ```bash
14
+ npm install node-red-contrib-iit-execwin
15
+ ```
16
+
17
+ ## Ejemplo de uso
18
+
19
+ ```js
20
+ msg.payload = "Get-Service -Name Spooler | Select-Object Status";
21
+ return msg;
22
+ ```
23
+
24
+ La salida llega en `msg.payload` (texto), `msg.stderr` (si hubo errores) y `msg.exitCode` (0 = éxito).
25
+
26
+ ## Seguridad
27
+
28
+ Este nodo ejecuta directamente lo que llegue en `msg.payload`. No lo conectes a una fuente de datos externa no confiable (un webhook público, un formulario sin validar, etc.) sin sanitizar antes el contenido — es funcionalmente equivalente a darle acceso a una terminal de tu equipo a quien controle ese mensaje.
29
+
30
+ ## Licencia
31
+
32
+ MIT
@@ -0,0 +1,68 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('iit-exec-win', {
3
+ category: 'IIT',
4
+ color: '#7FB2E5',
5
+ defaults: {
6
+ name: { value: '' },
7
+ shellType: { value: 'powershell' },
8
+ workingDir: { value: '' },
9
+ timeoutMs: { value: 30000, validate: RED.validators.number() },
10
+ },
11
+ inputs: 1,
12
+ outputs: 1,
13
+ icon: 'font-awesome/fa-terminal',
14
+ label: function () {
15
+ return this.name || 'iit-exec-win';
16
+ },
17
+ });
18
+ </script>
19
+
20
+ <script type="text/html" data-template-name="iit-exec-win">
21
+ <div class="form-row">
22
+ <label for="node-input-name"><i class="fa fa-tag"></i> Nombre</label>
23
+ <input type="text" id="node-input-name" placeholder="Nombre">
24
+ </div>
25
+ <div class="form-row">
26
+ <label for="node-input-shellType"><i class="fa fa-terminal"></i> Shell</label>
27
+ <select id="node-input-shellType" style="width:70%">
28
+ <option value="powershell">PowerShell</option>
29
+ <option value="cmd">CMD</option>
30
+ </select>
31
+ </div>
32
+ <div class="form-row">
33
+ <label for="node-input-workingDir"><i class="fa fa-folder-open"></i> Carpeta de trabajo</label>
34
+ <input type="text" id="node-input-workingDir" placeholder="C:\ruta\opcional">
35
+ </div>
36
+ <div class="form-row">
37
+ <label for="node-input-timeoutMs"><i class="fa fa-clock-o"></i> Timeout (ms)</label>
38
+ <input type="number" id="node-input-timeoutMs" placeholder="30000">
39
+ </div>
40
+ <div class="form-tips">
41
+ <p><i class="fa fa-info-circle"></i> El comando a ejecutar llega en <code>msg.payload</code> como texto. Nunca conectes este nodo a una entrada que reciba texto sin validar desde fuera de tu red de confianza — el comando se ejecuta tal cual.</p>
42
+ </div>
43
+ </script>
44
+
45
+ <script type="text/html" data-help-name="iit-exec-win">
46
+ <p>Ejecuta un comando de PowerShell o CMD en Windows y devuelve su resultado estructurado.</p>
47
+ <h3>Entradas</h3>
48
+ <dl class="message-properties">
49
+ <dt>payload <span class="property-type">string</span></dt>
50
+ <dd>El comando a ejecutar.</dd>
51
+ <dt class="optional">shellType <span class="property-type">string</span></dt>
52
+ <dd>Sobrescribe el shell configurado: "powershell" o "cmd".</dd>
53
+ <dt class="optional">workingDir <span class="property-type">string</span></dt>
54
+ <dd>Sobrescribe la carpeta de trabajo.</dd>
55
+ <dt class="optional">timeoutMs <span class="property-type">number</span></dt>
56
+ <dd>Sobrescribe el timeout en milisegundos.</dd>
57
+ </dl>
58
+ <h3>Salidas</h3>
59
+ <dl class="message-properties">
60
+ <dt>payload <span class="property-type">string</span></dt>
61
+ <dd>La salida estándar (stdout) del comando, sin espacios extra al inicio/fin.</dd>
62
+ <dt>stderr <span class="property-type">string</span></dt>
63
+ <dd>La salida de error (stderr), si la hubo.</dd>
64
+ <dt>exitCode <span class="property-type">number</span></dt>
65
+ <dd>Código de salida del proceso. 0 = éxito.</dd>
66
+ </dl>
67
+ <p><strong>Seguridad:</strong> este nodo ejecuta el texto de <code>msg.payload</code> directamente en una shell del sistema. No lo expongas a entradas externas no confiables (ej. un formulario web público) sin validar/filtrar antes.</p>
68
+ </script>
@@ -0,0 +1,92 @@
1
+ const { spawn } = require('child_process');
2
+
3
+ module.exports = function (RED) {
4
+ function IitExecWinNode(config) {
5
+ RED.nodes.createNode(this, config);
6
+ const node = this;
7
+
8
+ node.shellType = config.shellType || 'powershell'; // 'powershell' | 'cmd'
9
+ node.workingDir = config.workingDir || '';
10
+ node.timeoutMs = parseInt(config.timeoutMs) || 30000;
11
+
12
+ node.on('input', function (msg, send, done) {
13
+ send = send || function () { node.send.apply(node, arguments); };
14
+
15
+ const command = msg.payload;
16
+ if (typeof command !== 'string' || !command.trim()) {
17
+ node.status({ fill: 'red', shape: 'ring', text: 'comando vacío' });
18
+ node.error('msg.payload debe ser un string con el comando a ejecutar', msg);
19
+ if (done) done();
20
+ return;
21
+ }
22
+
23
+ const shellType = msg.shellType || node.shellType;
24
+ const cwd = msg.workingDir || node.workingDir || undefined;
25
+ const timeoutMs = msg.timeoutMs || node.timeoutMs;
26
+
27
+ let bin, args;
28
+ if (shellType === 'cmd') {
29
+ bin = 'cmd.exe';
30
+ args = ['/d', '/s', '/c', command];
31
+ } else {
32
+ bin = 'powershell.exe';
33
+ args = ['-NoProfile', '-NonInteractive', '-Command', command];
34
+ }
35
+
36
+ node.status({ fill: 'blue', shape: 'dot', text: 'ejecutando…' });
37
+
38
+ let stdout = '';
39
+ let stderr = '';
40
+ let finished = false;
41
+
42
+ const child = spawn(bin, args, { cwd, windowsHide: true });
43
+
44
+ const timer = setTimeout(() => {
45
+ if (!finished) {
46
+ finished = true;
47
+ child.kill();
48
+ node.status({ fill: 'red', shape: 'ring', text: 'timeout' });
49
+ node.error('El comando excedió el timeout de ' + timeoutMs + ' ms', msg);
50
+ if (done) done();
51
+ }
52
+ }, timeoutMs);
53
+
54
+ child.stdout.on('data', (d) => { stdout += d.toString(); });
55
+ child.stderr.on('data', (d) => { stderr += d.toString(); });
56
+
57
+ child.on('error', (err) => {
58
+ if (finished) return;
59
+ finished = true;
60
+ clearTimeout(timer);
61
+ node.status({ fill: 'red', shape: 'ring', text: 'error' });
62
+ node.error('No se pudo iniciar el proceso: ' + err.message, msg);
63
+ if (done) done(err);
64
+ });
65
+
66
+ child.on('close', (code) => {
67
+ if (finished) return;
68
+ finished = true;
69
+ clearTimeout(timer);
70
+
71
+ msg.payload = stdout.trim();
72
+ msg.stderr = stderr.trim();
73
+ msg.exitCode = code;
74
+
75
+ if (code === 0) {
76
+ node.status({ fill: 'green', shape: 'dot', text: 'ok' });
77
+ } else {
78
+ node.status({ fill: 'yellow', shape: 'ring', text: 'código ' + code });
79
+ }
80
+
81
+ send(msg);
82
+ if (done) done();
83
+ });
84
+ });
85
+
86
+ node.on('close', function () {
87
+ node.status({});
88
+ });
89
+ }
90
+
91
+ RED.nodes.registerType('iit-exec-win', IitExecWinNode);
92
+ };
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "node-red-contrib-iit-execwin",
3
+ "version": "0.1.0",
4
+ "description": "Nodo Node-RED para ejecutar comandos PowerShell/CMD en Windows, con salida estructurada",
5
+ "keywords": ["node-red", "exec", "windows", "powershell", "iit"],
6
+ "node-red": {
7
+ "nodes": {
8
+ "iit-exec-win": "iit-exec-win.js"
9
+ }
10
+ },
11
+ "engines": {
12
+ "node": ">=18.0.0"
13
+ },
14
+ "license": "MIT",
15
+ "author": "Jairo Sepúlveda",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/infraestructura-it/node-red-contrib-iit-execwin.git"
19
+ }
20
+ }