New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

run-pty

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

run-pty - npm Package Compare versions

Comparing version 1.0.1 to 2.0.0

CHANGELOG.md

19

package.json
{
"name": "run-pty",
"version": "1.0.1",
"version": "2.0.0",
"author": "Simon Lydell",

@@ -28,16 +28,19 @@ "license": "MIT",

"dependencies": {
"colorette": "^1.2.1",
"node-pty": "^0.9.0"
},
"devDependencies": {
"eslint": "7.4.0",
"eslint-plugin-jest": "23.18.0",
"jest": "26.1.0",
"prettier": "2.0.5"
"@types/jest": "26.0.8",
"@typescript-eslint/eslint-plugin": "3.7.1",
"@typescript-eslint/parser": "3.7.1",
"eslint": "7.6.0",
"eslint-plugin-jest": "23.20.0",
"jest": "26.2.2",
"prettier": "2.0.5",
"typescript": "3.9.7"
},
"scripts": {
"start": "node run-pty.js % cat % false % echo hello world % ping localhost % node test-keys.js",
"test": "prettier --check . && eslint . && jest",
"start": "node run-pty.js % cat % false % echo hello world % ping localhost % node test-keys.js % node signals.js % node slow-kill.js % node slow-kill.js 2000 \"Shutting down…\" % make watch",
"test": "prettier --check . && eslint . && tsc && jest",
"prepublishOnly": "npm test"
}
}
# run-pty
In `bash` you can use `fg`, `bg`, `jobs` and <kbd>ctrl+z</kbd> to run several commands at the same time in the same terminal. But it’s not very user friendly.
`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. Nothing more, nothing less.
`run-pty` is a command line tool that lets you run several commands concurrently. Show output for one command at a time. Kill all at once. Nothing more, nothing less.
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 <kbd>ctrl+z</kbd> to achieve a similar result, but run-pty tries to be easier to use, and cross-platform.
<kbd>ctrl+z</kbd> shows the _dashboard,_ which gives you an overview of all your running commands and lets you switch between them.
<kbd>ctrl+c</kbd> exits current/all commands.
<kbd>ctrl+c</kbd> kills commands.
A use case is running several watchers. Maybe one or two for frontend (webpack, Parcel, Sass), and one for backend (nodemon, TypeScript, or even some watcher for another programming language).
A use case is running several watchers. Maybe one or two for frontend (webpack, Parcel, Sass), and one for backend (nodemon, or even some watcher for another programming language).
## Example
```json
{
"scripts": {
"watch:frontend": "webpack-dev-server",
"watch:backend": "nodemon server/index.ts",
"watch:all": "run-pty % npm run watch:frontend % npm run watch:backend"
"start": "run-pty % npm run frontend % npm run backend",
"frontend": "parcel watch index.html",
"backend": "nodemon server.js"
}

@@ -24,10 +26,81 @@ }

```
1 🟢 pid 78147 npm run 'watch:frontend'
2 🔴 exit 1 npm run 'watch:backend'
$ npm start
1-2 switch command
ctrl+c exit current/all
ctrl+z this dashboard
> @ start /Users/lydell/src/run-pty/demo
> run-pty % npm run frontend % npm run backend
```
➡️
```
[1] 🟢 pid 11084 npm run frontend
[2] 🟢 pid 11085 npm run backend
[1-2] focus command
[ctrl+c] kill all
```
➡️ <kbd>1</kbd> ️️➡️
```
🟢 npm run frontend
> @ frontend /Users/lydell/src/run-pty/demo
> parcel watch index.html --log-level 4
[9:51:27 AM]: Building...
[9:51:27 AM]: Building index.html...
[9:51:27 AM]: Built index.html...
[9:51:27 AM]: Producing bundles...
[9:51:27 AM]: Packaging...
[9:51:27 AM]: ✨ Built in 67ms.
[ctrl+c] kill
[ctrl+z] dashboard
```
➡️ <kbd>ctrl+c</kbd> ➡️
```
🟢 npm run frontend
> @ frontend /Users/lydell/src/run-pty/demo
> parcel watch index.html --log-level 4
[9:51:27 AM]: Building...
[9:51:27 AM]: Building index.html...
[9:51:27 AM]: Built index.html...
[9:51:27 AM]: Producing bundles...
[9:51:27 AM]: Packaging...
[9:51:27 AM]: ✨ Built in 67ms.
⚪ npm run frontend
exit 0
[enter] restart
[ctrl+c] kill all
[ctrl+z] dashboard
```
➡️ <kbd>ctrl+z</kbd> ➡️
```
[1] ⚪ exit 0 npm run frontend
[2] 🟢 pid 11085 npm run backend
[1-2] focus command
[ctrl+c] kill all
```
➡️ <kbd>ctrl+c</kbd> ➡️
```
[1] ⚪ exit 0 npm run frontend
[2] ⚪ exit 0 npm run backend
$ ▊
```
## Installation

@@ -44,2 +117,12 @@

## iTerm2 flicker
[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.”
There might still be occasional flicker. Hopefully the iTerm2 developers will improve this some time. It does not happen in the standard Terminal app.
## License

@@ -49,3 +132,6 @@

[apiel/run-screen]: https://github.com/apiel/run-screen
[concurrently]: https://github.com/kimmobrunfeldt/concurrently
[iterm2]: https://www.iterm2.com/
[microsoft/node-pty]: https://github.com/microsoft/node-pty
[apiel/run-screen]: https://github.com/apiel/run-screen
[tmux]: https://github.com/tmux/tmux

@@ -5,8 +5,35 @@ #!/usr/bin/env node

const colorette = require("colorette");
const pty = require("node-pty");
/**
* @typedef {
| { tag: "Running", terminal: import("node-pty").IPty }
| { tag: "Killing", terminal: import("node-pty").IPty, slow: boolean }
| { tag: "Exit", exitCode: number }
} Status
*
* @typedef {
| { tag: "Command", index: number }
| { tag: "Dashboard" }
} Current
*/
// node-pty does not support kill signals on Windows.
// This is the same check that node-pty uses.
const IS_WINDOWS = process.platform === "win32";
const MAX_HISTORY_DEFAULT = 1000000;
const MAX_HISTORY = (() => {
const env = process.env.RUN_PTY_MAX_HISTORY;
return env !== undefined && /^\d+$/.test(env)
? Number(env)
: MAX_HISTORY_DEFAULT;
})();
const NO_COLOR = "NO_COLOR" in process.env;
const KEYS = {
kill: "ctrl+c",
restart: "enter ", // Extra space for alignment.
restart: "enter",
dashboard: "ctrl+z",

@@ -25,12 +52,69 @@ };

const HIDE_CURSOR = "\x1B[?25l";
const SHOW_CURSOR = "\x1B[?25h";
const DISABLE_ALTERNATE_SCREEN = "\x1B[?1049l";
const DISABLE_BRACKETED_PASTE_MODE = "\x1B[?2004l";
const RESET_COLOR = "\x1B[0m";
const CLEAR = IS_WINDOWS ? "\x1B[2J\x1B[0f" : "\x1B[2J\x1B[3J\x1B[H";
const runningIndicator = "🟢";
const killingIndicator = "⭕";
/**
* @param {number} exitCode
* @returns {string}
*/
const exitIndicator = (exitCode) => (exitCode === 0 ? "⚪" : "🔴");
const shortcut = (string) => colorette.blue(colorette.bold(string));
/**
* @param {string} string
* @returns {string}
*/
const bold = (string) => (NO_COLOR ? string : `\x1B[1m${string}${RESET_COLOR}`);
const runPty = shortcut("run-pty");
const pc = colorette.gray("%");
const at = colorette.gray("@");
/**
* @param {string} string
* @returns {string}
*/
const dim = (string) => (NO_COLOR ? string : `\x1B[2m${string}${RESET_COLOR}`);
/**
* @param {string} string
* @param {{ pad?: boolean }} pad
*/
const shortcut = (string, { pad = true } = {}) =>
dim("[") +
bold(string) +
dim("]") +
(pad ? " ".repeat(Math.max(0, KEYS.kill.length - string.length)) : "");
const runPty = bold("run-pty");
const pc = dim("%");
const at = dim("@");
/**
* @param {Array<string>} labels
* @returns {string}
*/
const summarizeLabels = (labels) => {
const numLabels = labels.length;
return LABEL_GROUPS.flatMap((group, index) => {
const previousLength = LABEL_GROUPS.slice(0, index).reduce(
(sum, previousGroup) => sum + previousGroup.length,
0
);
const currentLength = previousLength + group.length;
return numLabels > previousLength
? numLabels < currentLength
? group.slice(0, numLabels - previousLength)
: group
: [];
})
.map((group) =>
group.length === 1 ? group[0] : `${group[0]}-${group[group.length - 1]}`
)
.join("/");
};
const help = `

@@ -41,6 +125,6 @@ Run several commands concurrently.

${shortcut(summarizeLabels(ALL_LABELS.split("")))} switch command
${shortcut(summarizeLabels(ALL_LABELS.split("")))} focus command
${shortcut(KEYS.dashboard)} dashboard
${shortcut(KEYS.kill)} exit current/all
${shortcut(KEYS.restart)} restart exited command
${shortcut(KEYS.kill)} kill focused/all
${shortcut(KEYS.restart)} restart killed/exited command

@@ -54,9 +138,35 @@ Separate the commands with a character of choice:

Note: All arguments are strings and passed as-is – no shell script execution.
Use ${bold("sh -c '...'")} or similar if you need that.
Environment variables:
${bold("RUN_PTY_MAX_HISTORY")}
Number of characters of output to remember.
Higher → more command scrollback
Lower → faster switching between commands
Default: ${MAX_HISTORY_DEFAULT}
${bold("NO_COLOR")}
Disable colored output.
`.trim();
function drawDashboard(commands, width) {
/**
* @param {Array<Command>} commands
* @returns {string}
*/
const killAllLabel = (commands) =>
commands.some((command) => command.status.tag === "Killing")
? "force kill all"
: commands.every((command) => command.status.tag === "Exit")
? "exit"
: "kill all";
/**
* @param {Array<Command>} commands
* @param {number} width
* @param {boolean} attemptedKillAll
*/
const drawDashboard = (commands, width, attemptedKillAll) => {
const lines = commands.map((command) => [
colorette.bgWhite(
colorette.black(colorette.bold(` ${command.label || " "} `))
),
shortcut(command.label || " ", { pad: false }),
statusText(command.status),

@@ -79,18 +189,54 @@ command.name,

if (
attemptedKillAll &&
commands.every((command) => command.status.tag === "Exit")
) {
return `${finalLines}\n`;
}
// Newlines at the end are wanted here.
return `
${finalLines}
${shortcut(padEnd(label, KEYS.kill.length))} switch command
${shortcut(KEYS.kill)} exit current/all
${shortcut(KEYS.dashboard)} this dashboard
`.trim();
}
${shortcut(label)} focus command
${shortcut(KEYS.kill)} ${killAllLabel(commands)}
`.trimStart();
};
function firstHistoryLine(name) {
return `${runningIndicator} ${name}\n`;
}
/**
* @param {string} name
* @returns {string}
*/
const firstHistoryLine = (name) => `${runningIndicator} ${name}\n`;
// Newlines at start/end are wanted here.
function exitText(commandName, exitCode) {
return `
// Newlines at the start/end are wanted here.
const runningText = `
${shortcut(KEYS.kill)} kill
${shortcut(KEYS.dashboard)} dashboard
`;
/**
* @param {string} commandName
* @returns {string}
*/
const killingText = (commandName) =>
// Newlines at the start/end are wanted here.
`
${killingIndicator} ${commandName}
killing…
${shortcut(KEYS.kill)} force kill
${shortcut(KEYS.dashboard)} dashboard
`;
/**
* @param {Array<Command>} commands
* @param {string} commandName
* @param {number} exitCode
* @returns {string}
*/
const exitText = (commands, commandName, exitCode) =>
// Newlines at the start/end are wanted here.
`
${exitIndicator(exitCode)} ${commandName}

@@ -100,8 +246,11 @@ exit ${exitCode}

${shortcut(KEYS.restart)} restart
${shortcut(KEYS.kill)} exit all
${shortcut(KEYS.kill)} ${killAllLabel(commands)}
${shortcut(KEYS.dashboard)} dashboard
`;
}
function statusText(status) {
/**
* @param {Status} status
* @returns {string}
*/
const statusText = (status) => {
switch (status.tag) {

@@ -111,21 +260,34 @@ case "Running":

case "Killing":
return `${killingIndicator} pid ${status.terminal.pid}`;
case "Exit":
return `${exitIndicator(status.exitCode)} exit ${status.exitCode}`;
default:
throw new Error("Unknown command status", status);
}
}
};
function removeColor(string) {
/**
* @param {string} string
* @returns {string}
*/
const removeColor = (string) =>
// eslint-disable-next-line no-control-regex
return string.replace(/\x1b\[\d+m/g, "");
}
string.replace(/\x1B\[\d+m/g, "");
function truncate(string, maxLength) {
/**
* @param {string} string
* @param {number} maxLength
* @returns {string}
*/
const truncate = (string, maxLength) => {
const diff = removeColor(string).length - maxLength;
return diff <= 0 ? string : `${string.slice(0, -(diff + 2))}…`;
}
};
function padEnd(string, maxLength) {
/**
* @param {string} string
* @param {number} maxLength
* @returns {string}
*/
const padEnd = (string, maxLength) => {
const chars = Array.from(string);

@@ -137,34 +299,28 @@ return chars

.join("");
}
};
function commandToPresentationName(command) {
return command
/**
* @param {Array<string>} command
* @returns {string}
*/
const commandToPresentationName = (command) =>
command
.map((part) =>
/^[\w./-]+$/.test(part) ? part : `'${part.replace(/'/g, "’")}'`
/^[\w.,:/=@%+-]+$/.test(part) ? part : `'${part.replace(/'/g, "’")}'`
)
.join(" ");
}
function summarizeLabels(labels) {
const numLabels = labels.length;
return LABEL_GROUPS.map((group, index) => {
const previousLength = LABEL_GROUPS.slice(0, index).reduce(
(sum, previousGroup) => sum + previousGroup.length,
0
);
const currentLength = previousLength + group.length;
return numLabels > previousLength
? numLabels < currentLength
? group.slice(0, numLabels - previousLength)
: group
: undefined;
})
.filter(Boolean)
.map((group) =>
group.length === 1 ? group[0] : `${group[0]}-${group[group.length - 1]}`
)
.join("/");
}
/**
* @typedef {
| { tag: "Help" }
| { tag: "Error", message: string }
| { tag: "Parsed", commands: Array<Array<string>> }
} ParseResult
*/
function parseArgs(args) {
/**
* @param {Array<string>} args
* @returns {ParseResult}
*/
const parseArgs = (args) => {
if (args.length === 0 || args[0] === "-h" || args[0] === "--help") {

@@ -216,5 +372,14 @@ return { tag: "Help" };

};
}
};
class Command {
/**
* @param {{
label: string,
file: string,
args: Array<string>,
onData: (data: string) => undefined,
onExit: () => undefined,
}} commandInit
*/
constructor({ label, file, args, onData, onExit }) {

@@ -227,3 +392,5 @@ this.label = label;

this.onExit = onExit;
this.history = [];
/** @type {string} */
this.history = "";
/** @type {Status} */
this.status = { tag: "Exit", exitCode: 0 };

@@ -233,10 +400,13 @@ this.start();

/**
* @returns {void}
*/
start() {
if (this.status.tag === "Running") {
if (this.status.tag !== "Exit") {
throw new Error(
`pty already running with pid ${this.status.terminal.pid} for: ${this.name}`
`Cannot start pty with pid ${this.status.terminal.pid} because not exited for: ${this.name}`
);
}
this.history = [firstHistoryLine(this.name)];
this.history = firstHistoryLine(this.name);

@@ -249,3 +419,3 @@ const terminal = pty.spawn(this.file, this.args, {

const disposeOnData = terminal.onData((data) => {
this.history.push(data);
this.pushHistory(data);
this.onData(data);

@@ -258,3 +428,3 @@ });

this.status = { tag: "Exit", exitCode };
this.onExit(exitCode);
this.onExit();
});

@@ -265,39 +435,147 @@

log(data) {
this.history.push(data);
this.onData(data);
/**
* @returns {undefined}
*/
kill() {
// https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html
switch (this.status.tag) {
case "Running":
this.status = {
tag: "Killing",
terminal: this.status.terminal,
slow: false,
};
setTimeout(() => {
if (this.status.tag === "Killing") {
this.status.slow = true;
// Ugly way to redraw:
this.onData("");
}
}, 100);
if (IS_WINDOWS) {
this.status.terminal.kill();
} else {
// SIGHUP causes a silent exit for `npm run`.
this.status.terminal.kill("SIGHUP");
// SIGTERM is needed for some programs (but is noisy for `npm run`).
this.status.terminal.kill("SIGTERM");
}
return undefined;
case "Killing":
if (IS_WINDOWS) {
this.status.terminal.kill();
} else {
this.status.terminal.kill("SIGKILL");
}
return undefined;
case "Exit":
throw new Error(`Cannot kill already exited pty for: ${this.name}`);
}
}
/**
* @param {string} data
* @returns {void}
*/
pushHistory(data) {
this.history += data;
if (this.history.length > MAX_HISTORY) {
this.history = this.history.slice(-MAX_HISTORY);
}
}
}
function runCommands(rawCommands) {
/**
* @param {Array<Array<string>>} rawCommands
*/
const runCommands = (rawCommands) => {
/** @type {Current} */
let current = { tag: "Dashboard" };
let attemptedKillAll = false;
const printHistory = (command) => {
for (const data of command.history) {
process.stdout.write(data);
/**
* @param {Command} command
* @returns {undefined}
*/
const printHistoryAndExtraText = (command) => {
process.stdout.write(
SHOW_CURSOR +
DISABLE_ALTERNATE_SCREEN +
RESET_COLOR +
CLEAR +
command.history
);
switch (command.status.tag) {
case "Running":
if (command.history.endsWith("\n")) {
process.stdout.write(RESET_COLOR + runningText);
}
return undefined;
case "Killing":
if (command.status.slow) {
process.stdout.write(
HIDE_CURSOR + RESET_COLOR + killingText(command.name)
);
}
return undefined;
case "Exit":
process.stdout.write(
HIDE_CURSOR +
RESET_COLOR +
exitText(commands, command.name, command.status.exitCode)
);
return undefined;
}
};
/**
* @returns {void}
*/
const switchToDashboard = () => {
current = { tag: "Dashboard" };
console.clear();
console.log(drawDashboard(commands, process.stdout.columns));
process.stdout.write(
HIDE_CURSOR +
DISABLE_ALTERNATE_SCREEN +
RESET_COLOR +
CLEAR +
drawDashboard(commands, process.stdout.columns, attemptedKillAll)
);
};
/**
* @param {number} index
* @returns {void}
*/
const switchToCommand = (index) => {
const command = commands[index];
current = { tag: "Command", index };
console.clear();
printHistory(command);
printHistoryAndExtraText(command);
};
/**
* @returns {void}
*/
const killAll = () => {
for (const command of commands) {
if (command.status.tag === "Running") {
command.status.terminal.kill();
attemptedKillAll = true;
const notExited = commands.filter(
(command) => command.status.tag !== "Exit"
);
if (notExited.length === 0) {
switchToDashboard();
process.exit(0);
} else {
for (const command of notExited) {
command.kill();
}
// So you can see how killing other commands go:
switchToDashboard();
}
process.exit(0);
};
/** @type {Array<Command>} */
const commands = rawCommands.map(

@@ -311,12 +589,46 @@ ([file, ...args], index) =>

if (current.tag === "Command" && current.index === index) {
process.stdout.write(data);
const command = commands[current.index];
switch (command.status.tag) {
case "Running":
process.stdout.write(data);
return undefined;
case "Killing":
// Redraw with killingText at the bottom.
printHistoryAndExtraText(command);
return undefined;
case "Exit":
throw new Error(
`Received unexpected output from already exited pty for: ${command.name}\n${data}`
);
}
} else {
return undefined;
}
},
onExit: (exitCode) => {
const command = commands[index];
command.log(exitText(command.name, exitCode));
if (current.tag === "Dashboard") {
// Redraw dashboard.
onExit: () => {
// Exit the whole program if all commands are killed.
if (
attemptedKillAll &&
commands.every((command2) => command2.status.tag === "Exit")
) {
switchToDashboard();
process.exit(0);
}
switch (current.tag) {
case "Command":
if (current.index === index) {
const command = commands[index];
// Redraw current command.
printHistoryAndExtraText(command);
}
return undefined;
case "Dashboard":
// Redraw dashboard.
switchToDashboard();
return undefined;
}
},

@@ -343,7 +655,6 @@ })

process.stdin.setRawMode(true);
process.stdin.setEncoding("utf8");
process.stdin.on("data", (data) => {
onStdin(
data,
data.toString("utf8"),
current,

@@ -354,6 +665,34 @@ commands,

killAll,
printHistory
printHistoryAndExtraText
);
});
// Clean up all commands if someone tries to kill run-pty.
for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"]) {
process.on(signal, killAll);
}
// Don’t leave running processes behind in case of an unexpected error.
for (const event of ["uncaughtException", "unhandledRejection"]) {
process.on(event, (error) => {
console.error(error);
for (const command of commands) {
if (command.status.tag !== "Exit") {
if (IS_WINDOWS) {
command.status.terminal.kill();
} else {
command.status.terminal.kill("SIGKILL");
}
}
}
process.exit(1);
});
}
process.on("exit", () => {
process.stdout.write(
SHOW_CURSOR + DISABLE_BRACKETED_PASTE_MODE + RESET_COLOR
);
});
if (commands.length === 1) {

@@ -364,5 +703,15 @@ switchToCommand(0);

}
}
};
function onStdin(
/**
* @param {string} data
* @param {Current} current
* @param {Array<Command>} commands
* @param {() => void} switchToDashboard
* @param {(index: number) => void} switchToCommand
* @param {() => void} killAll
* @param {(command: Command) => void} printHistoryAndExtraText
* @returns {undefined}
*/
const onStdin = (
data,

@@ -374,4 +723,4 @@ current,

killAll,
printHistory
) {
printHistoryAndExtraText
) => {
switch (current.tag) {

@@ -384,15 +733,28 @@ case "Command": {

case KEY_CODES.kill:
command.status.terminal.kill();
break;
command.kill();
return undefined;
case KEY_CODES.dashboard:
switchToDashboard();
break;
return undefined;
default:
command.status.terminal.write(data);
break;
return undefined;
}
break;
case "Killing":
switch (data) {
case KEY_CODES.kill:
command.kill();
return undefined;
case KEY_CODES.dashboard:
switchToDashboard();
return undefined;
default:
return undefined;
}
case "Exit":

@@ -402,20 +764,17 @@ switch (data) {

killAll();
break;
return undefined;
case KEY_CODES.dashboard:
switchToDashboard();
break;
return undefined;
case KEY_CODES.restart:
command.start();
command.history.unshift("\n");
printHistory(command);
break;
printHistoryAndExtraText(command);
return undefined;
default:
return undefined;
}
break;
default:
throw new Error("Unknown command status", command);
}
break;
}

@@ -427,3 +786,3 @@

killAll();
break;
return undefined;

@@ -437,13 +796,12 @@ default: {

}
break;
return undefined;
}
}
break;
default:
throw new Error("Unknown current", current);
}
}
};
function run() {
/**
* @returns {undefined}
*/
const run = () => {
if (!process.stdin.isTTY) {

@@ -462,7 +820,6 @@ console.error(

process.exit(0);
break;
case "Parsed":
runCommands(parseResult.commands);
break;
return undefined;

@@ -472,11 +829,6 @@ case "Error":

process.exit(1);
break;
default:
console.error("Unknown parseResult", parseResult);
process.exit(1);
break;
}
}
};
// @ts-ignore
if (require.main === module) {

@@ -491,3 +843,2 @@ run();

drawDashboard,
exitText,
help,

@@ -494,0 +845,0 @@ parseArgs,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc