Skip to content

Instantly share code, notes, and snippets.

@j-greig
Last active March 9, 2026 10:10
Show Gist options
  • Select an option

  • Save j-greig/d53b288ba7c43c6bb0cf64632f8d98a1 to your computer and use it in GitHub Desktop.

Select an option

Save j-greig/d53b288ba7c43c6bb0cf64632f8d98a1 to your computer and use it in GitHub Desktop.
How to auth a Pi agent to Claude without an API key

How to auth a pi agent to Claude without an API key

If you embed a pi agent in your own app (like WibWob-DOS does), you do NOT need a raw ANTHROPIC_API_KEY. The agent can piggyback on your existing pi subscription login — the same OAuth token pi itself uses — with zero extra configuration.


How it works

When you run pi login normally, pi does an OAuth flow (browser → Anthropic/Google/etc.) and writes the resulting access token + refresh token to:

~/.pi/agent/auth.json

That file looks roughly like:

{
  "anthropic": {
    "type": "oauth",
    "access_token": "...",
    "refresh_token": "...",
    "expires": 1712345678000
  }
}

Your embedded agent just reads the same file. No API key needed.


The three lines that make it work

In your agent session initialisation:

import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";

const authStorage = AuthStorage.create();            // reads ~/.pi/agent/auth.json
const modelRegistry = new ModelRegistry(authStorage); // finds available models via those creds

const agent = new Agent({
  // ...
  getApiKey: (provider) => authStorage.getApiKey(provider),  // called per-request
});

AuthStorage.create() with no arguments defaults to ~/.pi/agent/auth.json. That is the same file pi writes when you log in via the CLI.

authStorage.getApiKey(provider) handles the full priority chain:

  1. Runtime override (CLI --api-key flag)
  2. api_key entry in auth.json
  3. OAuth token in auth.json — auto-refreshed if expired, with file locking so multiple concurrent instances do not race
  4. Environment variable (ANTHROPIC_API_KEY etc.)
  5. Custom fallback resolver (models.json custom providers)

For a standard claude-subscription login, path 3 fires and you get a fresh access token transparently whenever the previous one expires.


Files involved

Your app (WibWob-DOS as reference implementation)

src/services/wibwob-agent-session.ts The main wiring. Creates AuthStorage, ModelRegistry, AgentSession, and Agent. Passes the getApiKey callback. Also handles jailed coding tools, TUI tools, session persistence, and streaming transcript updates.

Key section is the initialize() method — search for AuthStorage.create().

pi-coding-agent package (installed globally via npm/brew)

These are compiled JS — no source available publicly, but the paths below are where they live on a typical macOS pi install:

@mariozechner/pi-coding-agent/dist/core/auth-storage.jsAuthStorage class. Reads/writes auth.json with file locking. Handles OAuth token refresh (with lock to prevent race conditions across multiple pi instances). Priority chain for getApiKey() lives here.

@mariozechner/pi-coding-agent/dist/core/model-registry.jsModelRegistry. Enumerates configured providers, calls getApiKey to check which are live, surfaces the list to resolveModel() in wibwob-agent-session.ts.

@mariozechner/pi-coding-agent/dist/core/agent-session.jsAgentSession. Wraps Agent with session persistence, tool management, compaction, and retry logic. The getApiKey callback set on Agent is called here on every API request.

pi-ai package (OAuth provider implementations, bundled inside pi-coding-agent)

@mariozechner/pi-ai/dist/utils/oauth/anthropic.js — claude-subscription PKCE OAuth flow (the one most pi users are logged in with)

@mariozechner/pi-ai/dist/utils/oauth/github-copilot.js @mariozechner/pi-ai/dist/utils/oauth/google-gemini-cli.js @mariozechner/pi-ai/dist/utils/oauth/openai-codex.js — OAuth flows for other supported subscription providers

@mariozechner/pi-ai/dist/utils/oauth/index.jsgetOAuthProvider(id) registry that AuthStorage calls to look up a provider by name (e.g. "anthropic") and get its refresh/getApiKey implementation

@mariozechner/pi-ai/dist/utils/oauth/pkce.js — shared PKCE helpers used by all the above

@mariozechner/pi-ai/dist/env-api-keys.jsgetEnvApiKey(provider) — the env-var fallback (ANTHROPIC_API_KEY etc.)

Runtime credential store (data file, not source)

~/.pi/agent/auth.json — Written by pi login. Read by AuthStorage.create(). Contains type:"oauth" entries per provider (or type:"api_key" if you stored a raw key instead). This file is the entire reason no API key is needed in your app config.


What breaks if auth fails

authStorage.getApiKey() returns undefined when the OAuth token is invalid (revoked, or auth.json missing — not just expired, which auto-refreshes fine).

ModelRegistry then finds no available models for that provider.

The first session.prompt() call throws:

No model available. Check provider auth.

In WibWob-DOS this surfaces in the agent window status bar.

Fix: run pi login again in your terminal. That rewrites auth.json and the embedded agent picks it up on next app restart (or after calling authStorage.reload()).


Vanilla pi uses identical code

This is not a WibWob-DOS invention. Pi's own CLI entry point (main.js) does exactly the same thing — lines 507-508:

const authStorage = AuthStorage.create();
const modelRegistry = new ModelRegistry(authStorage, getModelsPath());

The only extra thing pi's CLI adds is handling the --api-key flag by calling authStorage.setRuntimeApiKey(provider, key), which inserts it at priority 1 above everything else. If you want to support that flag in your embedded agent too, that's the call to make.

So: embedded agent, vanilla pi CLI, same file, same class, same auth.json.


No special config needed in your app

You do not need to set ANTHROPIC_API_KEY. You do not need to pass credentials into your app config. As long as the machine has a valid pi login (auth.json exists and is not revoked), the agent just works.

This is the entire mechanism WibWob-DOS uses for its embedded Wib & Wob agent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment