I am working on an internal tool built with Electron that runs various NodeJS and shell scripts.
I want to open a default MacOS Terminal window and run a command. I am aware of and using NodeJS child_process functions like spawn(), fork(), and exec() for other purposes, but in some cases I would much prefer to open a desktop OS Terminal window because the command output is complex, includes escape codes, tails logs, and will be used by developers who may want to control the terminal after being launched by the app.
I tried using exec() and spawn() with open -a Terminal as described here and it simply did not open a Terminal window.
I currently have a solution based on this article that looks like this:
import {exec} from "child_process";
import os = require("os");
export function openTerminal(cmd: string) {
if (os.platform() !== 'darwin') throw new Error('Not supported');
const command = [
`osascript -e 'tell application "Terminal" to activate'`,
`-e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'`,
`-e 'tell application "Terminal" to do script "${cmd}" in selected tab of the front window'`
].join(" ");
const child = exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error);
alert("Unable to open Terminal window, see dev console for error.");
}
});
child.on("exit", (code) => console.log("Open terminal exit"));
}
This works, but has some issues:
Initially the Electron app (built with
electron-packager) is not allowed to run this command, and the user is prompted with "This app wants to control your computer". If the user accepts the dialog the command and subsequent commands still just fail to open a Terminal window. They have to go to OS Security/Accessibility settings, unlock admin privileges, set the app to allow control, close the app, then re-open. Then it might work.Under working conditions (see above) it takes a painfully long time for the window to show up.
If Terminal is already open (which is usually the case) it often (but not always) fails to run the command in the new window, rather it will duplicate an existing tab and run the command in the original tab, which may not work if that tab was already running a command. Even if it does work this is not desirable, it should run the command in the new tab not an existing one.
Is there a way to open a terminal window in MacOS from Electron that avoids these problems?