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

railcode

v0.3.9

Published

Developer CLI for the multi-tenant Railcode platform: log in, scaffold, and deploy static apps.

Readme

railcode CLI

Developer CLI for the multi-tenant Railcode platform: log in, scaffold, and deploy static apps from the terminal. It authenticates with a personal API token (long-lived, revocable) and uses the existing multipart deploy API.

Install

pnpm install
pnpm build            # → dist/index.js (the `railcode` bin)

Then run node dist/index.js <command>, or pnpm link --global to put railcode on your PATH.

Commands

railcode signup [--human|--agent] [--api-url <url>]
                                   Sign up and deploy a private welcome app
railcode login [--api-url <url>]   Sign in and mint a personal API token
railcode login --setup-token <token>  Sign in with a one-time onboarding setup token
railcode init <app> [dir] [--template react|static] [--force]
                                   Scaffold an app. dir defaults to ./<app>;
                                   pass "." to scaffold into the current dir
railcode deploy [--private] [--no-source] [--force]
                                   Build (if configured) and deploy the app here
railcode pull [<deploy>] [--app <slug>] [--dir <path>] [--force]
                                   Download a deploy's source (default: the live one)
railcode db <list|query> ...       List data connectors / run read-only SQL
railcode connections <list|create|delete> ...   Manage org data connectors (admin)
railcode connector <list|catalog|link|add-mcp|tools|call|docs|fetch|access|share|...> ...
                                   Link your accounts (Gmail, Linear, …) or custom
                                   APIs/MCP servers, call them, share them
railcode agent <list|show|create|update|delete|run|schedule> ...
                                   Manage org-scoped managed agents
railcode members <list|set-role|remove|add> ...  Members + system roles (admin)
railcode roles <list|create|update|delete|add-member|remove-member|grants|grant|revoke|materialize|effective|catalog> ...
                                   Org roles + the granular grants table (admin)
railcode apps <list|show|access|set-access|add-editor|remove-editor|add-viewer|remove-viewer|transfer|archive|unarchive|delete> ...
                                   Apps + access policy (owner/admin; editors for
                                   access/add-viewer/remove-viewer)
railcode app kv <collections|list|get|set|delete|drop> ...
                                   Manage an app's KV store (owner)
railcode app files <list|download|upload|delete> ...
                                   Manage an app's files (owner)
railcode llm <providers|models>    The org's callable models, as apps see them
railcode llm default <model>       Set the org default (including railcode/*)
railcode llm connect chatgpt       Bill LLM calls to a ChatGPT plan (admin)
                                   Signs in with a login of its own; --from-codex
                                   reuses ~/.codex/auth.json (shared token, see help)
railcode analytics <app> [--range 1d|7d|30d|90d]    Per-app pageview analytics
railcode logs <connector|service-connector|llm|email|agent> [filters]
                                   Org observability logs (admin)

Postgres connections can use an SSH bastion. Set config.ssh to {host, port?, username, host_key} (the bastion's trusted OpenSSH public host key), and pass --ssh-key-file ./bastion.pem to connections create. Alternatively put the key contents in credentials.ssh_private_key. The optional key passphrase is credentials.ssh_passphrase; the database password remains credentials.password. Use --config-file and --credentials-file to load JSON settings. The database host is resolved from the bastion. See Postgres SSH setup.

MotherDuck is a native data connection: use --kind motherduck with config {region,database} and credentials {token} (a read scaling token). Its supported Postgres wire endpoint executes DuckDB SQL; saved queries and railcode db query use the existing grant/audit path. See MotherDuck setup.

In cloud mode the LLM discovery commands include the managed railcode provider and its public aliases (railcode/sonnet-4.5, railcode/haiku-4.5). CLI output never exposes the platform transport behind those aliases.

The members, roles, apps, analytics, logs, connections, and the connector admin subcommands mirror the web console's admin surfaces — everything an owner/admin can do in the dashboard is available from the CLI. They all target your saved org and are gated server-side by the same capabilities as the console (a builder gets 403; app list/show/access follow the per-app access policy).

  • signup works without a prior CLI install: npx -y railcode signup. With interactive stdin and stdout it offers human or agent setup; --human skips that choice. Without a terminal, or with --agent, it prints https://railcode.dev/auth.md and exits without requests, installation, or credential changes. Terminal detection is not agent identity detection. The human TUI offers email registration, existing-account login, or Google (when configured). Google opens browser signup and prints commands to return through browser CLI login. Email signup masks passwords and verification codes, supports resend, and creates or joins an eligible organization. Then it runs the onboarding survey and shows a prominent Signup complete panel with numbered next steps: run npm install --global railcode@latest, run npx --yes skills add Railcode-HQ/railcode-skills --global (choose agents in its prompts), then ask a coding agent to create and deploy a private welcome app using the Railcode skill. Signup does not install packages, scaffold, or deploy. Viewers finish at membership and CLI login. The API origin follows the same flag/env/saved/default precedence as login. Progress is saved under ~/.railcode/signup/, separately for each instance and user (or under RAILCODE_HOME), so Ctrl-C can be resumed with signup --human. Rerunning after completion shows the setup instructions again. Passwords and access JWTs are not persisted; only the personal token goes in the normal protected config. Local installation failures happen outside signup and their diagnostics remain visible in the user's terminal.
  • login opens your browser against the server URL (default https://api.railcode.app) to authorize the CLI, then mints a personal API token, resolves your organization, and saves everything to ~/.railcode/config.json (dir 0700, file 0600). A short-lived localhost callback catches the code automatically: the authorize page probes it and only navigates there once the CLI answers. When it can't be reached — browser on another machine, or one that won't follow a link to 127.0.0.1 — the Railcode page shows the code instead (with a copy button); paste it into the waiting prompt. Pasting the .../railcode-cli/callback?state=...&code=... address works too, if a browser does end up on an unreachable page: the CLI reads the code out of it, checking the state matches this login run. --paste / --no-browser skips the callback entirely and goes straight to the code page — the right default for SSH/headless boxes.
  • login --setup-token <token> is the non-interactive onboarding path (no TTY or browser — used by the dashboard's copied setup prompt). A setup token (rc_setup_...) is a short-lived (~10 min), ONE-TIME bootstrap credential, not a personal API token: the CLI exchanges it once for a personal API token and writes the same config as a browser login. If it's expired or already used, generate a fresh prompt from the dashboard.
  • init <app> [dir] scaffolds a directory. dir is optional and defaults to ./<app>; pass . to scaffold into the current directory, or any path to scaffold into it. The target need not be empty (same-named files are overwritten), but an existing railcode.json is preserved unless you pass --force. By default it creates a React + Vite + Zustand starter — a personalized "Welcome to Railcode, <name>" home wired to every SDK capability: identity/team, a live to-dos list and file uploads (work with zero setup), and SQL, saved queries, service connectors, and LLM cards that call the real thing when configured and fall back to a labeled sample otherwise. Hovering a name or card title reveals the exact SDK call behind it; every write fires a toast with the call that just ran. Use --template static for a no-build index.html (loads /_api/sdk.js and demos await me() + db.collection().put/get) plus railcode.json with "dist": ".".
  • deploy reads railcode.json ({ app, build?, dist? }), runs the resolved build command when needed, then uploads the output dir by app slug. On the first successful deploy for a slug, the server creates the app as part of that deploy; a failed first deploy does not leave a visible not-deployed app behind. The live URL is printed after success. It also uploads the project source alongside the built files, and sends the deploy number this folder is based on — see Source, pull, and the version marker. --no-source skips the source tree; --force deploys over a base someone else has moved past; --private sets access to private for this deploy only.
  • pull [<deploy>] downloads the source stored with a deploy — the live one by default, or the number the dashboard's deploy history shows (#3 — numbers count from 1 per app). --app <slug> picks the app when there's no railcode.json here, --dir <path> writes somewhere other than the current folder, and --force overwrites local files that differ. Files the deploy doesn't contain are never deleted. This requires a server with deploy source history endpoints; older servers keep accepting deploys, but pull reports that source pull is not supported yet.
  • apps manages apps and their access policy. Grants come in three tiers: owner, editor, and member (viewer). An editor can deploy, read/revert deploy history, pull source, read analytics, read the access policy, and add/remove individual viewers — but not delete, archive, transfer, change the access mode or the editor list, or use app kv/app files (those stay owner/admin). Editors can open the app in every access mode and keep the grant across mode changes, so the editor list is not an audience list; railcode apps access groups grants by tier to keep that clear. Use the atomic add-editor/remove-editor (and add-viewer/remove-viewer, restricted mode only) to change one person without rewriting the whole policy; on set-access, omitting --editors leaves the editor list alone while --editors "" clears it. transfer demotes the previous owner to editor rather than cutting them off.
  • ci github wires the app up to deploy from GitHub Actions, in one command. It resolves the app from railcode.json and the repository from the origin remote, mints a deploy token, hands it to GitHub as the repository secret RAILCODE_API_TOKEN via the gh CLI (over stdin — the plaintext never reaches your screen, your shell history, or an argv), and writes .github/workflows/railcode-deploy.yml. Without gh, or with --no-secret, it prints the token once plus the exact gh secret set command. --repo owner/name and --branch <name> override what it detects; --force overwrites an existing workflow file. Nothing is added to railcode.json. The workflow runs deploy --force, treating the selected branch as the deployment authority so a committed .railcode cannot block later pushes. Setup also ignores .railcode and removes it from Git tracking while keeping the local file (or warns if Git refuses due to staged changes). Commit the workflow, .gitignore, and staged marker removal together. CI can overwrite deployments made outside this branch, so use one deployment path for the app.
  • token manages those deploy tokens directly: create (plaintext shown once; --name <label>, --expires-in-days <n>), list, revoke <prefix>. The app comes from --app or railcode.json. A deploy token can deploy that one app and nothing else — it cannot read data, list apps, revert a deploy, or mint another token. It is EDIT-tier to create, every owner/editor/admin of the app can revoke any of them, and it stops working if whoever created it loses edit rights on the app. Long-lived by default: an expiring CI credential breaks a pipeline with no warning, so a lifetime is opt-in.
  • db inspects the org's data connectors (per-org Postgres) and runs ad-hoc read-only SQL. It works straight after railcode login — no app and no railcode.json required — since connectors are org-scoped; it hits the app-less /api/organizations/{org}/data/* plane with your login token. railcode db list (aliases ls, connections) prints each connector's name + engine (--json for the raw array). railcode db query "<sql>" (alias sql) runs SQL against --connection (default default) and prints a table + row count; --engine <postgres|bigquery|turso|motherduck> is inferred from the connector list when omitted, --params '<json-array>' binds $1, $2, … placeholders (SQL is never interpolated), --file <path> reads SQL from a file, and --json prints the raw { columns, rows, rowcount, truncated } envelope.
  • agent manages org-scoped managed agents when your permissions allow it. Manifests are JSON or YAML files in the same shape the API stores under agent.manifest.
    • railcode agent list lists agents; --json prints raw AgentOut[].
    • railcode agent show <name|uuid> prints one agent; --manifest prints only its manifest JSON.
    • railcode agent pull <name|uuid> --output agent.json writes the existing manifest for editing.
    • railcode agent create --file agent.yaml creates an agent; update <agent> --file agent.yaml replaces an existing agent manifest; delete <agent> --yes archives it. --visibility <org|personal> on create/update/test sets or changes who the agent belongs to (default org on create; omitted on update leaves it alone). A personal agent is invokable and manageable by its creator alone — no grant makes it shared — and personal -> org is blocked once created. Requires agent:create (personal) or agent:create_org (org). agent show/list print visibility, and show prints Owner: for a personal agent.
    • railcode agent test --file agent.yaml --input '{"k":"v"}' runs a draft manifest without saving; railcode agent run <agent> --input '{"k":"v"}' invokes a saved agent and prints the result. Use --trace for the step trace and --json for the raw run detail.
    • railcode agent schedule set <agent> --cron "0 9 * * *" --timezone UTC creates or updates the agent's one schedule. schedule show, pause, resume, delete --yes, and run-now operate on that single schedule.
  • connector works with connectors: org resources with an owner and an access mode, pointing at a third-party service. A connector is either an HTTP API (called by method + path) or an MCP server / linked account (called by tool name). Distinct from db/connections, which are SQL data connectors.
    • Link one you own. railcode connector catalog lists every provider this deployment can connect and how (api_key, oauth, token, mcp_oauth, mcp_token). railcode connector link <provider> [--method <id>] [--name <n>] [--token <t>] [--credential key=value ...] [--label "[email protected]"] creates it; an OAuth method prints a URL to open and then waits for the link to complete (--no-wait to skip). railcode connector add-mcp <name> <url> [--auth none|token|oauth] adds an arbitrary MCP server, and railcode connector create --own ... an arbitrary HTTP API. All three need the connector:create permission, which every role holds by default.
    • Per-user. railcode connector link <provider> --per-user [--name <n>] creates a connector that holds no credential: each member runs railcode connector connect <name> to sign in with their own account (OAuth providers only), and an app that calls it runs on the caller's account once they approve that app on the connect page it sends them to. connector status <name> [--app <slug>] shows your link and whether you approved an app; connector disconnect <name> removes your account and every approval. link <provider> refuses to add a separate row when a per-user connector for that provider exists (--own overrides); crons cannot use one.
    • Call one. railcode connector list shows what you can reach (kind, mode, owner, access, account); connector tools <name> [--refresh] prints an MCP/linked account's callable tools with their input schemas; connector call <name> <tool> [--args '<json>'] runs one; connector fetch <path> --connector <name> [--method] [--body] proxies an HTTP request; connector docs <name> prints the API documentation an agent would read.
    • Share one. A row you create is restricted — yours alone — until you open it up. connector access <name> shows the policy; connector access <name> organization|restricted sets the mode; connector share <name> --user <email> or --role <name> (optionally --pattern 'GET /v1/balance', --pattern 'TOOL /send_email', default everything) shares it; connector unshare takes it back, and the next call is refused immediately. connector transfer <name> --to <email> hands it over, connector relink <name> re-runs an expired OAuth link, and connector promote <name> (admin) makes it org-managed. An OAuth-linked row cannot be transferred or promoted — the token is your own account.
    • Admin. connector list --admin, connector native, connector enable <key> --credentials '<json>', connector create, connector delete, and connector endpoint-groups <name> manage org-wide rows and native presets.

A successful deploy can finish before a new organization's HTTPS certificate is ready. In that case railcode deploy prints Preparing secure connection and labels the URL as HTTPS pending. The deploy and source are saved normally. Open the dashboard to see when the app is ready; no redeploy is needed.

Configuration

~/.railcode/config.json:

{ "apiUrl": "...", "email": "...", "apiToken": "rc_...", "tokenPrefix": "rc_...", "orgUuid": "...", "orgSlug": "..." }

API URL resolution: --api-url > RAILCODE_API_URL > saved config > prompt. Set RAILCODE_API_TOKEN to override the saved token in CI. On a 401, the saved token is cleared and you're prompted to railcode login again.

In CI there is no saved config at all, so the org uuid has to come from the environment too: set RAILCODE_API_TOKEN, RAILCODE_API_URL and RAILCODE_ORG_UUID. Only the first is a secret; the other two are identifiers. railcode ci github writes all three for you.

Staying up to date

The CLI keeps itself current. When you run a command in an interactive terminal it checks the npm registry — at most once every 6 hours — for a newer railcode release within your current major and, if there is one, installs it globally (with whichever package manager it detects: npm/pnpm/yarn/bun) and tells you:

railcode: a new version is available — 0.1.26 → 0.1.27. Updating…
railcode: updated to 0.1.27. It takes effect on your next command.

It never jumps across a major version (a breaking 2.0.0 is left for you to install deliberately), the check is best-effort (a registry/network hiccup is silently ignored — it never blocks your command), and it's skipped in non-interactive/CI runs so automation never self-mutates a global install. The last_checked_for_updates timestamp lives in ~/.railcode/update-check.json, and all of the above is printed to stderr so --json output stays clean.

Environment overrides:

  • RAILCODE_NO_UPDATE=1 — turn auto-update off entirely.
  • RAILCODE_UPDATE_DRY_RUN=1 — check and print the command it would run, without installing (also forces the check even off-TTY; handy for scripts and testing).
  • RAILCODE_REGISTRY_URL (falls back to npm_config_registry) — point the check at a different npm-compatible registry.

railcode.json

{
  "app": "dashboard",
  "build": "npm run build",
  "dist": "dist"
}

build and dist are optional. Set "dist": "." for no-build static apps.

Deploy output resolution:

  1. railcode.json dist wins.
  2. Otherwise railcode.json build runs and uploads dist/.
  3. Otherwise a package.json with a build script runs <pm> run build and uploads dist/, where <pm> is the project's package manager — detected from a packageManager field or lockfile (pnpm/yarn/bun), defaulting to npm when there's no lockfile.
  4. Otherwise a root index.html can be deployed interactively; for CI set "dist": ".".

Source, pull, and the version marker

A deploy uploads two things: the built files that get served, and the project source so it can be pulled back later. The source tree is the project folder minus anything its .gitignore excludes, minus a built-in exclude list, minus the resolved build-output directory. The built-in list (matched by exact name at any depth) covers vendored deps and build output (node_modules, .venv, venv, dist, build), VCS internals (.git, .hg, .svn), framework/tool caches (.vite, .next, .nuxt, .svelte-kit, .astro, .turbo, .parcel-cache, .cache, .output, .wrangler, .vercel, .netlify, __pycache__, .pytest_cache, .mypy_cache, .ruff_cache, .tox, .eggs, .ipynb_checkpoints, coverage, .nyc_output), agent/local tooling state (.gstack, .playwright-mcp), editor and OS debris (.vscode, .idea, .DS_Store, Thumbs.db, desktop.ini), env files (.env, .env.local, .env.development.local, .env.production.local — .env.example still ships), package-manager debug logs, and the .railcode marker. If that tree is over the upload limit the deploy stops with a message naming the limit — add what doesn't belong to .gitignore, or pass --no-source.

On servers without deploy source history support, the extra deploy fields are ignored for compatibility: deploy still works, but no marker is written and railcode pull reports that the server does not support source pull yet.

railcode pull brings a deploy's source back down. With no argument it pulls whatever is live; railcode pull 412 pulls that deploy. It writes into the current folder (or --dir <path>), never deletes files the deploy doesn't contain, and stops rather than overwriting local files that differ unless you pass --force.

Both commands leave a version marker behind — .railcode, one small JSON file recording the instance, org, app and deploy number this folder now matches:

{ "api_url": "https://api.railcode.app", "org_uuid": "…", "app": "demo",
  "app_uuid": "…", "deploy": 412 }

The next deploy from that folder sends the number as its base version, and the server rejects the deploy (409) if the app has moved past it — so two people working from the same pull can't silently erase each other. The rejection says who moved it; resolve it with railcode pull && railcode deploy, or go over it on purpose with railcode deploy --force.

Because the marker is written after every successful deploy — including the first deploy of a brand-new app — plain edit-deploy iteration stays protected without pulling. It is scoped to one app: a copied project folder, or one re-pointed at another app or instance, sends no base version rather than the wrong one. Keep it out of git (railcode init gitignores it for you): it is sync state about your folder, so a colleague's git clone must not inherit it.