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

@danypops/pipes

v0.13.1

Published

Supervised Bun daemon for pi-pipes: authenticated loopback CI status/trigger/log operations across GitHub, GitLab, and Jenkins

Downloads

2,620

Readme

@danypops/pipes

Supervised Bun daemon (built on @danypops/vehicle-server) that owns CI credentials, real GitHub Actions/GitLab CI/Jenkins adapters, an orchestration layer with named pipeline presets, and a local SQLite pool of run status and cached logs — the standalone-adapter shape: the pool syncs on its own schedule regardless of whether any client is connected.

Service (systemd --user)

pipes service install   # write a systemd user unit, enable, and start it
pipes service status
pipes service restart
pipes service stop

pipes serve also works as a one-off foreground run. The daemon enforces a single-instance lock either way, so an ad hoc serve and the installed service can never both hold the port at once.

Operations

Exposed over an authenticated loopback HTTP API (POST /api/v1/ops), and callable via the CLI's generic passthrough (pipes call <op> [json]):

  • ci.help — configured backends and registered presets
  • ci.status / ci.log / ci.search — live backend reads, with grep/tail filtering and classified-failure context on the compact status verdict
  • ci.trigger / ci.wait / ci.cancel — trigger a raw job or a named preset, block until terminal, session-ownership-gated cancel
  • ci.subscribe / ci.unsubscribe / ci.pool / ci.tail — job-level background watching (autofocuses on a job's latest run, auto-unsubscribes once it finishes) with cheap, pool-only reads and token-budgeted log tails
  • ci.stages / ci.chain / ci.downstream — pipeline stage/step trees, a bounded (cycle-safe, node-capped) downstream/artifact tree, and a targeted downstream lookup for backends that need an explicit job name (Jenkins)

Auth

Real delegated auth wherever the backend has one: GitHub OAuth Device Authorization Grant, GitLab OAuth2 Device Flow (or Authorization Code+PKCE via a local loopback listener on older instances). Jenkins has no delegated auth API, so it uses a stored username+API-token pair — the one documented exception, not a default.

pipes login <github|gitlab|jenkins> [--as <profile>] runs the flow and stores the result — omit --as to use the plain default credential, or name a profile to keep a second account/server separate (see "Multiple repos/projects per backend" below). Every device-flow and PKCE login prints the verification URL/code as text regardless, and also best-effort opens it in your default browser (matching GitHub CLI's own UX) — a headless session or missing browser opener never blocks the login, it just falls back to the printed URL.

GitHub: reuse an already-authenticated gh CLI session

pipes login github --gh-cli [account] skips the OAuth device flow (and the GITHUB_CLIENT_ID App registration it needs) entirely by reading gh auth token instead — the same delegation pattern packed uses for npm login --auth-type=web: never re-implement a vendor CLI's own auth, just consume its result. Works whether gh stores its token in the OS keyring or a legacy plaintext file, since gh auth token is GitHub's own stable interface for exactly this, abstracting over both. Omit account for gh's current active account, or name one of gh's own multiple authenticated accounts (gh auth status lists them) — pairs naturally with --as to register each as its own pipes profile:

pipes login github --gh-cli DanyPops --as personal
pipes login github --gh-cli work-account --as work
GITHUB_CLIENT_ID=<client-id> pipes login github --as personal
GITHUB_CLIENT_ID=<client-id> pipes login github --as work
GITLAB_CLIENT_ID=<client-id> pipes login gitlab
JENKINS_URL=<url> JENKINS_USER=<user> JENKINS_API_TOKEN=<token> pipes login jenkins --as ci

Optional: credentials via Enigma

If a Enigma vault is running, pipes checks it first on every request, ahead of its own stored token and any static *_TOKEN env var — a credential Enigma rotates is picked up on the very next call, no daemon restart needed. Purely additive: pipes works identically with no Enigma running at all.

Register pipes as a scoped Enigma client (once), then pass the printed token to the daemon via ENIGMA_CLIENT_TOKEN:

enigma client add pipes --backends github,gitlab,jenkins
# -> prints a token once; export it wherever the pipes daemon is started
export ENIGMA_CLIENT_TOKEN=<printed token>

Without ENIGMA_CLIENT_TOKEN, pipes falls back to Enigma's shared admin-token file if one exists at $XDG_STATE_HOME/enigma/token — fine for a single-user machine where every local daemon is equally trusted, but a scoped client token is the least-privilege default.

With an Enigma new enough to serve its admin Unix socket (kernel-verified peer identity, SO_PEERCRED), pipes tries that transport first and needs no token at all for a client registered by uid (enigma client add pipes --backends ... --uid $(id -u)) — nothing to generate, export, or leak. Falls back to ENIGMA_CLIENT_TOKEN/the shared admin-token file unchanged when no socket is available (older Enigma, or none running).

The full ladder

Each backend resolves its credential in this order, every request, cheapest and least secret-bearing first:

  1. Static env var (GITHUB_TOKEN, GITLAB_TOKEN, JENKINS_API_TOKEN) — zero setup, but a plaintext secret in the process environment.
  2. A local named credential profilepipes login github --as work stores a separate credential per profile (one file per profile-qualified name, e.g. github-work.json), so two accounts on the same platform never collide. pipes credentials list / pipes credentials remove <name> manage what's stored, without ever printing a stored credential's contents. A repos.json target opts into one via its own profile field.
  3. Enigma, if running — checked first, ahead of both of the above, via a scoped client token or (when available) the zero-secret Unix-socket transport described above.

Each rung is opt-in: skipping straight to env vars, or never running Enigma at all, works identically to always having used it.

Multiple repos/projects/servers per backend

GITHUB_OWNER/GITHUB_REPO, GITLAB_URL/GITLAB_PROJECT_ID, and JENKINS_URL/JENKINS_USER/JENKINS_API_TOKEN each configure one default backend, named after the backend type. To address more than one repo, project, or Jenkins server through the same daemon, add a ~/.config/pipes/repos.json (or $XDG_CONFIG_HOME/pipes/repos.json) naming each target explicitly — once this file has entries for a backend type, it replaces that type's env-var default rather than adding to it:

{
	"github": [
		{ "name": "github-a", "owner": "octocat", "repo": "repo-a" },
		{ "name": "acme-github", "owner": "acme-corp", "profile": "work" }
	],
	"gitlab": [{ "name": "gitlab-a", "projectId": "42" }],
	"jenkins": [
		{ "name": "jenkins-ci", "baseUrl": "https://ci.example.com" },
		{ "name": "jenkins-auto", "baseUrl": "https://auto.example.com" }
	]
}

A GitHub target's repo field is optional. Given (github-a above), the backend is pinned to that one repo and jobRef stays a bare workflow file name ("publish.yml"), unchanged from a single-repo setup. Omitted (acme-github above), the backend is account-scoped: it covers every repo under owner, and jobRef must instead be "repo/workflow.yml" (e.g. "widgets/ci.yml") — the repo is resolved fresh on every call, not fixed at startup. A bare workflow name against an account-scoped backend fails loudly rather than guessing which repo you meant.

Every GitHub target shares one logged-in GitHub credential by default (a device-flow token authenticates any repo the granting user can access); every GitLab target likewise shares one credential, so every configured GitLab target must be on the same GitLab host. A target's optional profile field selects a separate, named local credential file instead of sharing the default (pipes login <backend> --as <profile> writes to it) — use this for a second GitHub/GitLab account. Jenkins targets are the exception: since a Jenkins API token is inherently scoped to one specific server+user, each target's profile defaults to its own name already, so two Jenkins servers never collide without needing an explicit profile.

ci(action=help) lists each configured target by its own name — pass that name as backend to address it.

Discovering what's under an account-scoped backend

An account-scoped GitHub backend hides which repos are actually valid -- ci(action=discover, backend=...) lists every repo the credential can see under that backend's owner (bounded to the first 100), and ci(action=discover, backend=..., repo=...) lists that repo's workflow files by their real file names -- the exact strings valid as jobRef's second half. Not implemented for GitLab/Jenkins yet; ci(action=discover) against either fails with a clear "capability not supported" error rather than a crash.