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

paperclip-hermes-codex-auth

v1.1.1

Published

Sign a Hermes agent in to OpenAI Codex from the Paperclip UI. Drives `hermes auth add openai-codex` on a pseudo-terminal, per Hermes profile, so each agent holds its own OAuth credential instead of sharing one.

Readme

paperclip-hermes-codex-auth

Sign a Hermes agent in to OpenAI Codex from the Paperclip UI — no terminal, one profile at a time.

Paperclip runs Hermes agents through the hermes_local adapter. That adapter deliberately defers all authentication to Hermes' own configuration, and Paperclip's managed-Codex-home machinery applies to the codex_local adapter only. So on a self-hosted instance, a Hermes agent with no Codex credential fails at dispatch with:

No Codex credentials stored. Run `hermes auth` to authenticate.

…and the only fix is a human with shell access running an interactive login inside the container. This plugin turns that into a button.

Why per profile, and not one login for everything

The Hermes adapter sets HERMES_HOME to the profile directory, and Hermes stores its OAuth state in auth.json at that root. So a profile is the unit of authentication.

It is tempting to sign in once and copy auth.json everywhere. Don't. A Codex OAuth credential carries a refresh token that rotates: two runtimes sharing one copy will refresh independently and invalidate each other, and the symptom — a sudden "credentials expired" on a machine nobody touched — is thoroughly confusing to debug.

This plugin therefore lists your profiles and signs each in on its own. The panel makes that visible rather than implying one login covers everyone.

What it does

  • Lists every profile under the configured profiles directory, with live credential state read from auth.json (presence, last refresh, pooled credential count).
  • Runs hermes auth add openai-codex --type oauth --no-browser on a pseudo-terminal, surfaces the device URL as a button and the user code as large text, and polls.
  • Verifies the result with one real Hermes round trip before reporting success.
  • Offers a "Test credential" button per profile, for when an agent starts failing and you want to know whether the credential is the reason.

Install

Plugin Manager → Install Plugin → paperclip-hermes-codex-auth. Then Settings → Plugins → Hermes Codex Sign-in.

Configuration

| Setting | Default | Notes | |---|---|---| | hermesPath | (auto-detect) | Leave blank and the plugin tries /paperclip/hermes-venv/bin/hermes, then the usual system paths, then PATH. Set it explicitly if Hermes lives elsewhere. | | profilesRoot | /paperclip/.hermes/profiles | One subdirectory per profile. Each is used verbatim as HERMES_HOME. | | scriptPath | /usr/bin/script | util-linux script, used to allocate the PTY. | | verify | true | Run a real round trip after signing in. Leave it on; see below. |

Updating

The panel shows the installed version and offers an Update button.

This exists because Paperclip has no plugin-update UI. The host implements upgrade fully — POST /api/plugins/:id/upgrade deactivates the runtime, downloads and validates the new package, diffs the manifest capabilities, and either parks the plugin in upgrade_pending for operator approval or returns it to ready and reactivates — and pluginsApi.upgrade() exists in the host's own API client. Nothing calls it. So an installed plugin otherwise has no way to ship a fix to the people running it.

The upgrade runs as the signed-in human, from plugin UI that is same-origin with the Paperclip app, and needs instance-admin rights. It is deliberately not done from the worker: a worker-side upgrade would be the plugin escalating itself.

After a successful update the running page still holds the old UI bundle — the host swapped the worker underneath it — so the button turns into Reload to finish.

If the new version requests capabilities the installed one did not, the host holds it in upgrade_pending for an administrator. That is the capability gate working, and the panel says so rather than reporting a failure.

A plugin installed from a folder on the server (a local-path install) cannot be updated from npm: the host's upgrade re-reads that folder. The panel shows the folder instead of a registry version, and the button reads Reload from folder. Put the new build in the folder first.

Design notes

A PTY is mandatory. Run with pipe stdio, the login emits zero bytes and waits — the device-code prompt only renders on a terminal. Rather than take a native node-pty dependency (a build toolchain in every host image), this borrows the PTY that util-linux script already allocates, which keeps the plugin pure JavaScript and installable from npm anywhere Paperclip runs. The BSD/macOS script takes different arguments and is not supported.

The device-code flow is undocumented. The Hermes CLI reference states OAuth is "still browser-based; no device-code alternative documented" — but --no-browser on 0.18.2 prints a device URL and a user code and polls for approval. The parser's test fixture is the exact byte sequence observed, because that is the only specification of this flow that exists. Re-run the tests after a Hermes upgrade.

Success is not parsed. Hermes writes the credential itself, so the session observes auth.json plus the process exit code rather than matching a success banner upstream is free to reword.

Presence is not validity. hermes auth status answers "a credential is present", which is a different question from "a credential works" — an expired refresh token looks exactly like a live one. Only a real round trip distinguishes them, so that is what the verification step does. This is the same lesson that paperclip-claude-auth learned from claude auth status, which cheerfully reports loggedIn: true for a bogus token.

No credential passes through this plugin. It reads presence and metadata from auth.json and never loads a token. There is no code path here that reads an access or refresh token, which is why there is no code path that can leak one.

The device code is redacted from every transcript and log. While a flow is open it is a live second factor, so a sign-in is owned by the person who started it and another principal cannot poll it.

Hermes is usually not on PATH. It is commonly a Python virtualenv install, so the executable never lands in /usr/local/bin. A wrong path is not merely inconvenient: it surfaces as "executable not found" against a profile the panel has just badged Signed in, which reads as a broken credential rather than a missing setting. So the path is auto-detected, and when detection fails the error names every location tried.

A rebuilt bundle does not reload until the plugin is disabled and re-enabled. The worker logs its version at startup so "which build is live" is answerable from the logs.

Development

npm install
npm test          # parser fixtures, profile discovery, probe interpretation
npm run typecheck
npm run build

Local install (source must sit inside the host's bind mount so the container can see it):

<paperclip-data>/plugin-src/paperclip-hermes-codex-auth  →  /paperclip/plugin-src/...
POST /api/plugins/install  {"packageName": "<abs path>", "isLocalPath": true}

The install dialog only accepts npm names, though the API, CLI and install guard all support local paths. Two things reliably bite on a source install: a root-owned npm cache gives EACCES (chown -R 1000:1000 the plugin and npm cache directories), and NODE_ENV=production makes a bare npm install skip devDependencies, so the build fails on a missing esbuild.

Status

Tested against Hermes Agent 0.18.2 with the hermes_local adapter. The device-code flow is undocumented upstream and could change without notice.

License

MIT