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

@x12i/pagx

v2.0.1

Published

An npm-style CLI and SDK for scoped agentic packages

Readme

pagx

npm license

An npm-style CLI and Node.js SDK for public agentic packages: versioned, scoped bundles of agent definitions, prompts, and assets. Publish them to the hosted pagx registry (or one you host on Cloudflare), and install them into any project's .pagx/ folder.

  • The npm package is @x12i/pagx; the command it installs is pagx.
  • Agent package scopes (for example @x12i/helper) live in your pagx registry and are unrelated to the CLI's npm scope.
  • The default registry is https://registry.pagenti.dev. Reading and installing need no configuration; publishing needs a publisher token.
  • The Cloudflare Worker owns authentication, metadata, and storage. Clients never receive Cloudflare, D1, R2, or database credentials.

Requirements

  • Node.js 22 or later

Upgrading to 2.0

  • Access is set per version. Publishing one version as restricted keeps the package's public versions installable, and latest for other readers stays on the last public version.
  • Tokens have a role: admin, read-write, or read. read-write and read tokens can be limited to named packages. Tokens issued by 1.x stay admin for their scope.
  • verifyPublisherToken returns { username, role, packages }. Replace const username = await verifyPublisherToken(...) with const { username } = await verifyPublisherToken(...).
  • 1.x clients keep installing, viewing, searching, and publishing public packages against the 2.0 registry.

Install

npm install -g @x12i/pagx
pagx --version

Upgrade that global command later with:

pagx upgrade

pagx upgrade installs the latest @x12i/pagx with the package manager that installed the pagx command: npm install -g, pnpm add -g, yarn global add, bun add -g, or volta install.

Or run it without a global install:

npx @x12i/pagx --help

To use the SDK from code, add it to your project:

npm install @x12i/pagx

Quick start

# 1. Install a package into ./.pagx/@x12i/helper (uses the default registry)
pagx install @x12i/helper

# 2. Or install a package folder you are authoring, without any registry
pagx install ./agents/my-agent      # -> ./.pagx/@x12i/my-agent

# 3. Publish. Set PAGX_TOKEN to the token the registry operator gave you.
#    Clients do not mint tokens. PowerShell: $env:PAGX_TOKEN = "..."
export PAGX_TOKEN=...
pagx init agents/my-agent --scope @x12i -y
pagx publish agents/my-agent

pagx ls, pagx check, pagx init, pagx pack, pagx uninstall, and local-path installs never contact a registry.

Commands

If you know npm, use the same words. pagx install, pagx i, pagx add, pagx isntall, and the dashed forms pagx -i and pagx --install are the same command. A unique prefix works too, so pagx inst and pagx pub are enough. pagx --help lists every command.

| npm command | pagx | What it does here | | --- | --- | --- | | install, i, add, in, ins, inst, … | same, plus -i and --install | Install into .pagx/ from the registry, a folder, or a .tgz. No arguments installs dependencies from pagx.json. | | uninstall, remove, rm, r, un, unlink | same | Remove the package from .pagx/ and from pagx.json. | | update, up | same | Reinstall the newest version that matches the saved range. | | — | upgrade | Install the latest version of the pagx command itself. --dry-run prints the package-manager command. | | ci | same | Reinstall the exact versions in pagx-lock.json. Fails if the lock is missing or out of date. | | ls, list, la, ll | same | List what is installed. A package with agent.type includes that type. la and ll include the path. | | outdated | same | Compare installed versions with the registry. Exits with code 1 when something is behind. | | — | check | Compare installed agent.type values with acceptedTypes in pagx.json. Exits with code 1 on a mismatch. Does not change .pagx/. | | publish | same, plus pub and --publish | Pack the current directory (or a folder) and upload it. | | pack | same | Write a .tgz without uploading. | | init, create, innit | same | Write pagx.json. -y skips prompts (there are none; defaults are always used). | | view, info, show, v | same | Metadata. Try version, versions, readme, or dist-tags. | | search, find, s, se | same | Filter package names. --scope limits the list. --type keeps packages whose latest version has that agent.type. | | config, get, set, c | same | set, get, list, and delete for registry, scope, _authToken, and //<registry>/:@<scope>:_authToken. | | whoami | same | The username, role, and package list for the selected token. Pass --scope when several tokens are stored. | | ping | same | GET /health on the registry. | | version | same | Print or bump pagx.json (patch, minor, major, or an exact version). | | dist-tag, dist-tags | ls | List tags. Change one with pagx publish --tag <tag>. | | owner ls | same | Publishers of a package. The scope is the owner. | | prefix, root | same | Project directory, and the .pagx folder packages install into. | | logout | same | Delete publisher tokens from ~/.pagxrc. | | login | use token | There is no password login. pagx token signs a publisher token. |

Flags you already use:

| Flag | Meaning | | --- | --- | | -g, --global | Install, update, remove, or list in ~/.pagx instead of ./.pagx. Does not edit pagx.json. | | --save / --no-save | Update pagx.json and pagx-lock.json (the default), or skip both. | | -E, --save-exact | Save 1.2.3 instead of ^1.2.3. | | --dry-run | Resolve or pack without writing or uploading. | | --ignore-scripts | Accepted. pagx never runs lifecycle scripts. | | --tag <tag> | Dist-tag for publish (latest by default). | | --access | public or restricted for publish. Omitted defaults to restricted when pagx.json contains "private": true, and to public otherwise. | | --type <type> | With search, keep packages whose latest version declares this agent.type. | | --registry, --scope, --userconfig, --json | Override configuration. --json works on any command. | | -v, --version | Print the CLI version. pagx version is the separate bump command. |

-D / --save-dev, --save-optional, and --save-peer are rejected: a pagx project has one dependencies list. pagx run, pagx test, pagx start, pagx unpublish, and pagx link are rejected with the reason, instead of failing as unknown commands.

These commands are pagx itself, not npm. Clients of the hosted registry do not run them. The registry operator issues a token from this repository:

npm run token:issue -- clientname    # writes .keys/clientname-token.txt

pagx keygen and pagx token remain for a registry you host yourself:

pagx keygen --out .keys
pagx token --key .keys/private-key.json --username x12i --expires-in 3600 --out .keys/publisher-token.txt
pagx token --key .keys/private-key.json --username x12i --role read-write --package @x12i/helper --out .keys/helper-token.txt

--role is admin (the default), read-write, or read. Repeat --package to limit a read-write or read token to named packages in its own scope. An admin token always covers the whole scope. Tokens issued before roles existed keep full access to their scope.

Package specs follow npm syntax: @scope/name, @scope/[email protected], "@scope/name@^1.0.0", or @scope/name@<dist-tag>.

Configuration

Nothing is required to read or install a public package from the default registry. To publish, to read a restricted package, or to use a registry you host, create a user (~/.pagxrc) or project (./.pagxrc) config file:

registry=https://registry.pagenti.dev
scope=@x12i
//registry.pagenti.dev/:_authToken=${PAGX_TOKEN}

Public installs need no token. A restricted version is visible only to a token that may read that package. Install, view, search, and ping send a token when .pagxrc or PAGX_TOKEN has one. For a package read, a scope line in .pagxrc is used when that package's scope has one. Set PAGX_TOKEN to the publisher token the registry operator issued for your username. A bare _authToken=... also works for the registry configured in the same file, but URL-scoped tokens are recommended. Keep .pagxrc out of source control.

One registry token is enough when you publish a single scope. To publish more than one scope from the same machine, store a token for each scope. Publish reads the scope from the package name and sends that token:

registry=https://registry.pagenti.dev
scope=@cursor
//registry.pagenti.dev/:@cursor:_authToken=${CURSOR_TOKEN}
//registry.pagenti.dev/:@claude:_authToken=${CLAUDE_TOKEN}
//registry.pagenti.dev/:@openai:_authToken=${OPENAI_TOKEN}
//registry.pagenti.dev/:@x12i:_authToken=${X12I_TOKEN}

scope= is the default for pagx init, pagx search, and pagx whoami. pagx whoami --scope @claude uses the @claude token. Publishing @x12i/funcx uses the @x12i token even when scope= is @cursor. If that scope has no line, publish stops instead of sending another scope's token. When any :@<scope>:_authToken line matches the active registry, those lines are the only file tokens used. PAGX_TOKEN, or loadConfig({ token }), still overrides the file and is sent for every scope.

Precedence, highest first:

  1. Command-line options
  2. Environment variables: PAGX_REGISTRY, PAGX_TOKEN, PAGX_SCOPE, PAGX_USERCONFIG
  3. The nearest project .pagxrc
  4. ~/.pagxrc
  5. The built-in default, https://registry.pagenti.dev

An explicitly changed registry never receives an unrelated token, and a bare _authToken is never sent to the built-in default; use PAGX_TOKEN or a URL-scoped token for it. A token stored for one scope is never sent for another scope. Production endpoints require HTTPS; HTTP is allowed only on localhost.

Package format

Every package has a pagx.json (or package.json) with a scoped name and a canonical SemVer version. Other manifest fields and files can hold any agent metadata and assets. agent.type is an optional label for the consumer that knows how to read those files. Install extracts the archive into .pagx/ and stops. pagx does not copy or symlink files into .cursor/, .claude/, or any other directory.

{
  "name": "@x12i/react-best-practices",
  "version": "1.0.0",
  "description": "Enforces modern React and Tailwind patterns",
  "agent": {
    "type": "cursor-rules",
    "typeVersion": 1,
    "entrypoint": "rules/"
  }
}

agent.type, when present, is lowercase words separated by single hyphens, at most 64 characters: cursor-rules, not Cursor Rules or cursor_rules. The registry does not decide which values exist. cursor-rules, claude-skill, and memorix-pack are conventions a harness may recognize. cursor-rule and cursor-rules are different labels.

agent.typeVersion is an optional integer from 1 to 999. It tells the harness which shape that type used, separately from the package's SemVer. It is rejected without agent.type. A missing generation means the harness uses whatever default it has for that type.

agent.entrypoint, when present, is a relative path inside the package. A trailing slash means a directory. Publish fails when that path is not a file or a directory prefix in the archive. pagx does not check that the files match the type.

acceptedTypes is not a package field. It belongs on the project pagx.json.

Installed and locally authored packages live under .pagx/ in your project:

.pagx/
  @x12i/
    helper/
      pagx.json
      README.md
      AGENT.md
      assets/

See examples/helper for a complete package.

Publish an agentic package

pagx init .pagx/@x12i/helper --scope @x12i -y
pagx publish .pagx/@x12i/helper
pagx publish .pagx/@x12i/helper --tag next
pagx publish .pagx/@x12i/helper --dry-run

Inside the package directory, pagx publish works without a path, just like npm publish.

  • Names are globally unique as the full @username/name pair; different scopes may reuse a short name.
  • A username has exactly one scope, equal to that username. admin and read-write tokens publish only there. A read token cannot publish.
  • Versions are immutable: bump the version before republishing.
  • Publishing updates the chosen dist-tag (latest by default, even when an older version is published). Use --tag next for prereleases.
  • SemVer build metadata (1.0.0+build) is rejected so equivalent versions can't produce ambiguous releases.
  • "private": true in the package manifest publishes that version as restricted unless --access public is set. Any other --access value is rejected. The setting applies to the version being uploaded.
  • A scope must be allowed to host private packages, or a restricted upload is rejected. Public versions of the same package stay installable. latest for everyone else stays on the last public version.

Archives are gzip tarballs. Publishing excludes .backup, .pagx-install.json, .git, .keys, .pagxrc, .env*, and node_modules. No lifecycle scripts or agent code run during packing or installation. README.md (or README) is optional and is exposed by the registry APIs.

Default limits: 20 MiB compressed, 32 MiB of file data, 10,000 entries, 64 KiB of manifest JSON, and 512 KiB of README. Links, special files, traversal paths, duplicate paths, and nonportable Windows names are refused.

Install and back up

pagx install @x12i/helper
pagx i @x12i/[email protected]
pagx install "@x12i/helper@^1.0.0"
pagx install @x12i/helper@next
pagx update
pagx outdated
pagx ls
pagx uninstall @x12i/helper

Install a local folder or tarball

Any spec that isn't a scoped name is a local path, as with npm install ./dir:

pagx install ./agents/neo-networks        # a package folder with pagx.json
pagx install ./x12i-neo-networks-1.0.0.tgz  # a file from pagx pack
pagx install file:../shared/agent          # npm's file: form

The folder is packed with the same rules as pagx publish (so .env*, .git, .keys, and node_modules are left out), validated, and installed into .pagx/@scope/name from its manifest name, with the same backup and receipt handling as registry installs. No registry or token is needed. With --save (the default) pagx.json records "@scope/name": "file:agents/neo-networks", and pagx install, update, and ci reinstall from that path. outdated skips file: dependencies.

Installation downloads compressed bytes, checks SHA-512 integrity and archive identity, and extracts into a staging directory. The destination is <working-directory>/.pagx/@scope/name, or ~/.pagx/@scope/name with --global.

Before replacement, different existing contents are compressed into .pagx/@scope/name/.backup/<content-sha256>.tgz. The content hash covers file paths, bytes, and empty directories, and ignores timestamps, modes, backups, and the pagx receipt.

  • Each historical version of the contents is backed up once, and all prior backups survive upgrades and downgrades.
  • Local extra files (including hidden files excluded from publication) are included in backups.
  • Identical contents are neither replaced nor backed up.
  • A failure before the directory swap preserves the old package. If a swap rollback fails, pagx keeps a recovery directory and reports its location.
  • Per-package locks prevent competing local installs. After a crash, inspect .pagx/.install-* before clearing the reported stale lock.

Dependencies and lock file

Explicit installs save dependency selectors in the working directory's pagx.json and record resolved versions and integrity in pagx-lock.json (unless --no-save is given). pagx install with no arguments installs every entry in dependencies, reusing matching lock entries. Explicit package specs always resolve the selector again. This is an npm-style workflow without an npm dependency graph or lifecycle scripts.

{
  "dependencies": { "@x12i/helper": "^1.0.0" },
  "acceptedTypes": ["cursor-rules", "claude-skill"]
}

acceptedTypes is optional project policy. pagx check compares it with the agent.type of each installed package. Install still extracts packages that are not on the list. Omit the key to skip the check. An empty array accepts nothing, including packages with no type. The check matches the type string only, not typeVersion. It exits with code 1 when something does not match, and it does not change .pagx/.

Inspect packages

pagx view @x12i/helper
pagx view @x12i/helper versions
pagx view @x12i/helper readme
pagx search --scope @x12i
pagx search react --type cursor-rules
pagx pack .pagx/@x12i/helper

SDK

@x12i/pagx is the library behind the CLI. Pagx.open() reads the same .pagxrc and environment variables, then the methods match the commands: install, update, uninstall, ci, list, outdated, search, view, publish, pack, whoami, and ping.

import { Pagx } from '@x12i/pagx';

const pagx = await Pagx.open(); // cwd, .pagxrc, PAGX_REGISTRY, PAGX_TOKEN
await pagx.install(['@x12i/helper']);          // writes .pagx/ and pagx.json
await pagx.update();                           // within the saved ranges
console.log(await pagx.list());
console.log(await pagx.view('@x12i/helper', 'version'));
await pagx.uninstall(['@x12i/helper']);

The same functions are available without the class, which is what the CLI calls:

import { loadConfig, installPackages, publishPackage, RegistryClient } from '@x12i/pagx';

const config = await loadConfig({ cwd: process.cwd() });
await installPackages(config, ['@x12i/helper@latest']);
await publishPackage(config, '.pagx/@x12i/helper');
const catalog = await new RegistryClient(config).json('/v1/packages?limit=50');

| Export | CLI equivalent | | --- | --- | | Pagx.open(options) | Reads configuration, then exposes the methods below | | installPackages / pagx.install | pagx install (updates pagx.json and pagx-lock.json unless save: false) | | updatePackages / pagx.update | pagx update | | removePackages / pagx.uninstall | pagx uninstall | | ciPackages / pagx.ci | pagx ci | | listInstalled / pagx.list | pagx ls | | outdatedPackages / pagx.outdated | pagx outdated | | searchPackages / pagx.search | pagx search (type is --type) | | checkPackages / pagx.check | pagx check | | viewPackage / pagx.view | pagx view | | packageOwners / pagx.owners | pagx owner ls | | publishPackage / pagx.publish | pagx publish | | packDirectory / pagx.pack | pagx pack | | initPackage / pagx.init | pagx init | | bumpManifestVersion / pagx.version | pagx version | | installPackage(config, spec) | Install one spec only. Does not edit pagx.json | | installLocal(cwd, path) | Install a folder or .tgz into <cwd>/.pagx. No registry | | DEFAULT_REGISTRY | The registry used when nothing else is configured | | uninstallPackage(cwd, spec) | Delete one install directory only | | loadConfig / writeConfig | .pagxrc. writeConfig with a scope stores //<registry>/:@<scope>:_authToken | | tokenForScope(config, scope) | Token publish sends for that scope | | RegistryClient | whoami(), ping(), metadata(name), download(path), json(path) | | parseName, parseSpec, resolveVersion, validateManifest, validateAgentType, readAcceptedTypes | Spec, manifest, and project-policy checks | | integrity, inspectArchive | SHA-512 and archive checks | | generatePublisherKeys, createPublisherToken, verifyPublisherToken | pagx keygen and pagx token | | PagxError | Error with an HTTP-style status |

Key helpers are also exported from @x12i/pagx/auth. The package is ESM (import, not require) and requires Node.js 22 or later.

import { generatePublisherKeys, createPublisherToken } from '@x12i/pagx';

const keys = await generatePublisherKeys();
const token = await createPublisherToken(keys.privateKey, 'x12i', { expiresIn: 3600 });

Cloud credentials stay entirely server-side. The SDK never receives D1, R2, or Cloudflare tokens.

Server and authentication

The registry server (in server/) uses @x12i/hybrid-docstore with Cloudflare D1 and R2. Version documents offload archives and READMEs to R2; metadata, dist-tags, and public verification keys live in D1. The hybrid interceptor rolls back uploaded objects when SQL fails, and a custom D1 provider commits immutable version creation and tag updates in one D1 batch. Local Wrangler development provides SQLite-backed D1 and local R2 storage.

Publisher tokens use Ed25519 signatures (EdDSA JWT). The trusted issuer holds the private signing key; the registry verifies with public keys. HTTPS encrypts transport; JWT claims are signed, not encrypted. Private signing keys are never stored as ordinary documents. Optional server-side issuance reads its private key from a Cloudflare secret. Key-generation APIs return private material once, over an administrator-authenticated connection.

pagx keygen --out .keys
pagx token --key .keys/private-key.json --username x12i --expires-in 3600 --out .keys/publisher-token.txt

More detail:

Development

git clone [email protected]:x12i/pagx.git
cd pagx
npm ci
npm run check                      # typecheck, tests, build
npm run dry-run --workspace server # validate the Worker bundle
npm pack --dry-run                 # inspect the npm tarball

During development, run node dist/client/cli.js in place of pagx. On Windows, the tests' local Worker runtime requires the Microsoft Visual C++ Redistributable.

Useful scripts: npm run setup:local, npm run dev:server, npm run db:migrate:local, npm run db:migrate:remote, and npm run deploy:server.

Releasing

Nothing publishes or deploys automatically.

Registry. devops-agent runs deploy/registry.workflow.json: it creates or reuses the R2 bucket and D1 database, writes server/wrangler.deploy.json with the real D1 ID, applies server/migrations (tracked in d1_migrations, like Wrangler), deploys the Worker, sets the PUBLIC_KEYS_JSON and ADMIN_TOKEN secrets, and waits for /health. The pagx-specific steps live in the deploy/pagx-devops.mjs plugin.

# CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN in the environment or .env
npm run release:registry:plan   # validate, no cloud calls
npm run release:registry        # idempotent; safe to rerun
npx devops-agent history        # encrypted action journal in .safe-actions/

The script reads .keys/public-keys.json and keeps the admin secret in .keys/admin-token.txt (created on first run). It fails if https://registry.pagenti.dev/health does not answer. The signing private key stays in .keys/ and is not uploaded. Issue a client token with:

npm run token:issue -- clientname

The workflow can also run directly: npx devops-agent workflow run deploy/registry.workflow.json --plugin deploy/pagx-devops.mjs with PAGX_PUBLIC_KEYS_JSON and PAGX_ADMIN_TOKEN set.

npm package. Publishing requires membership in the @x12i npm scope.

npm run check
npm version <major|minor|patch>
npm publish --access public
git push --follow-tags

License

MIT