macotron.shell
Run a command-line tool and get back what it printed and its exit code.
const r = await macotron.shell.run("/usr/bin/defaults", ["read", "-g", "AppleInterfaceStyle"]);
const dark = r.exitCode === 0 && r.stdout.trim() === "Dark";
Running commands
run(command: string, args?: string[]): Promise<{ stdout: string; stderr: string; exitCode: number }>
Runs the command with /bin/zsh -c, waits for it to exit, and resolves with its standard output, standard error, and exit status. The command runs off the main thread, so a slow tool does not freeze other plugins.
The promise does not reject when the tool fails. Check exitCode: 0 means success. A tool that cannot start at all also resolves, with a non-zero exitCode and the shell's error in stderr.
How the command line is built
Macotron puts each item of args in single quotes and appends it to command. The shell then runs the result. This has three effects:
argsare always passed through literally. Spaces, quotes,$, and*in an argument are safe, so put file names and user input there.commandis not quoted. The shell reads it as shell code, so pipes, redirects, globs, and$VARIABLESwork in it. Never buildcommandfrom text you did not write.~expands incommandbut not inargs. Pass absolute paths inargs.
// One fixed pipeline, no user input:
await macotron.shell.run("/usr/bin/pmset -g batt | /usr/bin/grep -o '[0-9]*%'");
// User input goes in args:
await macotron.shell.run("/usr/bin/mdfind", ["-name", query]);
Environment
Macotron is not sandboxed, and run() has no path rules or allow list. A command runs as your user, with the same file access as Macotron, and with Macotron's environment. It runs in a non-interactive, non-login shell, so zsh reads ~/.zshenv but not ~/.zshrc or ~/.zprofile. An app opened from the Finder gets only the system PATH (/usr/bin:/bin:/usr/sbin:/sbin), so call tools by full path.
There is no timeout. A tool that never exits keeps running, and its promise never resolves.
Plugins must work on a stock Mac. Call tools that ship with macOS, such as /usr/bin/open, /usr/bin/defaults, or /usr/bin/osascript, and not tools installed with Homebrew or npm.
See also
- macotron.fs to read and write files without a subprocess.
- macotron.shortcuts to run Shortcuts.
- The
time-machine.jsanddisk-usage.jsplugins, which parse the output oftmutil,df, anddu.