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

@storagesdk/cli

v0.1.3

Published

Command-line interface for storagesdk — talk to any backend storagesdk supports via `storage <verb>`. Includes an `mcp` subcommand that boots a Model Context Protocol server.

Readme

@storagesdk/cli

Command-line interface for storagesdk. Ships two binary aliases — storage (primary) and storagesdk (for searchability) — both pointing at the same script.

npm install -g @storagesdk/cli
storage --adapter tigris ls

Install

# global
npm install -g @storagesdk/cli

# or one-shot via npx (no install)
npx @storagesdk/cli adapters

# or pin to a project
npm install --save-dev @storagesdk/cli

After install both storage and storagesdk are on your PATH and resolve to the same binary. Use whichever feels natural — storage is shorter; storagesdk is unambiguous for grep/history.

Discover adapters

storage adapters is the discoverability surface for runtime adapter selection — same env-var convention as @storagesdk/adapters's registry.

storage adapters
# fs
# s3
# r2
# tigris
# ...
# Run `storage adapters <name>` to see env vars.

storage adapters tigris
# Env vars for tigris:
#
#   TIGRIS_BUCKET             required
#   TIGRIS_ACCESS_KEY_ID      required
#   TIGRIS_SECRET_ACCESS_KEY  required
#   TIGRIS_ENDPOINT           optional
#   TIGRIS_FORCE_PATH_STYLE   optional

storage adapters s3
# Env vars for s3:
#
#   S3_BUCKET             required
#   S3_ACCESS_KEY_ID      optional  fallback: AWS_ACCESS_KEY_ID
#   S3_SECRET_ACCESS_KEY  optional  fallback: AWS_SECRET_ACCESS_KEY
#   S3_REGION             optional  fallback: AWS_REGION
#   S3_ENDPOINT           optional
#   S3_FORCE_PATH_STYLE   optional

Backend-native fallbacks are shown next to the adapter-prefixed var. If you already have AWS_* set in your environment, S3_* reads them automatically — no duplication needed.

Pick an adapter once

Every command below takes --adapter <name>. Or set STORAGE_ADAPTER once and skip the flag:

export STORAGE_ADAPTER=tigris
export TIGRIS_BUCKET=my-bucket
export TIGRIS_ACCESS_KEY_ID=…
export TIGRIS_SECRET_ACCESS_KEY=…
storage ls photos/

Read commands

storage ls [prefix]              # list objects under a prefix
storage stat <path>              # metadata for one object
storage cat <path>               # stream bytes to stdout
storage sign download <path>     # signed GET URL (string)
storage sign upload <path>       # signed PUT/POST URL (JSON: method, url, fields?)

ls, stat, and sign download honor the TTY/JSON convention (aligned text in a terminal, JSON when piped, --json / --no-json to force). cat always streams raw bytes — pipe with > to save. sign upload always emits JSON because the result is structured (method + url + optional form fields for S3-style POST).

storage cat photos/cat.jpg > local.jpg
storage cat config.json | jq .
storage stat photos/cat.jpg
storage ls photos/ --limit 100 --cursor "$cursor"

storage sign download downloads/report.pdf --ttl 3600
url=$(storage sign download downloads/report.pdf)

storage sign upload uploads/incoming.jpg \
  --ttl 600 --content-type image/jpeg --max-size 5242880
# { "method": "PUT", "url": "..." }
# or
# { "method": "POST", "url": "...", "fields": { ... } }

sign upload accepts --content-type, --max-size, --min-size. Adapters that don't enforce these (e.g. fs) silently drop them.

StorageError becomes a stderr message formatted as ✗ <Code>: <message> (or just ✗ <Code> when the message is the same as the code) plus a per-code hint: NotFound (check the path), Unauthorized (check the env vars), InvalidArgument (check the command arguments), Conflict, NotSupported, Provider (the backend rejected the operation).

Write commands

storage cp <src> <dst>       # copy between local and remote (storage://); `-` is stdin/stdout
storage mv <src> <dst>       # same scheme detection as cp; source removed after copy
storage rm <path>            # delete one remote object (bare keys or storage://<key>)

cp and mv use a storage:// URL scheme to mark remote paths; anything else is local. At least one side must be remote — local-to-local is rejected (use the shell). cp also accepts - for stdin (as source) or stdout (as destination); mv does not. Same remote source and destination is rejected on both cp and mv (would destroy the object on mv):

storage cp ./report.pdf storage://reports/2026-06.pdf       # upload
storage cp storage://reports/2026-06.pdf ./report.pdf       # download
storage cp storage://a.jpg storage://b.jpg                   # remote → remote
storage cp - storage://from-stdin.txt < ./local.txt          # upload from stdin
storage cp storage://config.json - | jq .                    # download to stdout

storage mv storage://drafts/post.md storage://posts/post.md  # rename
storage rm storage://photos/cat.jpg

Write commands take --fork <name> to scope writes into a fork. --snapshot <id> is rejected with a clear message — snapshots are read-only. cp / mv accept --content-type <mime> to override the upload's Content-Type.

In human mode, write commands print a confirmation line to stderr (so stdout stays clean for piped data); JSON mode emits { action, from?, to?, path? } on stdout.

Snapshots and forks

List with storage snapshots and storage forks; manage with the singular storage snapshot and storage fork subcommand groups:

storage snapshots                                       # list snapshot ids
storage forks                                           # list fork names

storage snapshot create                                 # take a snapshot
storage snapshot create --name pre-deploy               # attach a label
storage snapshot rm snap-0193abc1234567890              # delete (idempotent)

storage fork create experiment-a                                          # seeded from base
storage fork create experiment-a --from-snapshot snap-0193abc1234         # seeded from a snapshot
storage fork rm experiment-a                                              # delete (idempotent)

Scope any object command into a snapshot or fork with --snapshot <id> / --fork <name>. --snapshot is reads only (rejected on writes); --fork works on both:

storage ls photos/ --snapshot snap-0193abc1234567890abcdef
storage cat photos/cat.jpg --fork experiment-a > local.jpg
storage cp ./image.jpg storage://image.jpg --fork experiment-a
storage sign downloads/report.pdf --snapshot snap-0193abc1234567890abcdef

Both flags compose on reads. Fork is applied first, so --fork X --snapshot Y addresses a snapshot inside the fork:

storage ls --fork experiment-a --snapshot snap-0193abc1234567890abcdef

Model Context Protocol

storage mcp boots a stdio Model Context Protocol server that exposes every storagesdk verb as an MCP tool — pair it with Claude Desktop, Cursor, the MCP Inspector, or any MCP host to drive your storage from an AI agent:

storage mcp                            # stdio, run until stdin closes
storage mcp --read-only                # browse-only (strips mutators)
storage mcp --scope agent-runs/        # path-restrict every tool
storage mcp --url-expires-in 1800      # presigned-URL TTL
storage mcp --max-inline-bytes 65536   # inline-text cap for download

stdout is reserved for the JSON-RPC protocol — do not run interactively. The readiness line and any errors go to stderr. The actual server lives in @storagesdk/ai/mcp (createMcpServer) so non-CLI hosts (HTTP servers, in-process embedding) can register the same tool roster.

Quick test with the MCP Inspector:

npx @modelcontextprotocol/inspector \
  -e FS_ROOT=/tmp/sdk-mcp -e FS_FOLDER=data \
  storage mcp --adapter fs

Output format

TTY-aware by default — human-readable when stdout is a terminal, JSON when piped:

storage ls photos/             # human (in a terminal)
storage ls photos/ | jq        # JSON (piped to jq)
storage ls photos/ --json      # force JSON
storage ls photos/ --no-json   # force human

For writes (cp, mv, rm), the human-mode confirmation line goes to stderr so stdout stays clean for downloaded bytes (storage cp storage://config.json -). JSON mode always emits structured output on stdout. Errors go to stderr in both modes.

License

Apache 2.0.