@narumitw/pi-accounts
Pi extension for switching named provider-owned OAuth accounts.
Package details
Install @narumitw/pi-accounts from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@narumitw/pi-accounts- Package
@narumitw/pi-accounts- Version
0.53.1- Published
- Oct 6, 2026
- Downloads
- 1,887/mo · 516/wk
- Author
- narumitw
- License
- MIT
- Types
- extension
- Size
- 552.7 KB
- Dependencies
- 2 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./dist/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
🔐 pi-accounts — Switch Between Provider Accounts
Save and switch named OAuth and API key accounts for Pi's built-in login providers.
Each Pi session keeps its own selection for every provider, and choosing default restores Pi's normal authentication only for that session without deleting saved accounts.
[!WARNING] Anthropic currently treats Claude Pro/Max use through third-party harnesses as extra usage billed per token, rather than consumption of the normal plan allowance. Review your Anthropic billing and extra-usage settings before using a named Anthropic account.
[!CAUTION] Named credentials are stored locally in a private file. API key entry uses visible, unmasked TUI/RPC input; use trusted clients and avoid screen sharing or recording.
✨ Features
- Manages named OAuth accounts for every built-in
/loginprovider: Anthropic, GitHub Copilot, Kimi For Coding, Meta, OpenAI, OpenAI Codex (legacy), OpenRouter, Radius, and xAI. - Saves named API key accounts for the same providers except OAuth-only OpenAI Codex (legacy).
- Selects an account—or Pi's default login—independently for each provider and Pi session, with an optional
PI_ACCOUNToverride for scripts and concurrent Pi processes. - Saves a default account for each provider to use in new sessions without changing existing sessions.
- Restores session selections after resume or reload while allowing concurrent sessions to use different accounts.
- Applies provider-specific credentials, endpoints, headers, and model availability through Pi's built-in providers.
- Refreshes rotating credentials and verifies the effective authentication before reporting success.
- Starts sessions without waiting for routine account selection or provider activation, while gating each provider's first use on verified authentication.
- Offers only the verified active OAuth credential to compatible in-process consumers.
- Writes credentials atomically to a private local file and fails closed for only the affected provider when activation fails.
- Imports legacy
pi-codex-accounts.jsondata while retaining the source file for rollback.
📦 Install
Install persistently:
pi install npm:@narumitw/pi-accounts
pi-codex-accounts is deprecated and archived under deprecated/.
Do not load both packages because each can refresh the same rotating Codex credential.
Migrate an existing installation with:
pi uninstall npm:@narumitw/pi-codex-accounts
pi install npm:@narumitw/pi-accounts
Try without installing permanently:
pi -e npm:@narumitw/pi-accounts
Try this package locally from the repository root:
npm --workspace @narumitw/pi-accounts run build
pi -e ./packages/pi-accounts
The package declares dist/index.ts, so an unbuilt local checkout must be built before Pi loads the package directory.
🚀 Quick start
Run /accounts in TUI or RPC mode.
Log in to save a named account, then choose Set default account → provider → account to use it when starting a new Pi session.
Use Switch … account to change only the current session.
For a process-local, non-interactive choice, run PI_ACCOUNT=work pi or PI_ACCOUNT=personal pi --print "..." after saving those accounts.
The name applies to every supported provider used by that process; a provider without that name fails closed rather than falling back to another account.
Routine account selection and provider activation continue in the background after Pi starts.
The first prompt, model switch, or /accounts operation that needs a provider waits for its current selection and authentication to finish; activation failures still fail that provider closed before a request is sent.
🔌 Supported providers
| Provider | Provider ID | Account-specific behavior |
|---|---|---|
| OpenAI | openai |
Pi's current ChatGPT subscription OAuth or OpenAI API keys, using the native Responses provider |
| OpenAI Codex (legacy) | openai-codex |
ChatGPT Plus/Pro OAuth, OAuth-only native-provider bridge, and Codex WebSocket invalidation |
| Meta | meta |
Muse subscription device login with provider-owned API key minting and renewal, or Meta API keys |
| Anthropic | anthropic |
Claude Pro/Max OAuth without interfering with Anthropic API-key auth after returning to default |
| GitHub Copilot | github-copilot |
Individual or Enterprise login, credential-derived API endpoint, and account-specific available models |
| Kimi For Coding | kimi-coding |
Kimi Code subscription OAuth with provider-owned Bearer-header authentication |
| OpenRouter | openrouter |
OAuth that mints a persistent account API key, or manually entered OpenRouter API keys |
| Radius | radius |
Gateway-bound OAuth with credential-specific dynamic model-catalog refresh and selected-model rebinding |
| xAI | xai |
SuperGrok or X Premium OAuth with the native xAI provider and model catalog |
💬 Commands
Run /accounts to log in with OAuth, add an API key account, switch provider accounts for the current Pi session, set defaults for new sessions, or remove saved accounts in TUI or RPC mode.
Arguments are ignored for compatibility; print and JSON modes provide no account-manager output.
Login uses Pi's native OAuth flow, including device codes and cancellation, with equivalent RPC dialogs.
Choose Add API key account, select a provider, name the account, and enter its key.
Blank keys and keys containing whitespace or terminal controls are rejected; !command expressions are not resolved by named profiles.
default is reserved for Pi's built-in login.
Switching affects account identity for the chosen provider, not the model or other sessions' selections.
Replacing an existing provider/account name or removing an account requires confirmation.
Removal returns the current session's affected selection to default, but other sessions using the removed shared credential fail closed until they choose another account or default; see Security and privacy.
⚙️ Settings
Open /accounts → Set default account, choose a provider, then select a saved account or Pi built-in login.
The picker shows the saved default and saves your selection immediately; leaving it before selecting changes nothing.
Defaults are user-wide settings in <getAgentDir()>/pi-accounts.json (normally ~/.pi/agent/pi-accounts.json); project overrides are not supported.
The existing providers.<provider-id>.active field stores the saved account name, so previous values remain compatible without migration.
An absent or null value means Pi's built-in login; named values must match a saved account under that provider.
The menu clears active when you choose Pi built-in login or remove the configured default account. Editing a saved default while PI_ACCOUNT is set does not change that process's effective selection.
New sessions, including /new, forks, and clones, snapshot these defaults.
A session's saved selection takes precedence on restart, resume, and /reload; changing a default never switches that session or other existing sessions.
Login and Switch … account still change only the current session, not the startup default.
PI_ACCOUNT=<name> takes precedence over both saved defaults and session selections, including on resume and /reload.
It is read when a session starts and remains fixed for that session; changing the process environment afterward affects only subsequently started sessions.
The value must be a saved account name: 1–64 letters, numbers, dots, underscores, or hyphens (surrounding whitespace is trimmed); default is reserved and invalid.
A missing name for any supported provider when used, or an invalid value, fails that provider closed rather than using a default or an existing session choice. Providers not managed by this package remain unchanged.
Unset PI_ACCOUNT and restart Pi to restore the session's saved selection. The override does not change saved default-selection fields or record its value in session entries; routine session initialization can still snapshot defaults, and OAuth refresh can update credentials in pi-accounts.json.
If a saved session selection is invalid or cannot be written, a valid PI_ACCOUNT can still authenticate without repairing that underlying error; unset it and restart Pi to recover through /accounts.
/accounts and the account status indicate the environment as the effective source without echoing raw environment input. While it is set, interactive switching is unavailable; login can save an account but does not replace the override, and removing its selected credential fails that provider closed immediately.
Sessions predating session-local selection support snapshot the current default once because their historical choice cannot be inferred. If a manually configured default names a missing account, affected sessions fail closed until you choose an available account or Pi's built-in login.
Saves preserve unknown settings fields and use the credential store's cross-process lock and private atomic replacement. Within one store, asynchronous reads follow queued writes; failed writes leave the queue usable. Malformed settings block saves instead of being replaced, and failed saves do not change the session's authentication.
🔌 Extension account protocols
Trusted extensions can use Pi's in-process pi.events API to discover named accounts,
activate one for the current session, and request a verified active OAuth credential.
Topology and activation replies contain account identifiers, never credential material;
the separate OAuth credential offer does contain secrets. No package import or
credential-file read is needed. See the repository's Named Account Protocols v1
for topology and activation; it links to the separate OAuth readiness and credential-source
specifications. These references describe payloads, reply timing, cancellation, and consumer safety.
Activation does not change the user-wide default. When PI_ACCOUNT is set, protocol activation
(including restoration of Pi auth) returns activation_failed without changing the selection.
Otherwise, activation may persist a session selection even if authentication later fails or the
requester cancels, leaving that provider fail-closed.
Consumers must bound their wait: a missing reply or timeout is not proof that activation
did not happen, and dependent work must not proceed without confirmation. pi.events
is not a trust boundary; load only trusted extensions.
🔒 Security and privacy
The extension refreshes each selected OAuth account through the provider's refresh() implementation and converts it through toAuth().
Named API key accounts use their saved key directly, without OAuth refresh or conversion, and never publish an OAuth credential offer.
It applies the returned API key, headers, and endpoint, then verifies the effective runtime state before reporting success.
If refresh, conversion, provider overlay, or verification fails, the extension installs a non-secret failing runtime credential and aborts turns for that provider.
It does not silently fall back to Pi's built-in login, an environment API key, or another named account.
Other providers remain independent and usable.
Selecting default removes the package-owned runtime override and restores the exact provider registration that existed before activation.
Pi's built-in credentials are never deleted.
For named accounts that resolve to an API key, the extension also verifies authentication for every available provider model.
A conflicting provider or model authentication header fails that provider closed, even if the conflicting model is not selected.
The extension does not rewrite these settings: remove the conflicting configured header or choose default from /accounts to recover.
Unrelated headers and native headers that resolve to the selected credential remain supported.
Session selections are stored as versioned, non-model custom entries in Pi's session JSONL.
The entries contain only provider IDs and account names, not OAuth credentials.
The owning Pi session ID prevents a fork or clone from treating copied parent entries as its own selection.
Resume and reload restore entries owned by the same session, while /tree navigation keeps one session-wide selection instead of changing authentication by branch.
A malformed matching selection entry fails managed providers closed until /accounts writes a valid snapshot; recovery defaults any other managed provider whose selection could not be trusted.
The extension implements the versioned oauth:credential-source:v1 protocol for compatible current-account consumers such as usage reporters.
It offers a fresh in-memory clone only after the named OAuth credential has produced and verified active runtime authentication for the exact Pi session.
Pending, default, stale, failed, replaced, reloaded, and shut-down states offer nothing.
Compatible consumers can use the extension-neutral oauth:credential-readiness:v1 protocol to await the required session-owned activation before requesting an offer.
The protocols do not persist or log the offer, which contains neither the account name nor extension identity.
Consumers must match its access token and provider metadata against freshly resolved runtime authentication.
Without a compatible consumer, account activation works unchanged and no credential is requested.
Pi extensions run with the user's process privileges; pi.events does not isolate installed extensions.
Install only trusted extensions because any extension can read user files and process memory.
The protocol reduces accidental credential coupling; it is not a sandbox.
GitHub Copilot's availableModelIds are projected into the active provider model list.
Switching Copilot accounts rebuilds the projection from the complete pre-overlay model catalog.
A currently selected model that is unavailable to the named account is rejected before the turn starts.
Kimi's provider-owned OAuth returns an Authorization: Bearer header instead of an API key.
The extension applies that header and installs a non-secret runtime selector to displace Pi's default Kimi credential.
Activation verifies the effective Bearer header and fails closed before a turn if Pi does not retain it.
OpenRouter's provider-owned OAuth returns a persistent API key represented as an OAuth credential with an empty refresh token. The extension preserves that exact provider credential separately from manually entered API key accounts.
OpenAI (openai) and OpenAI Codex (legacy, openai-codex) have independent accounts and session selections; credentials are not copied between them.
Meta's provider renews minted API keys using the saved identity token; an expired identity session requires a new named OAuth login.
Radius OAuth is bound to the active radius provider's configured gateway.
The extension refreshes Radius's dynamic model catalog when the session or effective named credential changes, rebinds a retained selected model to its refreshed endpoint, and fails closed if the selected model disappears or catalog publication fails.
Selecting default refreshes the Radius catalog against Pi's restored credential after a named account was active.
Shutdown removes named Radius authentication and makes a bounded attempt to restore the default catalog; Pi's next catalog refresh remains the recovery path if the gateway is unavailable during shutdown.
Custom gateways configured for the radius provider ID are supported, while arbitrary Radius provider aliases are not.
🗄️ Storage and migration
The canonical file is:
~/.pi/agent/pi-accounts.json
When PI_CODING_AGENT_DIR is set, the file is stored at $PI_CODING_AGENT_DIR/pi-accounts.json instead.
Its versioned structure keeps shared credential maps and startup defaults under separate provider IDs; see Settings for editing and session precedence.
Credential values are private and must not be committed.
When neither canonical nor legacy storage exists, reads return an empty store without creating a directory or file.
The first account change creates the private canonical file.
On first load, if pi-accounts.json does not exist and released pi-codex-accounts.json does, the extension:
- Locks and validates the legacy file.
- Repairs its permission to
0600. - Copies all Codex credentials and the active name into the
openai-codexprovider section. - Atomically installs private
pi-accounts.json. - Retains the private legacy file for rollback.
If both files exist, pi-accounts.json takes precedence and the legacy file is not imported again.
The retained legacy refresh token may become stale after pi-accounts rotates it, so rollback can require a new Codex login.
Older releases reject files that contain provider sections added later.
Before downgrading, stop Pi, back up the file, and remove provider sections the older release does not support (including meta and openai).
Releases without API key profiles also reject accounts with type: "api_key"; remove those accounts and any defaults referring to them from the downgrade copy.
Rollback
- Switch managed providers to
defaultand stop Pi sessions usingpi-accounts. - Remove
pi-accountsfrom the Pi package configuration. - Reinstall the deprecated
@narumitw/pi-codex-accountspackage only if necessary. - Reauthenticate Codex if the retained legacy refresh token was rotated.
Older pi-accounts releases ignore session selection entries and return to the retained provider-level compatibility default.
Select the desired account again after rollback because switches made by this version did not update that global default.
The repository preserves the predecessor implementation under deprecated/pi-codex-accounts for reference.
It is excluded from active workspace checks, version bumps, and publishing.
🚧 Limitations
- Named API key profiles cover only the supported login providers, not every API-key-only provider in Pi.
- Named profiles store literal keys locally; continue using Pi's
auth.json, environment variables, or!commandsecret-manager resolution for externally managed secrets. - It does not rotate accounts automatically, evade quotas, or report usage.
- It does not support arbitrary custom providers.
- Live OAuth login and model requests depend on provider service availability and account entitlement.
- Sessions created before session-local selection support cannot recover a historical per-session choice; see Settings.
🗂️ Package layout
packages/pi-accounts/
├── src/ # Authoritative implementation and helpers
│ ├── index.ts # Thin Pi entrypoint
│ └── accounts.ts # Account activation and session lifecycle
├── dist/ # Generated Jiti runtime
├── scripts/build-runtime.mjs # Runtime builder
└── test/ # Behavior and lifecycle coverage
The generated runtime is built from src/index.ts and does not import back into src.
🔎 Keywords
Pi extension, Pi coding agent, OAuth accounts, API key profiles, OpenAI, Meta, Muse, OpenAI Codex, ChatGPT Plus, ChatGPT Pro, Anthropic, Claude Pro, Claude Max, GitHub Copilot, GitHub Enterprise, Kimi For Coding, Kimi Code, OpenRouter, Radius, xAI, Grok, SuperGrok, X Premium, account switching.
📄 License
MIT.
See LICENSE.