pi-provider-opencode-console
v2.0.0
Published
Pi providers for OpenCode Console and Go: OAuth device-code login, live model discovery
Downloads
1,222
Maintainers
Readme
pi-provider-opencode-console
A Pi provider for OpenCode Console that signs in via the OAuth 2.0 device authorization grant and discovers account-specific models from the org-scoped /api/config endpoint. Ships two providers mirroring OpenCode's two auth systems: OpenCode Console (opencode-console) and OpenCode Go (opencode-go-console) — both sign in exclusively via device-code (OAuth).
Breaking change in 2.0: the
sk-service-key (API-key) auth method was removed. See Authenticate for the migration note.
Features
- OAuth 2.0 device authorization grant (RFC 8628) sign-in via
/login opencode-console(and/login opencode-go-console— Go reuses the shared Console device flow); pi's/loginlists both providers as "…(OAuth device code)", distinct from pi's native API-key option - Org-scoped sign-in: the device grant requests organization scoping, so the workspace is chosen during browser approval when the Console grants it; otherwise a single-org account self-selects and multi-org accounts pick explicitly after sign-in
- Console: per-org model discovery from the org-scoped
GET /api/configendpoint - Go: the public
GET https://opencode.ai/zen/go/v1/modelscatalog (the auth-ignoredlitelist), requests targeting the Go gateway - Public catalog rows are enriched from models.dev and drop disabled/deprecated ids
- Routes chat requests to the right upstream wire API per-model (
anthropic-messages,openai-completions,openai-responses,google-generative-ai) - Delegates streaming and parsing to pi-ai's built-in implementations
- Auto-refreshes the OAuth access token within 5 minutes of expiry, single-flight, with rotated tokens mirrored across both provider entries (shared device-flow session)
- Org selection at sign-in and via
/opencode-console switch-org - Adds
/opencode-consoleand/opencode-go-consoleforstatus,refresh,logout(Console alsoswitch-org) - Optional
OPENCODE_CONSOLE_SERVERoverride for self-hosted consoles
Install
pi install npm:pi-provider-opencode-consoleTo try a local checkout:
pi -e ./src/index.tsAuthenticate
OpenCode has two auth systems — Console (which absorbed Zen) and Go (the subscription gateway). Both providers here sign in with an OAuth device-code session on the shared Console device flow; pi stores exactly one credential per provider id in ~/.pi/agent/auth.json, so the stored oauth credential owns the provider.
Note: this extension is OAuth-only by design. pi's generic "Enter API key" login is not offered for these providers — if you want plain API-key access with a static model list, use pi's built-in
opencode/opencode-goproviders instead.
Migrating from service keys (pre-2.0): service-key auth was removed in 2.0. Any
{ "type": "api_key", "key": "sk-…" }entries foropencode-console/opencode-go-consolein~/.pi/agent/auth.jsonare now inert (pi reports "Provider is not configured"). Delete those entries and run the device-code sign-in below.
Device-code (OAuth) sign-in
/login opencode-console
# …or, for the Go gateway:
/login opencode-go-consolePi prints a user code and the verification URL. Approve the sign-in in your browser — if the Console grants an org-scoped session, the organization is the one you pick during browser approval (pi shows it as "server-scoped at sign-in"). Un-scoped single-org accounts self-select; multi-org accounts are prompted to pick one (persisted to auth.json). Go orgs are the Console org — the session is shared. A server-scoped session cannot switch organizations locally; sign in again to choose a different one.
Model catalogs by provider:
| Provider | Catalog |
| --- | --- |
| opencode-console | org-scoped GET {server}/api/config with Authorization: Bearer + x-org-id |
| opencode-go-console | public GET https://opencode.ai/zen/go/v1/models (lite list), called with the session's access token |
Manage
Run the provider commands to manage credentials:
/opencode-console status # default; shows auth method (device-code OAuth session), account, org, token expiry
/opencode-console refresh # re-pull the org-scoped catalog
/opencode-console switch-org # un-scoped sessions only; re-pick the org (scoped sessions: sign in again)
/opencode-console logout # clear the credential
/opencode-go-console status # auth method + account + org
/opencode-go-console refresh # re-pull the public Go catalog
/opencode-go-console logout # clear the Go credential; Console session preservedConfiguration
| Env var | Default | Notes |
| --- | --- | --- |
| OPENCODE_CONSOLE_SERVER | https://opencode.ai/console | Override for self-hosted consoles. Persisted to auth.json on first sign-in. |
Why separate providers?
The built-in opencode and opencode-go providers in Pi handle API-key-only access with static model lists. This extension deliberately registers distinct ids (opencode-console, opencode-go-console) so the built-ins stay untouched, while adding the OAuth device-code session, live catalogs, and org awareness that the built-ins do not have — mirroring the behavior of the sister project (opencode-copilot-chat).
Architecture
pi extension entry (src/index.ts)
├─ registerProvider("opencode-console", { oauth, refreshModels, streamSimple }) [OAuth-only: no apiKey method]
│ ├─ oauth.login → shared device-code flow + org picker → auth.json[providerId]
│ ├─ oauth.refreshToken → canonical refresh + sibling mirror (shared session)
│ ├─ refreshModels → org-scoped /api/config
│ └─ streamSimple → streamConsoleWithSession (session loader)
├─ registerProvider("opencode-go-console", { oauth, refreshModels, streamSimple }) [OAuth-only]
│ ├─ refreshModels → public /models on the Go gateway (lite list, session token)
│ └─ streamSimple → streamConsoleWithSession (Go entry, Console-entry fallback)
└─ registerCommand for both providers
streamConsole → delegates to pi-ai's lazy APIs
├─ anthropicMessagesApi
├─ openAICompletionsApi
├─ openAIResponsesApi
└─ googleGenerativeAIApiUpstream parity research and adopted-behavior citations live in docs/opencode-auth-parity.md.
URL routing is normalized per API kind in src/endpoint.ts so each SDK's URL composition produces the right endpoint:
| API kind | baseUrl normalization |
| --- | --- |
| anthropic-messages | strip trailing /v1 (Anthropic SDK appends /v1/messages) |
| openai-completions | keep /v1 (SDK appends /chat/completions) |
| openai-responses | keep /v1 (SDK appends /responses) |
| google-generative-ai | keep /v1beta (Google SDK appends /models/<id>:streamGenerateContent) |
Identity headers (x-opencode-org-id, x-opencode-client, x-opencode-request) are injected per request in streamConsole.
Development
npm install
npm run check
npm run packageProject
License
MIT
