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

@devfellowship/sandbox-cli

v1.2.0

Published

CLI for managing DevFellowship sandbox environments

Downloads

29

Readme

@devfellowship/sandbox-cli

CLI for managing DevFellowship preview sandbox environments. It's a thin Commander-based wrapper over the dfl-sandbox-manager daemon HTTP API — provision a per-branch preview of any DFL app, run migrations, tail container logs, SSH into the app container from your editor, and tear it down when you're done.

Install

As a one-off (recommended — avoids binary-name conflicts with other tools that ship a sandbox command):

npx @devfellowship/sandbox-cli <command>

Globally:

npm i -g @devfellowship/sandbox-cli
sandbox <command>

The installed binary is named sandbox. If you already have another tool on your $PATH that uses the same name, prefer npx or alias it.

Requires Node.js >=20.

Configuration

Resolution precedence (first one wins): env var → ~/.config/sandbox-cli/config.json → built-in default. The CLI also reads .env.local/.env from the repo root when run inside a checkout (legacy fallback for CI scripts).

| Key | Env var | Default | Purpose | |---|---|---|---| | daemonUrl | DAEMON_URL | https://sandbox-api.preview.devfellowship.com | Base URL of the sandbox-manager daemon. As of v1.2.0 the default points at production — set to http://localhost:3500 for local dev. | | sandboxToken | SANDBOX_TOKEN | (empty) | Bearer token sent as Authorization: Bearer <token>. Required for everything except health. See Token resolution below. | | computePublicIp | COMPUTE_PUBLIC_IP | (empty) | Public IP of the compute host (used by connect/legacy SSH paths) | | sshKeyPath | SSH_KEY_PATH | ./creds/sandbox-admin | Private key for the legacy ssh helper (connect only — logs/status are now HTTP-only) | | sshUser | SSH_USER | root | User for the legacy ssh helper | | previewDomain | PREVIEW_DOMAIN | preview.devfellowship.com | Domain under which sandboxes are exposed |

Persisted config is written to ~/.config/sandbox-cli/config.json (mode 0600; directory 0700).

sandbox config — manage persisted config (v1.2.0)

# Set values that survive across shells
sandbox config set daemonUrl https://sandbox-api.preview.devfellowship.com
sandbox config set sandboxToken dfl_sk_…

# Show the effective config (token is masked to first 8 chars)
sandbox config show
sandbox config show --json

Token resolution (v1.2.0)

When the CLI needs a SANDBOX_TOKEN it tries, in order:

  1. Environment variable SANDBOX_TOKEN.
  2. sandboxToken from ~/.config/sandbox-cli/config.json.
  3. Infisical auto-fetch — if INFISICAL_MACHINE_TOKEN is set, GET {INFISICAL_API_URL}/api/v3/secrets/raw/SANDBOX_TOKEN?secretPath=/apps/paperclip&environment=prod. The fetched value is cached in process memory only, never persisted.

If none resolve a token, the CLI fails loud with the precedence list — it does not silently fall back to unauthenticated calls.

Remote use

# One-time setup
sandbox config set sandboxToken dfl_sk_…
sandbox list

Without a token, authenticated routes (list, provision, destroy, migrate, rebuild, refetch, exec, cp, env, sql, logs, status, secrets, functions) return 401 UNAUTHORIZED. health works unauthenticated.

Commands

health — check daemon status

npx @devfellowship/sandbox-cli health

Pings GET /health on the daemon and prints uptime and active sandbox count.

list (alias ls) — list active sandboxes

npx @devfellowship/sandbox-cli list
npx @devfellowship/sandbox-cli ls --json

provision — create a sandbox for a branch

# Uses the daemon's default dev command and port
npx @devfellowship/sandbox-cli provision dfl-learn feat/my-branch

# Custom dev command and port
npx @devfellowship/sandbox-cli provision dfl-learn feat/my-branch \
  --dev-command "npm run dev -- --host 0.0.0.0 --port 5173" \
  --port 5173

Accepts a bare repo name (dfl-learn), org/repo, or a full GitHub URL.

destroy — tear down a sandbox by branch

npx @devfellowship/sandbox-cli destroy feat/my-branch
npx @devfellowship/sandbox-cli destroy feat/my-branch -y   # skip confirmation

logs — tail container logs (HTTP-only as of v1.2.0)

# Last 100 lines from the app container
npx @devfellowship/sandbox-cli logs my-sandbox-slug

# Specific service (app / rest / auth)
npx @devfellowship/sandbox-cli logs my-sandbox-slug rest --tail 500

# Follow mode (polls /v1/sandboxes/:slug/logs?since=<iso> every 2s)
npx @devfellowship/sandbox-cli logs my-sandbox-slug -f

tail — alias for logs --follow (v1.2.0)

npx @devfellowship/sandbox-cli tail my-sandbox-slug rest

status — sandbox status (HTTP-only as of v1.2.0)

npx @devfellowship/sandbox-cli status my-sandbox-slug          # registry only
npx @devfellowship/sandbox-cli status my-sandbox-slug -v       # + containers + 20-line logs of app/rest/auth

exec — run a command inside the app container (v1.2.0)

npx @devfellowship/sandbox-cli exec my-sandbox-slug cat /app/package.json
npx @devfellowship/sandbox-cli exec my-sandbox-slug --timeout 60000 npm run typecheck

Argument-mode only: shell metachars (>, <, |, backtick, $()) are rejected client- and server-side. Upload a script via cp and exec it instead. Exit code mirrors the remote command. Requires write scope.

cp — copy files between local and a sandbox (v1.2.0)

# Upload — must target /app/<…> (use --unsafe-path to override)
npx @devfellowship/sandbox-cli cp ./local.txt my-sandbox:/app/foo.txt

# Download
npx @devfellowship/sandbox-cli cp my-sandbox:/app/package.json ./package.json

# Outside /app/ (rare — kept around for support flows)
npx @devfellowship/sandbox-cli cp ./prod.crt my-sandbox:/etc/ssl/prod.crt --unsafe-path

10 MB cap. Path traversal (..) is rejected on both client and server.

env — print scrubbed env (v1.2.0)

npx @devfellowship/sandbox-cli env my-sandbox-slug
npx @devfellowship/sandbox-cli env my-sandbox-slug --json

Daemon allowlists VITE_*, NEXT_PUBLIC_*, SUPABASE_*, POWERSYNC_*, NODE_ENV, PORT, then strips anything ending in _SECRET/_TOKEN/_KEY (except _PUBLIC_KEY/_ANON_KEY). SUPABASE_SERVICE_ROLE_KEY is never returned.

sql — run SQL via PostgREST roles (v1.2.0)

# Anonymous SELECT (read scope)
npx @devfellowship/sandbox-cli sql my-sandbox --query 'select id, email from public.users limit 5'

# Authenticated user role
npx @devfellowship/sandbox-cli sql my-sandbox --role authenticated --file ./check.sql

# service_role (admin-equivalent — write scope required)
npx @devfellowship/sandbox-cli sql my-sandbox --role service_role --query 'select count(*) from auth.users'

# JSON output for scripting
npx @devfellowship/sandbox-cli sql my-sandbox --query 'select 1' --json

The first SQL token decides the required scope: SELECT/WITH need read; anything else (or --role service_role) needs write.

migrate — run SQL against a sandbox DB

# Inline SQL
npx @devfellowship/sandbox-cli migrate my-sandbox-slug --sql "create schema foo;"

# From a file
npx @devfellowship/sandbox-cli migrate my-sandbox-slug --file ./supabase/migrations/0001.sql

# With a source label for audit
npx @devfellowship/sandbox-cli migrate my-sandbox-slug --file 0001.sql --source claude-main

migrations <slug> (sibling command) prints the migration log for a sandbox.

rebuild — rebuild the app container (fresh npm ci)

npx @devfellowship/sandbox-cli rebuild my-sandbox-slug

refetchgit fetch + reset --hard the current branch in the app container

npx @devfellowship/sandbox-cli refetch my-sandbox-slug

functions — deploy and list edge functions (v1.1.0)

Manage Supabase-style edge functions on a sandbox without destroying and re-provisioning. Functions land at /functions/v1/<name> on the sandbox's preview URL.

# Auto-deploy from repo-contract.yaml (running app-<slug> container)
npx @devfellowship/sandbox-cli functions deploy my-sandbox-slug

# Explicit GitHub source — public or private (uses GH_TOKEN / `gh auth token`)
npx @devfellowship/sandbox-cli functions deploy my-sandbox-slug \
  github:devfellowship/dfl-schema:supabase/functions/dfl-lesson-studio-shotstack-proxy

# A specific ref (branch/tag/sha):
npx @devfellowship/sandbox-cli functions deploy my-sandbox-slug \
  github:devfellowship/dfl-schema@some-branch:supabase/functions/foo

# Local directory
npx @devfellowship/sandbox-cli functions deploy my-sandbox-slug ./supabase/functions/my-fn

# Override the deployed name (defaults to last path segment)
npx @devfellowship/sandbox-cli functions deploy my-sandbox-slug ./local/fn --name renamed-fn

# List what's deployed
npx @devfellowship/sandbox-cli functions list my-sandbox-slug

If the function imports from a sibling _shared/ directory (the dfl-schema convention), the CLI auto-includes it in the upload.

secrets — manage sandbox-scoped Infisical secrets (v1.1.0)

Slug-scoped wrapper over Infisical. Values are never displayed — the CLI only confirms which keys exist and which scope they come from (shared, app, sandbox). Writes always land at /sandboxes/<slug>/; the daemon refuses to touch other paths.

# List keys visible to the sandbox (key + scope, masked status)
npx @devfellowship/sandbox-cli secrets list my-sandbox-slug
# KEY                       SCOPE     STATUS
# SENTRY_DSN                shared    ••••
# SHOTSTACK_API_KEY         app       ••••
# DEBUG_FORCE_RENDER_FAIL   sandbox   ••••

# Confirm one key — exits 0 if set, 1 if missing. NEVER prints the value.
npx @devfellowship/sandbox-cli secrets get my-sandbox-slug SHOTSTACK_API_KEY
# SHOTSTACK_API_KEY (set, scope: app)

# Set one or more keys — auto-restarts the edge container so the new
# env is picked up on the next request.
npx @devfellowship/sandbox-cli secrets set my-sandbox-slug \
  SHOTSTACK_API_KEY=ssk_test_… \
  SHOTSTACK_ENV=stage

# Delete a key from /sandboxes/<slug>/. Refuses to delete inherited
# keys (those that come from /shared/ or /apps/<repo>/).
npx @devfellowship/sandbox-cli secrets delete my-sandbox-slug SHOTSTACK_API_KEY

Keys matching production patterns are rejected by the daemon: PROD_*, *PRODUCTION*, CLOUDFLARE_API_TOKEN, SSH_KEY*, INFISICAL_*. The daemon's Infisical token is scoped to the sandbox env only — production-tier secrets are unreachable from the CLI by design.

connect — SSH config + open Cursor / VS Code into the app container

# Auto-detect editor (cursor preferred, falls back to code), update ~/.ssh/config, open
npx @devfellowship/sandbox-cli connect my-sandbox-slug

# Explicit editor
npx @devfellowship/sandbox-cli connect my-sandbox-slug --cursor
npx @devfellowship/sandbox-cli connect my-sandbox-slug --code

# Just SSH into the container
npx @devfellowship/sandbox-cli connect my-sandbox-slug --ssh

# Update SSH config only (no editor/ssh)
npx @devfellowship/sandbox-cli connect my-sandbox-slug --config-only

Upserts a block in ~/.ssh/config with a <slug>.sandbox host alias; it only touches blocks between its own markers so your other SSH entries are left alone.

Related

License

MIT © DevFellowship