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

@andrzejchm/opencode-anthropic-auth

v2.1.1

Published

OpenCode Anthropic OAuth plugin with multi-account Claude Max rotation

Readme

OpenCode Anthropic Auth — multi-account

Use several Claude Pro/Max subscriptions with OpenCode and rotate between them automatically as each one fills up its 5-hour limit.

A fork of ex-machina-co/opencode-anthropic-auth, which does all the hard work of making a Claude subscription usable from OpenCode. This fork adds the multi-account layer on top.

[!WARNING] No guarantees. Rotating subscriptions specifically to get past plan limits is plausibly a Terms of Service violation on each account involved, and upstream already notes that heavy automated usage has gotten people banned. You are choosing that risk, not avoiding it.

Also: this is not a licence to hammer the API. Use it to stop losing an afternoon to a limit you hit at 4pm, not to run Ralph loops across three accounts.


What it does

You log into 2+ Claude subscriptions. The plugin uses the first one until it reaches its switch threshold, moves to the next, and picks an account back up on its own once its 5-hour window resets.

oc-anthropic in action

$ oc-anthropic status
   #  ACCOUNT                 ORG             TIER      5H    SWITCH  RESETS IN  7D    STATE
   1  [email protected]        Acme            max_5x    83%   80%     2h 18m     41%   parked
 > 2  [email protected]        Side Project    max_5x    12%   60%     4h 6m      52%   ACTIVE
   3  [email protected]    —               max_20x    2%   90%     4h 48m     14%   idle

default switch 60% · ~/.local/share/opencode

Account 1 is past its 80% threshold, so it handed over and is waiting for its window to reset. Account 2 is serving requests.

Usage is read from the anthropic-ratelimit-unified-* headers that every /v1/messages response already carries, so tracking costs no extra requests and adds no latency.


Install

This is not on npm. It installs straight from GitHub, so pin a commit or tag rather than tracking main.

1. Add the plugin

// ~/.config/opencode/opencode.json
{
  "plugin": ["github:andrzejchm/opencode-anthropic-auth#v2.1.0"]
}

Remove @ex-machina/opencode-anthropic-auth if you have it — two plugins registering the same auth provider will fight.

[!TIP] Pin to a tag or commit. An unpinned plugin re-resolves on startup, which is a supply-chain risk for any plugin, not just this one. #v2.1.0 above is that pin.

2. Install the CLI

bun add -g github:andrzejchm/opencode-anthropic-auth#v2.1.0

Gives you oc-anthropic. The plugin works without it — the CLI is how you add accounts and see what's going on.

3. Log in

oc-anthropic login    # once per subscription
oc-anthropic status

If you were already using the upstream plugin, your existing login is imported automatically as account #1. No need to re-authenticate.

Accounts label themselves from /api/oauth/profile, so you never type a label and can't mix up which credential is which.


How rotation works

Each account has a switch threshold. While the active account is below it, nothing changes. Once it crosses, the next account in order takes over.

account 1 (80%) ──crosses 80%──▶ account 2 (60%) ──crosses 60%──▶ account 3 (90%)
       ▲                                                                 │
       └──────────── its 5-hour window resets ◀──────────────────────────┘

Three rules worth knowing:

Accounts come back on their own. When an account hands over, it's parked until its 5-hour window actually resets — not until its utilization happens to dip. That's also what stops it flip-flopping around the threshold, which matters because prompt caching is per-account and every switch costs a full cache-miss re-read of the conversation.

The last account is a floor, not a cliff. If every account is over its threshold, requests keep going to the last one until Anthropic actually rejects them, rather than failing early while you still have headroom.

A rate-limit response is handled mid-request. On a 429 the account is parked and the request is retried on the next account, so you don't see the error. Only rate-limit responses trigger this — an ordinary 400 is returned as-is rather than burning every account on one malformed request.

A broken account doesn't break the rotation. If an account's refresh token has been revoked, it is parked and the request falls through to the next one. Run oc-anthropic login for that account to bring it back; oc-anthropic status shows the error against it.


Commands

oc-anthropic                       # status (default)
oc-anthropic status                # usage, thresholds, resets, state
oc-anthropic status --cached       # same, without touching the network
oc-anthropic refresh               # force a live re-read of every account

oc-anthropic login                 # add another subscription
oc-anthropic remove <acct>         # drop an account

oc-anthropic order <a> <b> <c>     # set rotation order
oc-anthropic threshold <acct> 80   # switch point for one account (or `default`)
oc-anthropic label <acct> <name>   # rename

oc-anthropic use <acct>            # force-switch now, ignoring thresholds
oc-anthropic unpark                # clear all parks, restart from #1

<acct> takes a full label, a unique prefix, or an account id — oc-anthropic threshold work@ex 80 works.

use parks everything ahead of the target for an hour, so it sticks until those expire or you run unpark.

status fetches live numbers for any account whose reading is stale or over a minute old, so what you see is what is true. Readings newer than that come straight from the store, which makes repeated runs instant. --cached skips the network entirely; refresh forces a re-read of everything.


Configuration

Everything is optional. The CLI writes to the store and always wins over config, so you won't set something with the CLI and have it silently ignored.

// ~/.config/opencode/opencode.json
{
  "plugin": [
    ["github:andrzejchm/opencode-anthropic-auth#v2.1.0", {
      "switchThreshold": 0.6,
      "weeklyThreshold": 0.98,
      "accountOrder": ["[email protected]", "[email protected]"],
      "accountThresholds": { "[email protected]": 80 }
    }]
  ]
}
  • ANTHROPIC_BASE_URL — Overrides the Anthropic API endpoint for both release lines, such as when using a proxy. Must be a valid HTTP(S) URL.
  • ANTHROPIC_INSECURE — Skips TLS certificate verification. Behavior differs by OpenCode version:
    • OpenCode v1 — Set to 1 or true to skip verification. Only effective when ANTHROPIC_BASE_URL is also set.
    • OpenCode v2 — Not supported. OpenCode v2 plugin request hooks cannot disable TLS verification. If set, the plugin logs a warning and leaves verification enabled; requests to an untrusted or self-signed ANTHROPIC_BASE_URL will fail.
  • ANTHROPIC_CLAUDE_CODE_VERSION — Overrides the Claude Code version reported to Anthropic for both release lines. Must be major.minor.patch (for example, 2.1.280). Defaults to the bundled version; a malformed value is logged and the bundled version is used instead. A value older than the bundled version is honored but logs a warning, since reporting an older version can make newer models reject the request. Read once when the plugin loads, so restart OpenCode after changing it.

| Option | Default | Meaning | |---|---|---| | switchThreshold | 0.6 | 5-hour utilization at which an account hands over | | weeklyThreshold | 0.98 | 7-day utilization at which an account is skipped entirely | | accountOrder | login order | rotation order, by label or id | | accountThresholds | {} | per-account overrides, keyed by label or id |

Thresholds accept 80 or 0.8 — both mean 80%.

Environment overrides: ANTHROPIC_SWITCH_THRESHOLD, ANTHROPIC_WEEKLY_THRESHOLD. Upstream's ANTHROPIC_BASE_URL, ANTHROPIC_INSECURE and ANTHROPIC_CLAUDE_CODE_VERSION still work.


Files

| Path | Contents | |---|---| | ~/.local/share/opencode/anthropic-accounts.json | the store: refresh tokens, mode 0600 | | ~/.local/share/opencode/anthropic-status.json | secret-free, rewritten on every switch |

The status file is the one to read. It's derived from the store on every write so it can't drift, and it contains no credentials — safe to cat, jq, watch, or point a status bar at:

watch -n5 'jq -r ".accounts[] | \"\(.state)\t\(.label)\t\(.u5h*100)%\"" \
  ~/.local/share/opencode/anthropic-status.json'

Both paths can be overridden with ANTHROPIC_ACCOUNTS_FILE and ANTHROPIC_STATUS_FILE. OPENCODE_AUTH_FILE overrides where the one-time import reads OpenCode's own credential from.

Switches are also logged to the OpenCode server log, with the reason:

switched to [email protected] (5h 12%) — left [email protected] at 5h 83%

Restart OpenCode after installing or updating

OpenCode loads plugins once, when the server starts. If you use the web or desktop app, or run opencode serve, that process keeps the plugin it loaded at startup for its entire life.

A server that has been up for days is still running the plugin from the day it started. Rotation will not happen, and oc-anthropic use will appear to do nothing, because the running server never re-reads the code.

pkill -f 'opencode serve'   # then start it again, or restart the desktop app

Worth checking if rotation isn't happening: ps -eo pid,lstart,command | grep 'opencode serve'.


Works with the web and desktop apps

The plugin runs inside the OpenCode server, not the client, so rotation behaves the same whether you use the TUI, the web app, or the desktop app. There is deliberately no TUI panel — the status file and CLI work everywhere.


Notes and limits

OpenCode still shows one credential. Its auth store has a single slot per provider, so opencode auth list shows one Anthropic entry regardless of how many accounts you have. That entry is a mirror; this plugin's store is the source of truth.

Mid-request retry needs a replayable body. Retrying on a different account requires re-sending the request body. That works for /v1/messages; if a request ever streams its body, it returns the rate-limit error instead of switching.

Prompt caching is per-account. Every switch costs one cache-miss re-read of the conversation. Parking-until-reset keeps that to roughly one switch per account per window — don't set thresholds so tight that it thrashes.

Usage readings expire. A reading only describes the 5-hour window it was taken in. Once that window ends, the account is re-probed via /api/oauth/usage before it can be selected — an old timestamp is never taken to mean an empty window, because the account may have been used since by another machine or by Claude Code itself. Until a reading is refreshed, oc-anthropic status shows ? rather than a confident 0%.

One account per subscription. Accounts are keyed by Anthropic's account uuid, so re-authorizing an existing account updates it in place instead of creating a duplicate.

Token refresh is locked across processes. Anthropic revokes a refresh token the moment it is exchanged, so two processes refreshing the same account at once revoke each other's credentials — easy to hit, since OpenCode runs a long-lived server while the CLI runs separately. Refreshes take a lock file in the data directory and re-read before exchanging.


Development

bun install
bun test           # 278 tests
bun run build      # dist/ is committed — see below
bunx biome check .

dist/ is checked in. OpenCode installs plugins straight from this repo and bun blocks the prepare script, so a source-only checkout would have no entrypoint. The pre-commit hook rebuilds and stages dist/ whenever src/ changes, so you should not need to think about it.

Rotation logic lives in src/accounts/:

| File | Responsibility | |---|---| | selector.ts | which account serves the next request — pure, heavily tested | | store.ts | the account file: atomic writes, migration, lookup | | usage.ts | rate-limit headers, /api/oauth/usage, /api/oauth/profile | | refresh.ts | per-account token refresh, deduplicated in-process and across processes | | lock.ts | cross-process file lock | | manager.ts | glue between the request path and the store | | status.ts | the secret-free status file | | cli.ts | oc-anthropic |

Everything upstream does — system prompt sanitisation, tool prefixing, the billing header, streaming transforms — is untouched, so rebasing on upstream stays cheap.


Licence

MIT, same as upstream. See LICENSE.

Credit to ex-machina-co for the plugin this is built on.