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

@jackmorgan/phaser-cli

v0.1.0

Published

CLI for the Phaser API — browse catalog, manage keys, upload tracks

Readme

@jackmorgan/phaser-cli

Command-line interface for the Phaser API. Written in Go, distributed on npm.

Install

npm install -g @jackmorgan/phaser-cli

Postinstall pulls the prebuilt binary from the matching GitHub release (cli-v<version>). Supported: darwin/arm64, darwin/amd64, linux/arm64, linux/amd64, windows/arm64, windows/amd64. Set PHASER_CLI_SKIP_INSTALL=1 to skip.

Quick start

# Read-only (API key)
phaser auth login                         # paste an existing key
phaser catalog tracks --limit 10

# Artist flow (SIWE JWT → mint key → upload)
phaser auth siwe                          # signs an EIP-4361 challenge, saves JWT
phaser artists init --name "My Band" --slug my-band
phaser keys create --name cli --save      # mints and saves API key
phaser tracks upload song.mp3 --title "Track 1" --price 0.05 --cover cover.jpg --publish

Config lives at ~/.phaser/config.json (override with PHASER_CONFIG_HOME). Env overrides: PHASER_API_KEY, PHASER_API_URL, PHASER_PRIVATE_KEY.

Authentication

Two credential types, used for different endpoints:

| Credential | Where from | Used by | | ----------- | -------------------------------------- | ----------------------------------------------- | | API key | auth login / keys create | Catalog reads (catalog *, albums get) | | JWT | auth siwe (wallet signature, 1-hour) | Mutations: keys, tracks, albums, artists init |

The CLI picks the right credential per command automatically.

SIWE login

phaser auth siwe does the full EIP-4361 handshake:

  1. derives your address from your private key locally,
  2. fetches /v1/auth/challenge,
  3. EIP-191 signs the SIWE message with secp256k1 (pure-Go, no cgo),
  4. posts the signature to /v1/auth/verify,
  5. stores the returned JWT in config.

Private key is resolved in this order: --private-key flag → --private-key-file <path>$PHASER_PRIVATE_KEY → interactive prompt (masked). The key never leaves your machine.

Signatures are byte-identical to viem.signMessage — see internal/siwe/siwe_test.go.

Commands

Browse (API key)

| Command | Summary | | -------------------------------- | ---------------------------------------------- | | phaser health | Check API health | | phaser catalog tracks | List tracks (--q, --genre, --sort, --cursor, --limit, --artist) | | phaser catalog track <id> | Get a single track | | phaser catalog search <q> | Cross-search tracks/artists/albums | | phaser catalog genres | List genres | | phaser catalog artists | List artists | | phaser albums get <id> | Get an album with its track list |

Auth + config

| Command | Summary | | ------------------------ | --------------------------------------- | | phaser auth login | Save an existing API key | | phaser auth siwe | Sign in with Ethereum → JWT | | phaser auth logout | Clear saved credentials | | phaser auth status | Show which creds are active | | phaser config show | Print config (secrets redacted) | | phaser config path | Print config file path |

API keys (JWT)

| Command | Summary | | -------------------------- | ---------------------------------------------------- | | phaser keys list | List your keys (prefix + status) | | phaser keys create | Mint a key (--name, --save writes to config) | | phaser keys revoke <id> | Revoke by ID |

Artist profile + albums (JWT)

| Command | Summary | | ------------------------------------------- | --------------------------------------------- | | phaser artists init --name X --slug x | Create your artist profile (one per wallet) | | phaser albums create --title X | Create an album | | phaser albums update <id> --title Y | Update album metadata |

Storage + analytics (JWT)

| Command | Summary | | ----------------------------- | ------------------------------------------------------------- | | phaser storage balance | Credit, monthly burn, months of runway | | phaser storage invoices | Ledger of topups/charges/refunds (--cursor, --limit) | | phaser analytics | Streams, revenue, per-track table, last-30-days daily totals |

Tracks (JWT)

Simple path — single-shot multipart, ≤100 MB:

phaser tracks upload song.mp3 \
  --title "Track 1" \
  --genre synthwave \
  --price 0.05 \
  --cover art.jpg \
  --album-id <uuid> \
  --track-number 1 \
  --release-date 2026-04-20 \
  --publish

Advanced — presigned direct-to-S3 (for large files):

# Option A: let the CLI PUT the files for you
phaser tracks presign --title "Track 2" --audio big.flac --cover cover.jpg --upload
phaser tracks finalize --track-id <returned-id> --publish

# Option B: just get the URLs and PUT them yourself
phaser tracks presign --title "Track 2" --audio-content-type audio/flac -o json
# …curl -X PUT --data-binary @big.flac -H "Content-Type: audio/flac" "<upload_url>"
phaser tracks finalize --track-id <returned-id> --source-audio-key <key>

Other mutations:

| Command | Summary | | ---------------------------------- | ----------------------------------------------- | | phaser tracks update <id> | Update title/genre/price/album/etc. | | phaser tracks publish <id> | Publish a finalized draft | | phaser tracks delete <id> --yes | Archive (soft-delete) |

All commands support -o json. Use --help on any subcommand for flag details.

Shell completion

Cobra generates the scripts — run phaser completion <shell> --help for the per-shell install steps. Quick hits:

# zsh (one-shot this session)
source <(phaser completion zsh)

# bash (persistent)
phaser completion bash | sudo tee /etc/bash_completion.d/phaser

# fish (persistent)
phaser completion fish > ~/.config/fish/completions/phaser.fish

Error messages

Validation failures from the server include a details array (Zod issues). The CLI pretty-prints them:

Error: http 400: Validation error
  - title: String must contain at least 1 character(s)
  - stream_price_usdc: Expected string, received number (expected string, got number)

Use -o json on any command to see the full raw error payload.

Upload progress

tracks upload and tracks presign --upload print a live progress line to stderr when stdout/stderr is a TTY:

uploading   47.32%  47.32 MB / 100.00 MB  8.12 MB/s

Non-TTY environments (CI, piped output) stay silent to keep logs clean.

Development

go build -o bin/phaser ./cmd/phaser       # local build
go test ./...                             # unit tests (SIWE signing vs viem)
bash scripts/build.sh                     # cross-compile all 6 targets

Adding a new endpoint

  1. Add the typed method + response struct in internal/client/<resource>.go (one file per resource).
  2. Add a command file under internal/commands/<resource>.go.
  3. Register the top-level command in internal/commands/root.go.

The client.do method (JSON) and client.doMultipart (file uploads) are the single paths all requests flow through — retries, logging, tracing go there.

Release

Bump the version, tag, push:

# From packages/cli
npm version patch                                   # e.g. 0.1.1
git commit -am "cli: v0.1.1"
git tag cli-v$(node -p "require('./package.json').version")
git push && git push --tags

.github/workflows/cli-release.yml cross-compiles, uploads gzipped binaries to the release, and publishes the npm package. Requires the NPM_TOKEN secret.

Security notes

  • Private keys are used locally to sign one SIWE challenge and are never transmitted. Still, prefer a dedicated wallet — don't use high-value keys.
  • Config file is written with 0600 perms; keep ~/.phaser/ out of backups that may expose it.
  • API keys are shown once at mint time — keys list only ever returns prefixes.