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

@sheepit-ai/cli

v0.9.1

Published

Sheepit CLI — codegen Flag constants, manage releases, run platform operations from the terminal

Readme

@sheepit-ai/cli

Generate type-safe flag constants and run Sheepit operations from your terminal.

npm version license

Command-line tool for the Sheepit platform. Generate typed flag constants for your codebase, and manage flags, rollouts, releases, events, and crashes from your terminal or CI pipeline. The binary is sheepit.

Why it exists

Flag names are strings, and a typo in a string silently never matches, so the feature stays off and nobody notices. The CLI fetches your live flag definitions and writes typed constants, so a wrong name is a compile error instead of a dead feature. It also exposes the read and write operations you would otherwise click through in the dashboard, which makes them scriptable in CI.

Install

# project-scoped, for npm scripts and CI
npm install -D @sheepit-ai/cli

# or run without installing
npx @sheepit-ai/cli init

A global install is available through the unscoped sheepit-ai alias:

npm install -g sheepit-ai
sheepit --help

Quick start

With a project-scoped install, prefix these with npx.

# 1. log in (opens the browser; stores a profile in ~/.sheepit/credentials.json)
sheepit login

# 2. inspect your project
sheepit flags list

# 3. typed flag constants for your codebase
sheepit init                        # writes sheepit.config.json, including apiUrl
export SHEEPIT_API_KEY=lp_dev_...   # codegen reads its key from this variable
sheepit codegen                     # writes ./src/generated/flags.ts by default

Then reference flags by constant instead of string:

import { Sheepit } from "@sheepit-ai/sdk-js";
import { Flags } from "./generated/flags";

const sheepit = Sheepit.init({ apiKey: "lp_pub_..." });
const showBeta = sheepit.flag(Flags.show_beta_ui, false); // a misspelled key fails to compile

Commands

| Command | Description | | ---------------------------------------------------------------------------- | ----------------------------------------------------- | | sheepit init | Create sheepit.config.json (interactive) | | sheepit codegen | Regenerate typed flag constants from the dashboard | | sheepit login / logout <profile> | Browser login (PKCE) or --key; remove a profile | | sheepit whoami / use / profiles | Show, switch, or list saved profiles | | sheepit env use <slug> / show / clear | Set, show, or clear the profile's default environment | | sheepit flags list / get / kill / restore | Inspect flags; activate or clear a kill switch | | sheepit rollouts list / get / start / pause / advance / complete | Manage staged rollouts | | sheepit releases list / get / health / create | Inspect and register releases | | sheepit events names / query | List event types; run a time-series query | | sheepit crashes list / get / reports | Inspect crash groups and reports (Apple SDK) | | sheepit audit tail | Recent audit-log entries | | sheepit integrations github list / link / unlink | Manage GitHub repo links | | sheepit feedback "<message>" | Send feedback to the Sheepit team | | sheepit help [topic] | Show help for a topic | | sheepit doctor --url <SDK URL> --publishable-key <key> | Check a forwarder in front of a self-hosted install |

Output is a table when stdout is a terminal and JSON when piped. Force either with --json or --output table.

Default environment

sheepit env use staging saves a default environment for the active profile (one optional environment field per profile in ~/.sheepit/credentials.json; @sheepit-ai/mcp reads and writes the same field, so a default set here also applies to your MCP tools). sheepit env show prints what applies and sheepit env clear drops the default, so calls use the key's own environment again. A login never picks one, and a re-login keeps the default you already saved.

The default applies only to environment-bound data. Today that is releases list and releases create; flags are universal and ignore it. For those commands the environment resolves, first match wins:

  1. the per-call flag (--environment-id <uuid>)
  2. the profile's default environment
  3. the API key's own environment (Production for a login key; applied by the server)

When neither 1 nor 2 is set the CLI sends no environment at all, so the request is identical to what an older CLI sends. releases create prints the environment it used, and says when that came from your default.

  • sheepit ci release --environment stays an assertion: the release lands in its API key's own environment, and the flag only fails the run if you named a different one. The default is ignored there and CI keys have no saved profile anyway.
  • 🔴 The environment is part of ci release's revision identity. Changing the key (or its environment) between two runs of one pipeline creates a second release instead of reusing the first.
  • The credentials file is replaced atomically (temp file in the same directory, mode 0600, then rename). Two shells changing it at once is still last-writer-wins; env show reads the value back.

Connecting to a self-hosted install

sheepit login signs in to Sheepit Cloud unless something tells it otherwise. To point it at your own install, pass the origin your browser uses — one flag, not two:

sheepit login --host https://sheepit.acme.internal

Or state the install once and run plain sheepit login. Login uses the first of these it finds and prints which one:

  1. --url / --host
  2. SHEEPIT_API_URL, e.g. https://sheepit.acme.internal/api
  3. apiUrl in the config file in the current directory (sheepit.config.json, or the first .sheepitrc* found), which sheepit init writes. Login asks you to confirm it first, because a config file can come from someone else's repo. Without a terminal it refuses it.
  4. Sheepit Cloud. Login prints a notice naming --host before it opens the browser.

Any flag (--host, --url or --web-url) turns off the variable and the config file. Login also refuses a dashboard and an API that belong to different installs, because signing in on one and redeeming the code on the other hands the second a credential.

A self-hosted install is a single origin: the dashboard is at / and the API is behind /api on the same host, so --host sets both and records them in the profile. Everything after login — flags, rollouts, events, codegen — then talks to your install. sheepit whoami shows web_url for a self-hosted profile so you can tell it apart from a hosted one.

Your install is never brokered through sheepit.ai. The CLI authenticates directly against the instance you name, the same way glab auth login --hostname does — which is what keeps an air-gapped install working and keeps your logins out of our infrastructure.

If your reverse proxy does not follow that layout, --url and --web-url still override each origin individually:

sheepit login --host https://sheepit.acme.internal --url https://sheepit-api.acme.internal

For a headless or CI login, --key still works and honours --host and SHEEPIT_API_URL the same way. With neither and no terminal it refuses, rather than send the key to Sheepit Cloud or to a host named by a config file nobody confirmed:

sheepit login --host https://sheepit.acme.internal --key lp_sec_... --name acme

Checking the forwarder in front of the SDK listener

If your SDK traffic reaches the install through your own forwarder (a load-balancer rule, nginx, a framework rewrite), sheepit doctor checks it end to end and exits 1 if anything fails. Here --url is the address your SDK calls, not the API origin login takes:

sheepit doctor --url https://app.example.com/_s --publishable-key "$PUBLISHABLE_KEY" \
  [--secret-key-env SECRET_KEY_VAR] [--expect-ip <your public IP>] [--origin https://app.example.com]

It uses no saved profile and talks to --url only. sheepit help doctor lists the checks.

Configuration

sheepit.config.json holds the project slug, the API URL, and codegen targets. It contains no secrets, so commit it:

{
  "projectSlug": "my-app",
  "apiUrl": "https://api.sheepit.ai",
  "codegen": {
    "platforms": ["web", "ios", "server"],
    "outputs": {
      "typescript": "./src/generated/flags.ts",
      "swift": "./ios/Generated/Flags.swift"
    }
  }
}

| Field | Type | Notes | | ------------------- | --------------------------------------------- | --------------------------------------------------------------------------------------- | | projectSlug | string (required) | Must match the slug in your dashboard | | apiUrl | string | Used by codegen when SHEEPIT_API_URL is unset. sheepit init writes it. No default | | codegen.platforms | ("web" \| "ios" \| "android" \| "server")[] | Platforms to generate for (Android SDK: coming soon) | | codegen.outputs | { typescript?, swift?, kotlin? } | Output paths. Without outputs, codegen writes ./src/generated/flags.ts |

.sheepitrc and .sheepitrc.json are also read, in that order after sheepit.config.json.

| Environment variable | Purpose | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | SHEEPIT_API_KEY | Key used instead of any saved profile (lp_dev_… or lp_sec_…). Required for sheepit codegen, which does not read saved profiles. | | SHEEPIT_API_URL | API URL. Required whenever SHEEPIT_API_KEY is set (otherwise the command fails; codegen also accepts apiUrl from the config file). There is no default, so a self-hosted key is never sent to Sheepit Cloud by omission. Sheepit Cloud: https://api.sheepit.ai. | | SHEEPIT_ENVIRONMENT_ID | Default for releases create --environment-id | | SHEEPIT_RELEASE_ENVIRONMENT | Default for releases create --environment (an environment slug, such as production). Set this or SHEEPIT_ENVIRONMENT_ID, not both. Deliberately not SHEEPIT_ENVIRONMENT, which @sheepit-ai/server reads as its runtime environment |

CI usage

Fail the build if a developer forgot to regenerate flag constants:

- name: Check flag codegen is in sync
  env:
    SHEEPIT_API_KEY: ${{ secrets.SHEEPIT_API_KEY }}
    # Required. Omit it and the run fails rather than silently defaulting to
    # Sheepit Cloud — which is what would send a self-hosted key to us.
    # (codegen can drop this if sheepit.config.json already carries `apiUrl`.)
    SHEEPIT_API_URL: https://api.sheepit.ai
  run: |
    npx @sheepit-ai/cli codegen
    git diff --exit-code src/generated/flags.ts

Draft flags (created but not yet published) are emitted too, marked with a DRAFT doc comment, so creating one in the dashboard changes the generated file just as an active flag does. sheepit flags list includes drafts as well; use --status active when you need the flags SDKs actually receive.

Register a release from GitLab CI

Works on GitLab.com and self-managed GitLab alike: the job calls the Sheepit API, so the runner needs outbound HTTPS to your API URL and nothing has to reach your GitLab. Store a project secret key (lp_sec_…) as a masked, protected CI/CD variable named SHEEPIT_API_KEY. Set SHEEPIT_API_URL as a CI/CD variable next to it (https://api.sheepit.ai, or your self-hosted install), not in the job's variables:. GitLab ranks project and group CI/CD variables above .gitlab-ci.yml ones, so a job-level URL is silently replaced by any group-level SHEEPIT_API_URL, which on a self-hosted install could send the key to Sheepit Cloud; keeping the URL beside the key leaves one value to check. Make it protected too: whoever can change it on an unprotected branch can point the key at their own server. Pin the version you tested (npx @sheepit-ai/cli@<version>) so a pipeline never changes behaviour on its own.

sheepit ci release (CLI 0.8.0 or later; hosted Sheepit or BYOC 0.5.0+) is one step that registers the release and the build it ships, and binds the two. The binding is what per-release health is measured through: Sheepit counts the sessions and crashes of exactly that build, matching telemetry by its platform, version and build number, so the app sends no ids. Today's SDKs do not declare crash capture in that telemetry, so once its telemetry resolves, a bound release with sessions reports capture_unverified: sessions and errors are counted, but no crash-free percentage is given. Product and per-platform health, measured by platform and app version, do not depend on it.

register-release-web:
  stage: deploy
  needs: [deploy-web]
  image: node:22
  environment: production # also records a deployment
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # not on merge-request pipelines
  script:
    - npx @sheepit-ai/[email protected] ci release --platform web --environment production

register-release-ios:
  stage: deploy
  needs: [upload-testflight]
  image: node:22
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - |
      npx @sheepit-ai/[email protected] ci release --platform ios --channel beta \
        --version "$APP_MARKETING_VERSION" --build-number "$APP_BUILD_NUMBER" \
        --environment production --artifact build/App.ipa
  • Safe to re-run. Retrying the job, re-running the pipeline or running a new pipeline on the same commit registers nothing twice: every record has an id derived from the release identity, and the command reads before it writes. A new pipeline adds only a new deployment; a retried job's deployment is deduplicated by the API. The JSON on stdout says, per record, whether it was created, reused or (deployments) recorded.
  • It only adopts what it would have written. The ids are derivable by anyone holding a key of the project, so before reusing an existing record the command checks that it names this build, environment, version, build number and commit, and was written by ci release. If not, it exits non-zero and changes nothing: that version is blocked until someone looks, but no release ever reports another build's data. Any key of the project can still register a matching record first with its own pipeline link, so keep secret keys to the people and pipelines you trust with production.
  • Give a job without environment: one anyway if you want scheduled health. A release stays created until a deployment is recorded, and the periodic health snapshot skips created releases (sheepit releases health <id> computes it on demand either way).
  • The release lands in the API key's own environment, because that is the only environment its binding can be written to. --environment (or SHEEPIT_RELEASE_ENVIRONMENT) is an assertion: when it names another environment the command exits non-zero before writing anything. A pipeline that releases to staging and to production gives each job the key of its environment. The environment is printed to stderr on every run.
  • One release per platform. A product with web, server, iOS and Android repos registers one release from each repo's pipeline. --platform takes web, ios, ipados, macos, tvos, watchos, android or server (backend is still accepted and stored as server); all is refused, because no telemetry ever reports it. SheepitKit reports the device the app runs on, so register an Apple build under every platform it runs on: a universal iPhone and iPad app is two runs, --platform ios and --platform ipados. SheepitKit before 0.8.0 reports every Apple device as ios.
  • --version and --build-number must equal what the app reports, or no event is attributed to the release: the SDK's release for web and server — which defaults to the commit SHA here, as CI_COMMIT_SHA, and which the server SDKs read from CI_COMMIT_SHA, GITHUB_SHA, VERCEL_GIT_COMMIT_SHA or SHEEPIT_RELEASE on their own — and the marketing version plus build number (CFBundleShortVersionString / CFBundleVersion, versionName / versionCode) on Apple platforms and Android, where --build-number is required. APP_MARKETING_VERSION and APP_BUILD_NUMBER above stand for however your pipeline exposes them. Both are capped at 64 characters, the same bound the SDKs report, except SheepitKit, which sends at most 63 bytes of UTF-8 (its crash report's limit): keep an Apple --version within that. A web or server release named by a tag still records its commit.
  • The same version and build from a different commit is refused, not overwritten: give every build its own build number.
  • The app needs no ids from CI. The API names the build of every event from the platform, version and build number the SDK already reports, using the same id this command registers, so the job does not pass anything to the app. The app only has to report the right version (see the SDKs' release option). Telemetry that arrives before this job runs, or from a pipeline that cannot reach the API, joins the build once it is registered. Its health reads capture_unverified (sessions and errors counted, no crash-free rate) until the app's SDK declares that it captures crashes.
  • --application <name> is only for a project that ships two apps on one platform. Telemetry names builds of the default application, so a build registered under another name is measured only from telemetry that sends its runtime ids, and the command says so on stderr. Without it, the application is shown under the CI project path.
  • --artifact <file> (repeatable) records the file's SHA-256 against the build.
  • The command reads the commit, branch, pipeline id and URL, and the job's environment: from GitLab's predefined variables (GitHub Actions works too, without a deployment). Outside CI, pass --commit-sha.
  • --dry-run reads (it needs the key) and writes nothing; it prints what it would create.

sheepit releases create remains for a release without a build: it is not idempotent (a re-run creates a second release), never writes a binding, and takes --environment <slug> or --environment-id <uuid> for any environment of the project.

Keys

| Key | Use | | ----------------------- | ---------------------------------------------------------------------------------------------- | | Developer, lp_dev_… | Read-only: codegen, flags list, and other reads. Fits CI. | | Secret, lp_sec_… | Everything, including writes such as flags kill and rollout control. Keep it out of clients. | | Publishable, lp_pub_… | Not usable: the endpoints the CLI calls reject publishable keys. |

FAQ

Where are profiles stored? ~/.sheepit/credentials.json, mode 0600. Switch with sheepit use <profile> or per command with --profile <name>.

Can I manage more than one project? Yes. Each project has its own sheepit.config.json. Commands other than codegen can use profiles for different keys and hosts (--profile); codegen ignores profiles and always uses SHEEPIT_API_KEY with SHEEPIT_API_URL or the config file's apiUrl.

Does the CLI send telemetry? Yes. After each project command (not the login/profile commands or help) it posts one $cli_command_run event to the API it is using, carrying the command name, success, duration, argument count, and error code, never argument values.

Is there a programmatic API? Output is JSON when piped, for tools like jq. For in-process use, install @sheepit-ai/sdk-js or @sheepit-ai/server.

Requirements

Node.js 20+.

License

MIT. Copyright (c) 2026 GoaTech AI LLC. See LICENSE.