Opencode plugin adding support for Actually Open Router
  • TypeScript 100%
Find a file
2026-07-11 00:24:20 -06:00
src attempt to fix fatal crash 2026-07-11 00:24:20 -06:00
.gitignore feat: opencode-aor plugin — three-protocol aor providers with discovery 2026-07-09 12:19:13 -06:00
LICENSE Add LICENSE 2026-07-09 14:32:02 -06:00
package.json feat: opencode-aor plugin — three-protocol aor providers with discovery 2026-07-09 12:19:13 -06:00
README.md Merge remote-tracking branch 'origin/dev' into feat/cost-limit-passthrough 2026-07-11 00:00:19 -06:00

opencode-aor

An opencode plugin for Actually Open Router (aor).

It exposes your aor instance as three providers in opencode — one per client API shape — and populates each provider's model list from the server's GET /v1/models response:

Provider npm package API shape
aor @ai-sdk/openai-compatible OpenAI
aor-anthropic @ai-sdk/anthropic Anthropic
aor-google @ai-sdk/google Google

All three point at the same aor base URL and use the same sk-aor-... key. Each model is registered under whichever provider(s) match the api field aor reports for it, so a request is sent in the protocol the serving upstream expects.

Install

Add the plugin to your opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    ["git+https://git.trevhost.com/devtrev/opencode-aor.git", { "baseURL": "http://localhost:8000/v1" }]
  ]
}

baseURL is your aor instance's OpenAI-style root (the part before /chat/completions). Defaults to http://localhost:8000/v1; can also be set via the AOR_BASE_URL env var.

Auth

There are two ways to provide your sk-aor-... key. Use either or both.

aor's GET /v1/models is public, so the plugin discovers models at startup with no key. The three providers (AOR / AOR (Anthropic) / AOR (Google)) then appear in opencode's /connect list. Select one, paste your sk-aor-... key, and that provider is live in the model picker immediately — no restart.

Paste the key once per provider you use. opencode stores credentials per-provider-ID, so the three api shapes are three separate /connect entries — just like openai, anthropic, and google are separate entries. (You don't need to connect a provider whose api shape you won't use.)

AOR_API_KEY env var (set once for all three)

export AOR_API_KEY=sk-aor-...

This covers all three providers' request keys at once, and is also sent on the discovery fetch (harmless for public endpoints; useful if you re-enable auth gating on /v1/models). A /connect-stored key takes precedence over the env var for that provider.

opencode does not load .env files, so AOR_API_KEY must be set in the real process environment (shell rc, direnv, or a wrapper).

How it works

On startup the plugin's config hook fetches GET ${baseURL}/models (no key needed for the default public endpoint), groups the returned models by their api field (openai / anthropic / google), and injects three providers into opencode's config — each carrying the models that speak its protocol.

A model backed by providers of more than one api type is reported by aor with an array api field and is registered under each matching provider — pick whichever protocol you prefer from the model picker.

Notes / limitations

  • Discovery is at startup. If aor isn't running when opencode starts, the providers get no models and won't appear in /connect or the picker. Start aor, then (re)start opencode.
  • Auth headers are chosen automatically by the AI SDK package per provider (Authorization: Bearer for openai-compatible, x-api-key for anthropic, x-goog-api-key for google). aor accepts the sk-aor-... key in any of these headers.
  • No protocol translation. aor forwards the client's request shape verbatim to the matching upstream. Sending an OpenAI-shape request to a model only served by an anthropic upstream would not work — which is why each model is only registered under the protocol(s) its upstreams speak.

Cost tracking & context window

The plugin reads cost and limit metadata from aor's GET /v1/models response and injects them into opencode's per-model config. This enables:

  • $ spent display in opencode's TUI — opencode computes cost client-side from token counts × the pricing we inject.
  • % context used display — opencode computes token usage against the context window limit we inject.

These values come from aor's primary (highest-priority) provider for each model. When limits or pricing vary greatly across providers for the same model, consider splitting into separate catalog models in aor's config so clients get deterministic metadata. See aor's docs/requirements-key-budgets.md for details.

Note: opencode v2's runner currently hardcodes cost: 0 in its step-end events. Cost tracking works on the v1 processor path. This is an opencode gap, not a plugin limitation.

LICENSE

MIT

CONTRIBUTING

See devtrev/CONTRIBUTING