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

drawer-so

v0.18.1

Published

Drawer CLI — publish files and directories to drawer.so from the terminal.

Downloads

24

Readme

drawer-so

Publish files and directories to Drawer from the terminal.

The CLI targets https://drawer.so by default; override with DRAWER_BASE_URL.

Install

npm install -g drawer-so
# verify
drawer --help

Quick start

# one-time: paste your drawer API key (drw_…)
drawer auth login

# create a new artifact from a single file (defaults to private)
drawer create ./report.html --title "Q3 report"

# create from a directory (zipped + uploaded as a multi-file artifact)
drawer create ./site --title "demo site"

# update an existing artifact in place (cannot change visibility — use publish)
drawer update art_abc1234567 ./report.html

# patch — surgical find/replace
drawer patch art_abc1234567 --find "Q3" --replace "Q4"

# flip visibility — public requires a y/N prompt (or --yes)
drawer publish art_abc1234567 --visibility unlisted
drawer publish art_abc1234567 --public --yes

# make private again
drawer unpublish art_abc1234567

# list / open
drawer list
drawer open art_abc1234567

Config

API key + base URL are stored in ~/.config/drawer/cli-config.json (chmod 600).

Override per-invocation with env vars:

  • DRAWER_API_KEY — override the saved key
  • DRAWER_BASE_URL — override the base URL (default https://drawer.so)

Commands

drawer auth login                         Paste an API key and save it
drawer auth logout                        Delete saved key
drawer auth whoami                        Show stored key prefix + base URL

drawer create <file|dir>                  Create a new artifact (defaults to private)
  --title <text>                          Title shown in the browser tab
  --visibility <tier>                     private (default) | unlisted | public
  --private / --unlisted / --public       Shorthand visibility flags
  --password <text>                       Gate the artifact (unlisted/public only)
  --tag <name>                            Apply a tag (repeatable)
  --tags <a,b,c>                          Comma-separated tags

drawer update <artifact_id> <file|dir>    Push a new version (no visibility change)
  --title <text>                          Update the title
  --tag / --tags                          Replace the tag set
  --expected-version <vid> / --force      Optimistic concurrency (same as patch/revert)

drawer publish <artifact_id>              Flip visibility of an existing artifact
  --visibility <tier>                     private | unlisted | public
  --private / --unlisted / --public       Shorthand visibility flags
  --password <text>                       Set or rotate the password
  --clear-password                        Remove the password modifier
  --yes / -y                              Skip the public-visibility y/N prompt

drawer unpublish <artifact_id>            Alias for `publish <id> --private`

drawer list                               List your artifacts
  --tag <name>                            Filter by tag
  --favorited                             Only starred artifacts
drawer get <artifact_id>                  Print artifact metadata as JSON
  --include-content                       Include current file contents (MCP get_artifact)
  --version <version_id>                  Inspect a historical version
drawer read <artifact_id> <path>          Read one file from an artifact
  --lines <start:end>                     1-indexed inclusive line range
  --offset <n> --limit <n>                0-indexed MCP-style window (offset + count)
  --version <version_id>                  Read a historical version
drawer grep <artifact_id> <pattern>       Search for a pattern across files
  --files / --path <a,b,c>                Limit search to these paths
  --max-results <n>                       Cap matches returned (default 50, max 200)
  --case-sensitive                        Case-sensitive matching (default off)
drawer open <artifact_id>                 Open the artifact URL in your default browser
drawer delete <artifact_id> [--yes]       Permanently delete (asks first)

drawer patch <artifact_id>                Find/replace surgical edits — single or batch
  --file <path>                           Defaults to the artifact's entry file
  --find <text>   --replace <text>        Single-edit form (mutually exclusive with --edit/--edits)
  --replace-all                           Replace every occurrence (single-edit form only)
  --edit "path:old:new"                   Batch edit (repeatable; first `:` splits path, LAST splits new)
  --edits <file.json>                     Batch edits from a JSON array
  --expected-version <vid>                Fail-fast version pinning (optimistic concurrency)
  --force                                 Skip the implicit drift check

drawer regex <id> <pattern> <replacement> Regex-based find/replace on one file
  --path <path>                           Defaults to the entry file
  --flags <imsu>                          Regex flags (g/y forbidden)
  --replace-all                           Replace every match (default: require exactly one)
  --preview                               Dry-run, don't write
  --expected-version <vid> / --force      Same as patch

drawer revert <artifact_id> [--to <ver>]  Promote a prior version back to current
  --expected-version <vid> / --force      Same as patch
drawer diff <artifact_id> <from> [to]     Unified diff between versions

drawer files add <id> <files...>          Add files to an existing artifact (new version)
  --mode error|overwrite                  Collision behavior (default: error)
  --path <slot>                           Targeted slot (single-file only; overrides source name)
  --expected-version <vid> / --force      Optimistic concurrency
drawer files rename <id> <from> <to>      Rename a file within an artifact
drawer files rm <id> <path>               Delete a file from an artifact

drawer upload-request <id>                Mint a back-channel upload URL and await the drop
  --paths <p1,p2,...>                     Slot paths the upload page should render
  --mode error|overwrite                  Collision behavior
  --note <text>                           Note shown to the user above the form
  --timeout <secs>                        Total wait budget (default 300)
  --no-await                              Just mint the URL; don't block

drawer comments list <id>                 List comments on an artifact
  --status active|resolved|all            Filter (default: active)
drawer comments post <id> <body...>       Post a comment (flagged via=agent)
  --force                                 Skip the implicit drift check
drawer comments enable <id>               Enable comments on an artifact you own
drawer comments disable <id>              Disable comments on an artifact you own

drawer duplicate <id>                     Duplicate an artifact (templates v1)
  --title <text>                          Title for the new artifact

Notes

  • Uploads use the unified /api/artifacts/upload endpoint, which dispatches by Content-Type (single file vs. zip archive).
  • Directories are bundled into a zip in-memory and uploaded; node_modules and .git are skipped by default.
  • Uploads cap at 100 MB raw / 200 MB extracted.
  • All commands honor per-API-key rate limits (60 mutating requests/min, 1000/hour).

Tests

Light vitest smoke suite covering CLI-unique behavior (flag parsing, command dispatch, error rendering — HTTP layer is mocked, no live server).

cd cli && npm test          # run the cli suite directly

The root repo's npm test runs both the root vitest suite and the CLI suite.

For maintainers — publishing

One-liner to publish a release from a clean checkout:

git pull && cd cli && npm install && npm run build && npm publish --access public
  • npm login is a one-time setup on the publishing machine.
  • Bumping versions: npm version patch && git push --follow-tags && npm publish --access public (use minor or major as appropriate). The prepublishOnly script re-runs the build.
  • See PUBLISHING.md for the full pre-flight checklist.