Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Run several commands concurrently. Show output for one command at a time. Kill all at once.
run-pty
is a command line tool that lets you run several commands concurrently and interactively. Show output for one command at a time. Kill all at once.
It’s like concurrently but the command outputs aren’t mixed, and you can restart commands individually and interact with them. I bet you can do the same with tmux if you – and your team mates – feel like installing and learning it. In bash
you can use command1 & command2
together with fg
, bg
, jobs
and ctrl+z to achieve a similar result, but run-pty tries to be easier to use, and cross-platform.
ctrl+z shows the dashboard, which gives you an overview of all your running commands and lets you switch between them.
ctrl+c kills commands.
A use case is running several watchers. Maybe one or two for frontend (webpack, Parcel, Vite), and one for backend (nodemon, or even some watcher for another programming language).
Another use case is running a couple of commands in parallel, using --auto-exit.
{
"scripts": {
"start": "run-pty % npm run frontend % npm run backend",
"frontend": "parcel watch index.html",
"backend": "nodemon server.js"
}
}
$ npm start
> start
> run-pty % npm run frontend % npm run backend
➡️
[1] 🟢 npm run frontend
[2] 🟢 npm run backend
[1-2] focus command (or click)
[ctrl+c] kill all
[↑↓←→] move selection
➡️ 1 ️️➡️
🟢 npm run frontend
> frontend
> vite --no-clearScreen
VITE v5.1.6 ready in 81 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
▊
[ctrl+c] kill (pid 36842)
[ctrl+z] dashboard
➡️ ctrl+c ➡️
🟢 npm run frontend
> frontend
> vite --no-clearScreen
VITE v5.1.6 ready in 81 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
^C
⚪ npm run frontend
exit 130
[enter] restart
[ctrl+c] kill all
[ctrl+z] dashboard
➡️ ctrl+z ➡️
[1] ⚪ exit 130 npm run frontend
[2] 🟢 npm run backend
[1-2] focus command (or click)
[ctrl+c] kill all
[↑↓←→] move selection
[enter] restart exited
➡️ ctrl+c ➡️
[1] ⚪ exit 130 npm run frontend
[2] ⚪ exit 130 npm run backend
$ ▊
npm install --save-dev run-pty
npx run-pty --help
The above example called run-pty
like so:
run-pty % npm run frontend % npm run backend
Instead of defining the commands at the command line, you can define them in a JSON file:
run-pty.json:
[
{
"command": ["npm", "run", "frontend"]
},
{
"command": ["npm", "run", "backend"]
}
]
run-pty run-pty.json
(The JSON file can be called anything – you specify the path to it on the command line.)
The JSON format lets you specify additional things apart from the command itself.
Key | Type | Default | Description |
---|---|---|---|
command | Array<string> | Required | The command to run. Must not be empty. |
title | string | command as a string | What to show in the dashboard. |
cwd | string | "." | Current working directory for the command. |
status | { [regex: string]: [string, string] | null } | {} | Customize the status of the command in the dashboard. |
defaultStatus | [string, string] | null | null | Customize the default status of the command in the dashboard. |
killAllSequence | string | "\u0003" | Sequence to send to the command when using “kill all”. The default is the escape code for ctrl+c. |
command: On the command line, you let your shell split the commands into arguments. In the JSON format, you need to do it yourself. For example, if you had run-pty % npm run frontend
on the command line, the JSON version of it is ["npm", "run", "frontend"]
. And run-pty % echo 'hello world'
would be ["echo", "hello world"]
. See also: Shell scripting.
title: If you have complicated commands, it might be hard to find what you’re looking for in the dashboard. This lets you use more human readable titles instead. The titles are also shown when you focus a command (before the command itself).
cwd: This is handy if you need to run some command as if you were in a subdirectory. When focusing a command, the cwd
is shown below the title/command (unless it’s "."
(the CWD of the run-pty
process itself) or equal to the title):
🟢 Custom title: npm run something
📂 my/cwd/path
status: It’s common to run watchers in run-pty
. Watchers wrap your program – if your program crashes, the watcher will still be up and running and wait for source code changes so it can restart your program and try again. run-pty
will display a 🟢 in the dashboard (since the watcher is successfully running), which makes things look all green. But in reality things are broken. status
lets you replace 🟢 with custom status indicators, such as 🚨 to indicate an error.
The keys in the object are regexes with the u
flag.
The values are either a tuple with two strings or null
.
For each line of output, run-pty
matches all the regexes from top to bottom. For every match, the status indicator is set to the corresponding value. If several regexes match, the last match wins. Graphic renditions are stripped before matching.
This is how the value ([string, string] | null
) is used:
NO_COLOR
environment variable is set. In NO_COLOR
mode, graphic renditions are stripped as well. So you can use ANSI codes (in either string) to make your experience more colorful while still letting people have monochrome output if they prefer. Unlike the first string, the second string is drawn in 1 character slot in the terminal. (Windows – except the newer Windows Terminal – does not support emojis in the terminal very well, and for NO_COLOR
you might not want colored emojis, so a single character should do.)null
resets the indicator to the standard 🟢 one (not defaultStatus
).defaultStatus: This lets you replace 🟢 with a custom status indicator at startup (before your command has written anything). The value works like for status
.
killAllSequence: When you use “kill all” (or “restart selected”) run-pty sends ctrl+c to all commands. However, not all commands exit when you do that. In such cases, you can use killAllSequence
to specify what sequence of characters to send to the command to make it exit.
If you want to run a couple of commands in parallel and once they’re done continue with something else, use --auto-exit
:
run-pty --auto-exit % npm ci % dotnet restore && node build.js
--auto-exit
mode degrades to a simpler, non-interactive UI.To limit how many commands run in parallel, use for example --auto-exit=5
. Just --auto-exit
is the same as --auto-exit=auto
, which uses the number of logical CPU cores.
Note: --auto-exit
is for conveniently running a couple of commands in parallel and get to know once they are done. I don’t want the feature to grow to GNU Parallel levels of complexity.
Let’s say you run run-pty % npm run $command
on the command line. If the command
variable is set to frontend
, the command actually executed is run-pty % npm run frontend
– run-pty receives ["%", "npm", "run", "frontend"]
as arguments (and has no idea that frontend
came from a variable initially). This is all thanks to your shell – which is assumed to be a bash-like shell here; the syntax for Windows’ cmd.exe
would be different, for example.
If you try to put that in a JSON file as "command": ["npm", "run", "$command"]
, run-pty is going to try to execute npm
with the literal strings run
and $command
, so npm
receives ["run", "$command"]
as arguments. There’s no shell in play here.
Another example: Let’s say you wanted a command to first run npm install
and then run npm start
to start a server or something. If you run run-pty % npm install && npm start
from the command line, it actually means “first run run-pty % npm install
and once that’s done (and succeeded), run npm start
”, which is not what you wanted. You might try to fix that by using escapes: run-pty % npm install \&\& npm start
. However, run-pty is then going to execute npm
with ["install", "&&", "npm", "start"]
as arguments. There’s no shell in play here either.
run-pty only executes programs with an array of literal strings as arguments.
If you want a shell, you could do something like this: run-pty % bash -c 'npm install && npm start'
or "command": ["bash", "-c", "npm run \"$command\""]
. You can also but that in a file, like my-script.bash
, and use run-pty % ./my-script.bash
or "command": ["./my-script.bash"]
. If you need cross-platform support (or get tired of bash), you could instead use run-pty % node my-script.js
or "command": ["node", "my-script.js"]
.
iTerm2 has a bug where the window flickers when clearing the screen without GPU rendering: https://gitlab.com/gnachman/iterm2/-/issues/7677
GPU rendering seems to be enabled by default, as long as your computer is connected to power.
You can enable GPU rendering always by toggling “Preferences > General > Magic > GPU Rendering + Advanced GPU Settings… > Disable GPU rendering when disconnected from power.”
run-pty tries to avoid clearing the screen and only redraw lines that have changed, but there might still be occasional flicker. Hopefully the iTerm2 developers will improve this some time. It does not happen in the standard Terminal app.
MIT.
FAQs
Run several commands concurrently. Show output for one command at a time. Kill all at once.
We found that run-pty demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.