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

@xec-sh/core

v0.11.1

Published

One TypeScript API for commands across local shells, SSH hosts, Docker containers, and Kubernetes pods

Downloads

133

Readme

@xec-sh/core

One TypeScript API for commands, wherever they run — local shell, SSH host, Docker container, Kubernetes pod.

npm install @xec-sh/core

The idea

import { $ } from '@xec-sh/core';

await $`pnpm build`;                                    // this machine
await $.ssh('deploy@prod-1')`systemctl restart app`;    // an SSH host
await $.docker('postgres-main')`pg_dump mydb`;          // a container
await $.k8s('production/api')`cat /var/log/app.log`;    // a pod

Four environments, one $. Every target accepts the same template-literal syntax, the same chaining methods, and returns the same result shape. zx, execa, dax and Bun Shell stop at the local machine; this is the same ergonomics across the seam.

Safe by default

const file = 'my file; rm -rf /';
await $`cat ${file}`;        // runs `cat 'my file; rm -rf /'` — quoted, inert

const flags = ['-l', '-a'];
await $`ls ${flags} src/`;   // arrays expand to escaped arguments

await $.raw`echo $HOME`;     // raw interpolation, when you actually mean it

Results you can use directly

const branch = await $`git branch --show-current`;
console.log(`Branch: ${branch}`);            // "Branch: main" — like $(...) in a shell

const result = await $`grep TODO src/ -r`.nothrow();
result.ok          // exit 0 and not signalled
result.stdout      // string
result.exitCode    // 128+signum for signalled processes — a kill is never "success"
result.cause       // why not ok

const pkg   = await $`cat package.json`.json<{ version: string }>();
const files = await $`ls -1`.lines();
const image = await $`cat logo.png`.buffer();

for await (const line of $`journalctl -f`) { /* stream lines */ }

Failures explain themselves — the error message carries the exit code and the head of stderr, so catch (e) logs are diagnostic without extra work.

Every environment is a chain

const staging = $.ssh('deploy@staging')
  .cd('/srv/app')                 // on the host
  .env({ NODE_ENV: 'staging' })   // on the host
  .timeout(60_000)
  .retry({ maxRetries: 3 });

await staging`pnpm migrate`;

// The identical chain on a pod — .cd()/.env() apply inside the pod,
// never to your local kubectl process:
await $.k8s('staging/api').cd('/srv/app').env({ DEBUG: '1' })`node check.js`;

// And in a container — .cd() maps to `docker exec -w`:
await $.docker('builder').cd('/workspace')`make all`;

Environment-specific power stays available where it belongs:

const ssh = $.ssh('deploy@prod');
await ssh.uploadFile('./dist.tar.gz', '/srv/app/dist.tar.gz');
const tunnel = await ssh.tunnel({ localPort: 5432, remoteHost: 'db', remotePort: 5432 });

const pod = $.k8s('production/api').pod('api-7f9d');
await pod.portForward(8080, 80);
await pod.follow(line => audit(line));           // streaming logs
await pod.copyFrom('/var/log/app.log', './app.log');

The contract

Enforced by tests, not aspirational:

  • An option either takes effect in its environment or fails loudly — nothing is accepted and silently dropped. AbortSignal cancels on every adapter.
  • Output over maxBuffer kills the producer and fails with the truncated head preserved — never an empty result with exit code 0.
  • Secrets are masked in command echoes, events and error messages: tokens, API keys, URL credentials, PEM blocks. Masking is pattern-based, so a value passed as a bare argument (mysql -pP4ss, redis-cli -a P4ss) can still slip through — pass credentials by environment or stdin, not on the command line.
  • SSH connections are pooled, self-heal on drop, and are released by dispose(). The library installs no global process handlers unless you opt in with installCleanupHandlers().
  • The same on Linux, macOS and Windows, and the unit suite runs on all three: interpolated values are quoted for the shell that will parse them — cmd.exe gets caret escaping — paths compose through node:path, glob answers /-separated results everywhere, a line ends with \n or \r\n, and a timeout takes down the whole process tree. What a command means is still the shell's: cmd.exe has no &&, no $VAR and no sleep.

Utilities

import { parallel, within, sleep, glob } from '@xec-sh/core';

await parallel(['build A', 'build B'].map(t => $`run ${t}`), { maxConcurrent: 2 });

await within(async () => {
  $.defaults({ cwd: '/tmp', env: { NODE_ENV: 'test' } });
  await $`npm test`;                 // scoped configuration
});

await $.withTempDir(async dir => {
  await $`unzip release.zip -d ${dir}`;   // dir is a path; removed afterwards
});

$.verbose = true;                    // echo each command (redacted) to stderr

// A command that owns the terminal — npm login, vim, ssh — runs attached to
// it; output goes to the user, not into result.stdout
await $.interactive()`npm login`.timeout(0);

Dependencies

One runtime dependency: ssh2, and it is not loaded until an SSH target is first used — importing the package pulls in Node builtins only. Docker and Kubernetes adapters speak the docker/kubectl CLIs, so behaviour matches what you would get by hand, and both load lazily. Works on Node.js 20+, Bun and Deno.

License

MIT