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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mylocalgpt/shell

v0.1.0

Published

Virtual bash interpreter for AI agents. Pure TypeScript, zero dependencies.

Readme

@mylocalgpt/shell

Virtual bash interpreter for AI agents. Pure TypeScript, zero runtime dependencies. Runs in any JavaScript runtime - browsers, Node.js, Deno, Bun, and Cloudflare Workers. Ships with 65+ commands, a full jq implementation, and an under 40KB gzipped entry point.

  • Pure JS, under 40KB gzipped, zero dependencies, runs anywhere
  • 65+ commands including grep, sed, awk, find, xargs, curl, and a full jq implementation
  • Pipes, redirections, variables, control flow, functions, arithmetic
  • Configurable execution limits, regex guardrails, no eval
  • OverlayFs: read-through overlay on real directories with change tracking

Install

npm install @mylocalgpt/shell

Quick Start

import { Shell } from '@mylocalgpt/shell';

const shell = new Shell({
  files: { '/data.json': '{"name": "alice"}' },
});

const result = await shell.exec('cat /data.json | jq .name');
console.log(result.stdout); // "alice"\n

API Reference

Shell Constructor

const shell = new Shell(options?: ShellOptions);

| Option | Type | Description | |--------|------|-------------| | fs | FileSystem | Custom filesystem implementation. When provided, files is ignored. | | files | Record<string, string \| (() => string \| Promise<string>)> | Initial filesystem contents. Values can be strings or lazy-loaded functions. Ignored when fs is provided. | | env | Record<string, string> | Environment variables. Merged with defaults (HOME, USER, PATH, SHELL). | | limits | Partial<ExecutionLimits> | Execution limits. Merged with safe defaults. | | commands | Record<string, CommandHandler> | Custom commands to register. | | onUnknownCommand | (name, args, ctx) => CommandResult | Handler for unregistered commands. | | onOutput | (result) => ExecResult | Post-processing hook for exec results. | | hostname | string | Virtual hostname (used by hostname command). | | username | string | Virtual username (used by whoami command). | | enabledCommands | string[] | Restrict available commands to this allowlist. | | network | NetworkConfig | Network handler for curl. See Network Config. | | onBeforeCommand | (cmd, args) => boolean \| void | Hook before each command (return false to block). | | onCommandResult | (cmd, result) => CommandResult | Hook after each command (can modify result). |

shell.exec(command, options?)

Execute a shell command. Never throws; all errors are returned in the result.

const result = await shell.exec('echo hello', {
  env: { EXTRA: 'value' },   // per-call env vars
  cwd: '/workspace',          // override working directory
  stdin: 'input data',        // provide stdin
  signal: controller.signal,  // AbortSignal for cancellation
  timeout: 5000,              // timeout in milliseconds
});

ExecResult:

interface ExecResult {
  stdout: string;
  stderr: string;
  exitCode: number;
}

State persistence: environment exports, functions, working directory, and filesystem persist across exec() calls. Shell options (set -e, etc.) reset per call.

shell.defineCommand(name, handler)

Register a custom command that works in pipes and redirections.

shell.defineCommand('fetch-url', async (args, ctx) => {
  const response = await fetch(args[0]);
  return { stdout: await response.text(), stderr: '', exitCode: 0 };
});

await shell.exec('fetch-url https://api.example.com | jq .data');

shell.reset()

Clear environment and functions, reset working directory. Filesystem is kept intact.

Accessors

  • shell.fs - the virtual FileSystem instance
  • shell.cwd - current working directory
  • shell.env - environment variables (Map)

Supported Bash Features

  • Pipes and redirections (|, >, >>, <, 2>&1, &>)
  • Variables and expansion ($VAR, ${VAR:-default}, ${VAR/pattern/replace})
  • Control flow (if/elif/else/fi, for/while/until, case/esac)
  • Functions with local variables
  • Arrays and arithmetic ($((expr)))
  • Command substitution ($(cmd), `cmd`)
  • Here documents (<<EOF)
  • Glob expansion (*, ?, [...])
  • Brace expansion ({a,b}, {1..10})
  • set -euo pipefail
  • Subshells ((cmd))
  • Conditional expressions ([[ ]], [ ])

Commands

| Command | Description | |---------|-------------| | cat | Concatenate and print files | | cp | Copy files | | mv | Move/rename files | | rm | Remove files | | mkdir | Create directories | | rmdir | Remove empty directories | | touch | Create empty files or update timestamps | | chmod | Change file permissions | | ln | Create symbolic links | | stat | Display file status | | file | Determine file type | | grep | Search file contents (-i, -v, -c, -l, -n, -r, -w, -E) | | sed | Stream editor (s, d, p, i, a, c, y commands; -i, -n, -e) | | awk | Pattern scanning (print, if/else, variables, field splitting) | | head | Output first lines (-n, -c) | | tail | Output last lines (-n, -c, -f) | | sort | Sort lines (-r, -n, -u, -k, -t) | | uniq | Filter duplicate lines (-c, -d, -u) | | wc | Count lines, words, characters (-l, -w, -c, -m) | | cut | Remove sections from lines (-d, -f, -c) | | tr | Translate characters (-d, -s) | | rev | Reverse lines | | tac | Reverse file line order | | paste | Merge lines from files (-d) | | fold | Wrap lines (-w, -s) | | comm | Compare sorted files | | join | Join lines on a common field | | nl | Number lines | | expand | Convert tabs to spaces | | unexpand | Convert spaces to tabs | | strings | Print printable strings | | column | Format into columns (-t, -s) | | find | Search for files (-name, -type, -path, -maxdepth) | | xargs | Build commands from stdin (-I, -n) | | diff | Compare files | | base64 | Encode/decode base64 (-d) | | md5sum | Compute MD5 hash | | sha1sum | Compute SHA-1 hash | | sha256sum | Compute SHA-256 hash | | expr | Evaluate expressions | | od | Octal dump | | ls | List directory contents (-l, -a, -R, -1) | | pwd | Print working directory | | tree | Display directory tree | | du | Estimate file space usage | | basename | Strip directory from path | | dirname | Strip filename from path | | readlink | Read symbolic link target | | realpath | Resolve canonical path | | echo | Print arguments (-n, -e) | | printf | Format and print | | env | Display environment | | printenv | Print environment variables | | date | Display date/time | | seq | Print number sequence | | hostname | Print hostname | | whoami | Print current user | | which | Locate a command | | tee | Duplicate stdin to file and stdout | | sleep | Pause execution | | yes | Repeat a string (output-capped) | | timeout | Run command with time limit | | xxd | Hex dump (-l, -s) | | curl | HTTP requests via network handler | | jq | JSON processor (full implementation) |

jq Support

Full jq implementation built from scratch. Supports identity, field access, array/object indexing, pipe, array/object construction, conditionals, try/catch, reduce, foreach, label/break, string interpolation, user-defined functions, and 80+ builtins including map, select, keys, values, has, in, contains, test, match, sub, gsub, split, join, ascii_downcase, ascii_upcase, tostring, tonumber, length, type, empty, error, env, path, getpath, setpath, delpaths, to_entries, from_entries, with_entries, group_by, sort_by, unique_by, min_by, max_by, flatten, range, floor, ceil, round, pow, log, sqrt, nan, infinite, isinfinite, isnan, isnormal, add, any, all, limit, first, last, nth, indices, inside, ltrimstr, rtrimstr, startswith, endswith, ascii, explode, implode, tojson, fromjson, @base64, @base64d, @uri, @csv, @tsv, @html, @json, @text, now, todate, fromdate, strftime, strptime, gmtime, mktime, builtins, debug, input, inputs, $ENV.

Available as a standalone import:

import { jq } from '@mylocalgpt/shell/jq';

Execution Limits

All limits are configurable via the limits constructor option.

| Limit | Default | Prevents | |-------|---------|----------| | maxLoopIterations | 10,000 | Infinite loops | | maxCallDepth | 100 | Stack overflow from recursion | | maxCommandCount | 10,000 | Excessive command execution | | maxStringLength | 10,000,000 | Memory exhaustion from string growth | | maxArraySize | 100,000 | Memory exhaustion from array growth | | maxOutputSize | 10,000,000 | Unbounded output accumulation | | maxPipelineDepth | 100 | Deeply nested pipes |

Custom Commands

const shell = new Shell({
  commands: {
    'fetch-data': async (args, ctx) => {
      const url = args[0];
      const response = await fetch(url);
      return { stdout: await response.text(), stderr: '', exitCode: 0 };
    },
  },
});

// Or after construction:
shell.defineCommand('transform', async (args, ctx) => {
  return { stdout: ctx.stdin.toUpperCase(), stderr: '', exitCode: 0 };
});

Custom commands receive the full CommandContext and participate in pipes, redirections, and all shell features.

Hooks

onUnknownCommand

Handle commands not in the registry:

const shell = new Shell({
  onUnknownCommand: async (name, args, ctx) => {
    if (name === 'python') {
      return { stdout: '', stderr: `${name}: use jq for data processing\n`, exitCode: 127 };
    }
    return { stdout: '', stderr: `${name}: command not found\n`, exitCode: 127 };
  },
});

onOutput

Post-process all exec results (truncate, redact, log):

const shell = new Shell({
  onOutput: (result) => ({
    ...result,
    stdout: result.stdout.slice(0, 30000), // truncate large output
  }),
});

OverlayFs

Read-through overlay that reads from a real host directory and writes to memory. The host filesystem is never modified.

import { Shell } from '@mylocalgpt/shell';
import { OverlayFs } from '@mylocalgpt/shell/overlay';

const overlay = new OverlayFs('/path/to/project', {
  denyPaths: ['*.env', '*.key', 'node_modules/**'],
});
const shell = new Shell({ fs: overlay });

await shell.exec('cat src/index.ts | wc -l');
await shell.exec('echo "new file" > output.txt');

const changes = overlay.getChanges();
// { created: [{ path: '/output.txt', content: 'new file\n' }], modified: [], deleted: [] }

Available as a separate entry point at @mylocalgpt/shell/overlay. Requires Node.js (uses node:fs).

Network Config

curl delegates all HTTP requests to a consumer-provided handler. The shell never makes real network requests.

const shell = new Shell({
  network: {
    handler: async (url, opts) => {
      const res = await fetch(url, { method: opts.method, headers: opts.headers, body: opts.body });
      return { status: res.status, body: await res.text(), headers: {} };
    },
    allowlist: ['api.example.com', '*.internal.corp'],
  },
});

await shell.exec('curl -s https://api.example.com/data | jq .results');

Security Model

See THREAT_MODEL.md for the full security model, threat analysis, and explicit non-goals.

What we do:

  • All user-provided regex goes through pattern complexity checks and input-length caps
  • Execution limits prevent infinite loops, deep recursion, and memory exhaustion
  • No eval() or new Function() code paths
  • Path traversal prevention via normalized absolute paths
  • Environment variables stored in Map (no prototype pollution)
  • No node: imports; runs in sandboxed environments

What we don't do:

  • This is not an OS-level sandbox. The shell executes within your JavaScript runtime's security context.
  • Custom commands have full access to the JavaScript environment. Use OS-level isolation for untrusted code.

License

Apache-2.0