npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@trevthedev/shell-harness

v1.0.16

Published

queues and runs shell scripts

Downloads

2

Readme

Introduction

Shell Harness provides an easy way to interact with shell commands and scripts. Benefits:

  • an initial script can be run to set context for all subsequent commands
  • all commands run in a long-running Child Processes vs. a Child Processes for each command. This can be significantly faster than spawning new processes for each command. 36 times faster in one benchmark.
  • Multiple shells can be spawned to run commands in parallel
  • Commands are queued in a FIFO manner

Installation

npm install @trevthedev/shell-harness

How To

Launch a shell(s)

import ShellHarness from '@trevthedev/shell-harness'
const shell = new ShellHarness()

Send a command

const cmd = await shell.createCommand('printf HELLO ;')
console.log(cmd.output) // HELLO

All shell commands or scripts must terminate with a semi-colon ;

All stderr output is redirected to stdout via 2>&1 and not command should send data to stderr (required as Node does not process stdout and stderr sequentially)

Send a command to all shells

const cmd = await shell.createCommand('cd / ;', undefined, undefined, true)

returns a promise that will resolve once the command has been executed on all shells ;

Interact with the shell

const cmdline = shell.interact('echo "what is your name?" ; read name;\n')
cmdline.on('data', stdout => {
  if (stdout === 'what is your name?\n') {
    cmdline.stdin.write('Bob\necho $name\n ')
    cmdline.sendDoneMarker() // required to indicate that this interaction is completed
  } else {
    console.log(stdout) // `Bob\n`
  }
})
const outcome = await cmdline
console.log(outcome.output) // `what is your name?\nBob\n`

Get a root or other user shell

const rootShell = new ShellHarness({
  user: 'root', // or other user
  rootPassword: process.env.RPASSWORD
})
const cmd = await rootShell.createCommand('whoami;')
console.log(cmd.output) // 'root\n'
console.log(rootShell.config.rootPassword) // undefined

This will switch to another user via sudo and a sudo'er password is required - unfortunately shell does not allow one to su without sudo.

Intercept and replace output

The output from a command can be intercepted in two ways and different result substituted if required.

const cb = (cmdX, cbData) => {
  console.log(cbData) // 'HIT'
  return true
}
const cmd = await shell.createCommand('printf HELLO ;', 'HIT', cb)
console.log(cmd) // true

or

const cb = (cmdX, cbData) => {
  console.log(cbData) // 'HIT'
  return true
}
const cbShell = new ShellHarness({
  doneCallback: cb
})
const cmd = await cbShell.createCommand('printf HELLO ;', 'HIT')
console.log(cmd) // true
cbShell.close()

Receive data via IPC

const cmd = shell.interact(
  'printf "{\\"ipc\\": \\"true\\"}\\n" 1>&$NODE_CHANNEL_FD ; printf HELLO ; \n'
)
cmd.on('message', data => {
  console.log(data) // { ipc: "true" }
  cmd.sendDoneMarker() // required to finished command
})
const res = await cmd
console.log(res.output) // `HELLO`

Send data via IPC

const cmd = shell.interact('echo ; \n')
cmd.on('data', stdout => {
  if (stdout === '\n') {
    cmd.sendMessage('HELLOBOB')
    cmd.stdin.write('read -r line <&3 ; printf $line ; \n')
  } else cmd.sendDoneMarker() // required to finished command
})
const res = await cmd
console.log(res.output) // \n"HELLOBOB"`

Node formats messages sent via IPC

Configuration

{
  shell: '/bin/sh',        // shell to use
  initScript: undefined,   // script to run when shell is first launched

  doneCallback: undefined, // callback before any command is completed

  user: undefined,         // user to switch to via sudo then su
  rootPassword: undefined, // root password to switch user

  numberOfProcesses: 1,    // number of shells to spawn
  concurrentCmds: 100,     // max number of commands to send to the shell at a time per shell

  doneMarker: '__done__',  // unique identifier to determine whether command is completed - a sequential number is also added

  log: true                // produce a log
}

API

const shell = new ShellHarness(optionalConfig)

const cmd = shell.createCommand(
  command,
  optionalDoneCallBackPayload,
  optionalDoneCallback,
  sendToEveryShell
)

const cmd = shell.interact(
  command,
  optionalDoneCallBackPayload,
  optionalDoneCallback
)

cmd.on('data', data => {})
cmd.on('message', message => {})

// other events include: created, enqueued, executing, cancelled, failed and finished

const result = await cmd
// result = {
//  error: whether shell exited in error,
//  command: the command sent,
//  output: string result }