Context: I have a javascript file that activates PowerShell's native SpeechSynthesizer module. The script receives a message and passes that through to PowerShell, where it is rendered as speech.
Problem: there is horrible latency (~5sec) between execution and response. This is because the script creates an entirely new PowerShell session and SpeechSynthesizer object with every execution.
Objective: I want to change the script so that a single PowerShell session and SpeechSynthesizer object is persisted and used across multiple sessions. I believe this will eradicate the latency completely.
Limiting Factor: this modification requires making the PowerShell execution stateful. Currently, I don't know how to incorporate stateful commands for the PowerShell in a javascript file.
Code:
const path = require('path');
const Max = require('max-api');
const { exec } = require('child_process');
// This will be printed directly to the Max console
Max.post(`Loaded the ${path.basename(__filename)} script`);
const execCommand = command => {
// Max.post(`Running command: ${command}`);
exec(command, {'shell':'powershell.exe'}, (err, stdout, stderr) => {
if (err) {
// node couldn't execute the command
Max.error(stderr);
Max.error(err);
return;
}
// the *entire* stdout and stderr (buffered)
Max.outletBang()
});
}
// Use the 'outlet' function to send messages out of node.script's outlet
Max.addHandler("speak", (msg) => {
let add = 'Add-Type -AssemblyName System.speech'
let create = '\$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer'
let speak = `\$speak.Speak(\'${msg}\')`
let command = ([add,create,speak]).join('; ')
execCommand(command)
});
Objective, Re-stated: I want to move the add and create commands to a 'create' handler which will only be ran once. The speak command will be run an arbitrary amount of times afterward.
Attempted Solution: I've found one package (https://github.com/bitsofinfo/powershell-command-executor) that supposedly supports stateful PowerShell commands, but it's very complicated. Also, the author mentions a risk of command injection and other insecurities, of which I have no knowledge of.
Any and all suggestions are welcome. Thanks!
Related
I have created a node script which should run as a background process. Right after the script starts running run I need to get some user data (username & password).
After getting the user data I wish to close the terminal but the process should continue running in the background.
To complicate things, the node script is being built with pkg lib and will be started as an .exe
pkg on NPM
Here is a the code that will be executed:
async function init() {
try {
await fetchConfig();
const credentials = await getCredentials(); // this one prompts the request for user input
watchForNewFiles(credentials); // that one should use the credentials and continue running in the background.
} catch (e) {
console.error(e);
}
}
init();
Here's a solution:
// Neccesary libs
let fs = require("fs");
let child_process = require("child_process");
// Filename should end in .bat
let filename = "__tmp.bat";
// Main function, call this to prompt the values.
function prompt() {
// Write batch script
// Prompts 2 variables & writes them as JSON into the same file
fs.writeFileSync(filename,`#echo off
set /p Var1="Name: "
set /p Var2="Password: "
echo {"name": "%Var1%", "password": "%Var2%"} > ${filename}
exit`);
// Execute the script in a cmd.exe window & write the result into stdout & parse it as JSON
let result = JSON.parse(child_process.execSync(`start /w cmd /c ${filename} && type ${filename}`));
// Delete the file
fs.unlinkSync(filename);
return results;
}
// Example usage:
console.log(prompt()) // prints { name: 'hi', password: 'world' }
Tested as a node14-win-x64 pkg binary, works perfectly.
AFAIU this is not possible to do from inside node.js's running code by itself.
You have to use some kind of tool to put the node.js program in the background.
This answer lists several tools for this purpose.
This one is also useful.
I've been worked on a vue project.
This vue project use the nodejs API I've created, in simple way, they are two entire differents project which are not located in the same directory and they are launched separately.
The problem is whenever I debug a route with node --inspect --debug-break event_type.controller.js for example named:
"/eventtype/create"
exports.create = (req, res) => {
const userId = jwt.getUserId(req.headers.authorization);
if (userId == null) {
res.status(401).send(Response.response401());
return;
}
// Validate request
if (!req.body.label || !req.body.calendarId) {
res.status(400).send(Response.response400());
return;
}
const calendarId = req.body.calendarId; // Calendar id
// Save to database
EventType.create({
label: req.body.label,
}).then((eventType) => {
Calendar.findByPk(calendarId).then((calendar) => {
eventType.addCalendar(calendar); // Add a Calendar
res.status(201).send(eventType);
}).catch((err) => {
res.status(500).send(Response.response500(err.message));
});
}).catch((err) => {
res.status(500).send(Response.response500(err.message));
});
};
Even if I create a breakpoint on const userId = jwt.getUserId(req.headers.authorization);
and from my vue app I trigger the api createEventType event, my break point is not passed.
Also when I press f8 after the breakpoint on my first line with the debugger, my file close automatically.
I do not use VS Code but Vim for coding but I've heard that maybe Vs Code could allow a simplified way to debug nodesjs application.
NOTE: I use the V8 node debugger.
For newer NodeJS versions (> 7.0.0) you need to use
node --inspect-brk event_type.controller.js
instead of
node --inspect --debug-break event_type.controller.js
to break on the first line of the application code. See https://nodejs.org/api/debugger.html#debugger_advanced_usage for more information.
The solution (even if it's not really a solution) has been to add console.log to the line I wanted to debug.
i have been looking for some days after this in order to avoid writting again a lot of code i already have in C language.
Is any way of running an external program, that i already has coded on C, obviously giving some parameters that it need to accomplish some task and then get the "return" variable? All that is for a lil home proyect i am making.
I am looking for making this on node.js just cause its the framework i am using for getting my backend done.
First question i made on a long time, only making it because i am not finding out it.
There are multiple options in the nodejs child_process module. Some of those options will "wait" and run the external program synchronously such as execFileSync(), execSync() and spawnSync(). But, keep in mind that if you do that, your entire node.js process is blocked while waiting. You would never want to do that in a server that needs to be responsive to incoming requests, but could do it other types of scripts.
Also, you don't have to use the synchronous versions. You can still run the program and get the results, just doing so asynchronously where you'll get notified when it completes.
Given that main programs in C can only return an integer value (a 0 or non-zero byte) indicating the exit status of the app: running the the app in a child process with node's child_process.exec or child_process.spawn and printing the value of the variables you wish to return to standard output (with a function like printf) will allow you to capture and use them in node.
For example, in an example.js file you can run the executable file you compiled, pass in a parameter as a string via argv, and return a promise. By making your server async, it will allow the node process to await the return value of your program and handle its success or failures asynchronously:
#!/usr/bin/env node
const http = require('http')
const { exec } = require("child_process");
const hostname = '127.0.0.1'
const port = '3000'
const execCProgram = (parameter) => {
return new Promise((resolved, rejected) => {
exec(`./example ${parameter}`, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
rejected(error);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
const cData = {returnValue: stdout};
resolved(cData);
});
})
}
const server = http.createServer(async (req, res) => {
await execCProgram('JS_DATA').then(data => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end(`My data in node: ${data.returnValue}\n`)
});
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
Example C program this runs:
#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[]) {
char *mutated_parameter = strcat(argv[argc - 1], " that was mutated in my C program and returned");
printf("%s", mutated_parameter);
return 0;
}
👉 If the return value is anything other than 0, the promise from example.js will throw.
Example Client-side Output:
$ curl localhost:3000
My data in node: JS_Data that was mutated in my C program and returned
Note: consider using spawn or spawnSync if your app is going to run for a while and generate a lot of output data.
I'm running a function which I've written in JavaScript inside a nodejs/Electron client.
This function is meant to copy a file from the users flash drive to their c:/Windows/System32 (The file is being copied there so that it can be ran from Command Prompt manually next time the computer is touched without having to switch directories)
The problem is, the files are not being copied, and copyFileSync is not throwing an error.
Here is the code I'm specifically having a problem with:
try {
console.log('copying t.bat');
fs.copyFileSync(remote.app.getAppPath() + '\\app\\files\\scripts\\files\\t.bat', 'C:\\Windows\\System32\\t.bat');
} catch(err) {
console.log('could not copy t.bat', err);
$('#mfail_title').text('Could not copy t.bat file');
$('#mfail_data').text(err);
UIkit.modal("#master_fail").show();
return false;
}
As you can see, I have copyFileSync inside a TRY CATCH block. I know this code is running because in the console I get copying t.bat, but nothing else.
How can I get my files to copy, or at least throw an error when it cannot?
This client is running inside OOBE mode on various Windows 10 machines, therefore always has administrator access.
I've tried updating to the async version of copyFile, but I'm having the same issue. Here is my code
var source = remote.app.getAppPath() + '\\app\\files\\scripts\\files\\t.bat';
var destination = 'C:\\Windows\\System32\\t.bat';
fs.copyFile(source, destination, (err) => {
if (err) {
console.log(err);
} else {
source = remote.app.getAppPath() + '\\app\\files\\scripts\\files\\p.bat';
destination = 'C:\\Windows\\System32\\p.bat';
fs.copyFile(source, destination, (err) => {
if (err) {
console.log(err);
} else {
source = remote.app.getAppPath() + '\\app\\files\\scripts\\files\\p.bat';
destination = 'C:\\Windows\\System32\\p.bat';
child = spawn("powershell.exe",['-ExecutionPolicy', 'ByPass', '-File', remote.app.getAppPath() + '\\app\\files\\scripts\\' + type + '.ps1']);
}
});
}
});
This should copy a file, then when it's complete it should copy another file, once that is complete, it should run a powershell script.
Each copyFile checks for an error before moving on, but it never throws an error, and the file is never copied.
I had a similar issue earlier, In which an Antivirus(Comodo) was not allowing electron app to access the hard drive.
Copy and other file operations were successful in that case as well, because electron in such case access the corresponding sandbox
Please check this is not the case with you.
You can actually access 'fs' in console from electron and check other things in the file system.
Looks to me as if you're using fs on then renderer process (client side) which will not work (assuming that your fs is the node.js fs module and (*)). Your first script seems to use jQuery (hints for renderer) and the second one uses remote in the first line.
fs can only (*) be used on the main process and you'll need to create an IRC channel and do something like:
ircRenderer.sendSync('copy-file-sync', {from: '/from/path', to: '/to/path'})
and, of course, implement the handler for that quickly invented 'copy-file' channel on the main process.
(*) Edit: I haven't played around a lot with nodeIntegration = true, so fs may or may not work on the renderer process with that flag set on the BrowserWindow. But the irc messaging should definitely work and if not, the problem is outside electron, probably related to file permissions.
Here's the setup: I create simple WebSocket Server using the ws library. I then attach a listener for when the client sends me the URL of a PDF to transform. I download it locally then I call another command to transform it:
const download = require("download");
wss.on("connection", ws => {
ws.onmessage = async msg => {
await download(msg.data, destination, {
filename: fileName
});
transformPDF(ws, msg.data);
};
// ...
});
After that, the transformPDF function calls the spawn command to execute a command line binary. I parse the percentage done from the stdout and then try to emit it to the client. But even before this, the connection has been closed and I'm not sure why:
const { spawn } = require("child_process");
const transformPDF = (ws, url) => {
// ...
const child = spawn("k2pdfopt", settings);
child.stdout.on("data", data => {
// ...
ws.send(percentageDone); // <--- connection is broken before this is called
});
};
I have tried to make the transformPDF function a promise and then awaiting it. I have also tried adding an optional detached option to the spawn process. I'm not really sure why it's closing since I've also successfully replaced the command k2pdfopt with something like a lengthy find, and that worked just fine (although it did batch all of the data in the stdout before calling ws.send).
Any help or insight on why it's closing is much appreciated.
Turns out that when I was creating a child process, it was resetting the Visual Studio Code live-server extension that I had running the index.html. That explains why I was also getting a status code of 1001, which I found out most likely means the client refreshed.
I fixed the issue by simply installing the node package live-server and running my index.html from a different terminal.