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

@synthforgeio/cli

v0.1.0

Published

SynthForge IO command line: generate relational schemas from a description, generate multi-table synthetic datasets, and download them

Readme

SynthForge IO CLI

synthforge (alias sf) drives SynthForge IO from a terminal or a CI job: generate a relational schema from a plain-language description, generate a multi-table dataset from that schema, and download it as CSV, Parquet, JSON or JSONL.

npm install -g @synthforgeio/cli
synthforge --help

One run, no install. The package ships two commands, so name the one you want:

npx -p @synthforgeio/cli synthforge whoami

Needs Node 20 or newer.

Before you start: an API key

Every command talks to the API as you, so you need a personal API key. Create one in the app under Settings. The key is shown once, at creation. Treat it like a password.

Two ways to give the CLI that key. Pick either.

Put it in the environment. Nothing is written to disk, which is what you want in CI:

export SF_API_KEY='sfk_live_your_key'
synthforge whoami

Or sign in and let the CLI keep a key for you:

synthforge login

It asks for your email and password (the password is not echoed on a terminal), then mints an API key and stores it. What it prints:

Logged in as [email protected]
API key sfk_live_abcd… stored at /home/you/.config/synthforge/credentials.json

login names the key it mints cli, and before minting it revokes any active key of yours that already has that name. That is deliberate: signing in from five machines leaves you with one key, not five. Use --name to keep them apart if you want one per machine:

synthforge login --email [email protected] --name laptop

The full key is never printed again after that first line, and only its short prefix is shown.

The credentials file

~/.config/synthforge/credentials.json

Written with mode 0600 in a directory created with mode 0700, and it holds three fields:

{
  "apiKey": "sfk_live_…",
  "baseUrl": "https://api.synthforge.io",
  "createdAt": "2026-09-13T10:04:11.812Z"
}

synthforge logout deletes that file. It does not revoke the key on the server: the key keeps working until you revoke it in Settings, or until the next synthforge login under the same name replaces it. On a shared machine, do both.

Commands

Most commands print their result as JSON on stdout, so jq and friends work as you expect. Progress notes go to stderr, which keeps a pipeline clean.

synthforge whoami

Who the current credentials belong to. The quickest way to tell whether a key is live.

synthforge whoami

synthforge schemas list

Your schemas. --limit (default 50) and --offset page through them.

synthforge schemas list --limit 10

synthforge schemas get <id>

One schema in full, including its tables, columns and foreign keys.

synthforge schemas get 5f3c1b7a-...

synthforge schemas create --file <path>

Create a schema from a JSON file you already have. --name overrides the name in the file.

synthforge schemas create --file ./orders-schema.json --name "Orders demo"

synthforge schemas generate -d "<description>"

Describe what you want in plain language and get a schema back. The command creates the job, waits for it, and prints the new schema id on stdout and nothing else, which is what makes it usable in a shell variable.

synthforge schemas generate -d "customers and orders, each customer places 1 to 5 orders" --name demo

--name suggests a name for the result. --timeout-ms (default 600000, ten minutes) and --poll-ms (default 2000) control the wait.

synthforge generate --schema <id> --rows <pairs>

Generate a dataset. The command creates the job, waits for it to finish, and prints the id, the final status and the row count. Add -o and it downloads the artifact as well.

synthforge generate --schema 5f3c1b7a-... --rows customers=500,orders=2000 --format csv,parquet -o ./demo.zip

| Option | Default | What it does | |---|---|---| | --schema <id> | required | which schema to generate from | | --rows <pairs> | required | row counts as table=count, comma separated | | --format <list> | csv | comma-separated export formats | | --seed <n> | allocated for you | fixes the random seed, so the same run gives the same data | | --name <name> | none | names the dataset | | -o, --output <path> | none | download the finished artifact to this path | | --timeout-ms <n> | 600000 | give up waiting after this long | | --poll-ms <n> | 2000 | how often to check |

Leave --seed out and the API picks one and tells you, on stderr, so you can reproduce the run later. The artifact is a ZIP holding every format you asked for.

synthforge status <dataset-id>

Where a dataset job has got to. Useful when you did not wait for it.

synthforge status 8b21e4d9-...

synthforge cancel <dataset-id>

Stop a job that is still running.

synthforge cancel 8b21e4d9-...

synthforge download <dataset-id> -o <path>

Download a finished dataset. Prints the path it wrote.

synthforge download 8b21e4d9-... -o ./demo.zip

Environment variables

| Variable | What it does | |---|---| | SF_API_KEY | the API key to use | | SF_API_BASE | the API to talk to. Defaults to https://api.synthforge.io | | SF_PASSWORD | password for synthforge login, so it never reaches your shell history or the process list | | SYNTHFORGE_CONFIG_DIR | somewhere other than ~/.config/synthforge to keep credentials |

The key is resolved in this order, first hit wins:

  1. SF_API_KEY
  2. the credentials file written by synthforge login

The base URL follows the same order: SF_API_BASE, then whatever login recorded, then https://api.synthforge.io. The two are resolved separately, so SF_API_BASE can point a stored key somewhere else without you logging in again.

When no key is found anywhere, the command stops before making a request and says so.

Scripting and CI

Non-interactive login, with the password out of argv:

export SF_PASSWORD="$MY_PASSWORD"
synthforge login --email [email protected]

login will not prompt when both the email and a password are supplied, so it fails fast in a job rather than hanging on a prompt that nobody can answer. Better still in CI, skip login and set SF_API_KEY: nothing is written to disk and there is no key to clean up.

Schema to data in two lines, with jq nowhere in sight because schemas generate prints only the id:

SCHEMA_ID=$(synthforge schemas generate -d "customers and orders with a foreign key")
synthforge generate --schema "$SCHEMA_ID" --rows customers=500,orders=2000 -o ./data.zip

Every command exits 0 on success and 1 on failure, so set -e does the right thing. Failures print one line on stderr:

error: row_counts has no entry for table(s): line_items (invalid_config) [HTTP 400]

The code in parentheses is stable enough to branch on. Waiting commands fail the same way when the job fails, is cancelled, or runs past --timeout-ms.

Reproducible fixtures, with the seed pinned so every run draws the same values:

synthforge generate --schema "$SCHEMA_ID" --rows customers=100 --seed 42 -o ./fixtures.zip

Help

License

MIT