PYYUPSK

a self-taught dev from Thailand

Pongsakorn Thipayanate · Samutsakhon, TH ·webring

tutorial

Introducing opencode-aipass

A dynamic opencode plugin for aipass-proxy that discovers AIPass models from GET /v1/models at startup — no more hand-maintained models in opencode.json.

view .mdopen in claudeopen in chatgpt

I built aipass-proxy to expose AIPass through the OpenAI GET /v1/models and POST /v1/chat/completions endpoints. The first guide — Using aipass-proxy with opencode — showed how to wire it to opencode with a manual provider.aipass.models block in opencode.json.

That worked, but it had a flaw: you had to copy model IDs by hand, and they drifted. When AIPass added gpt-5.6-sol or renamed gemini-3.1-flash-lite, you had to edit the file again. opencode-aipass fixes that. It fetches the list from your local proxy and registers the provider for you.

Important Warning

Same warning as before. AIPass terms prohibit use of the platform API outside the official interface (section 3.4). This proxy — and the plugin that talks to it — uses the API outside that interface. AIPass can suspend your account. Not legal advice — read the terms before you use either project.

The problem with the old way

The original setup looked like this in opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "aipass": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "AIPass",
      "options": {
        "baseURL": "http://localhost:47871/v1",
        "apiKey": "unused"
      },
      "models": {
        "gemini-3.1-flash-lite": { "name": "Gemini 3.1 Flash Lite (Unlimited)" },
        "claude-opus-5@azure": { "name": "Claude Opus 5" },
        "gpt-5.6-terra": { "name": "GPT-5.6 Terra" }
      }
    }
  }
}

Every key under models had to match an ID from GET /v1/models exactly. The proxy already knew the real list — you were just copying it. The plugin removes that duplication.

What the plugin does

opencode-aipass v0.1.1 is a provider plugin for opencode v2. On startup it calls GET /v1/models on your proxy, turns the response into Model.Info entries with tools: true (the proxy emulates tool calls via <tool_call>), and registers them with ctx.provider.transform. opencode then shows them in the picker as aipass/<model-id>.

It then keeps the list in sync:

  • Polls GET /v1/models every 60 seconds. If the id set changed, it calls ctx.provider.reload.
  • Persists the last good list in ctx.storage. If the proxy is offline at next startup, it restores that cached list and logs a warning instead of showing nothing.
  • Exposes a manual refresh as command aipass:refresh (TUI) and tool aipass_refresh (for agents).

You no longer edit opencode.json when AIPass ships a model.

Prerequisites

  1. aipass-proxy v0.2.1 installed and running at http://localhost:47871 (or your custom port). See the project page for curl -sL https://fasu.dev/aipass-proxy | bash, aipass-proxy setup, and aipass-proxy start.
  2. opencode v2 and @opencode/plugin ^2.0.8 (peer effect ^4.0.0-rc.112, zod ^4.1.8).

Verify the proxy first:

curl http://localhost:47871/v1/models | head -c 500

If that returns {"data": [...]} you are ready for the plugin.

Install

Option A — Project-local (fastest, no publish)

This is how the aipass-proxy repo itself uses the plugin. Point opencode at the source file:

{
  "$schema": "https://opencode.ai/config.json",
  "plugins": ["./plugin/src/index.ts"],
  "model": "aipass/gpt-5.6-sol"
}

Use this when you clone pyyupsk/aipass-proxy and want the plugin without publishing. The provider id is aipass and the model id is whatever the proxy returns — e.g. aipass/gpt-5.6-sol.

Option B — Published package

If you want the plugin in your own project without the proxy repo:

# one-time publish from the proxy repo
cd plugin && npm publish
# then in your project
opencode plugin add opencode-aipass
{
  "$schema": "https://opencode.ai/config.json",
  "plugins": ["opencode-aipass"],
  "model": "aipass/gpt-5.6-sol"
}

Both options behave the same. Pick A for local dev, B for sharing across projects.

Options

You can override the defaults:

{
  "plugins": [{
    "package": "./plugin/src/index.ts",
    "options": {
      "baseURL": "http://localhost:47871/v1",
      "name": "AIPass",
      "refreshInterval": 60000,
      "timeoutMs": 5000
    }
  }]
}
  • baseURL — proxy base URL. Default http://localhost:47871/v1.
  • name — provider display name in opencode. Default AIPass.
  • refreshInterval — ms between GET /v1/models polls. Default 60000.
  • timeoutMs — fetch timeout in ms. Default 5000.

Change baseURL if you run the proxy on a different port or expose it on your LAN.

How it works (in 6 steps)

  1. Calls GET {baseURL}/models with AbortSignal.timeout(timeoutMs). On failure, keeps the previous list and warns to console.
  2. Builds a provider via @opencode/ai/providers/openai-compatible with baseURL and apiKey: "unused", advertising capabilities: { tools: true, input: ["text"], output: ["text"] }.
  3. Registers with ctx.provider.transform — this is what populates the model picker.
  4. Persists models.map(m => m.id) to ctx.storage. On next cold start with proxy offline, restores from storage.
  5. Every refreshInterval re-fetches and diffs id sets. Only calls ctx.provider.reload when the set actually changed (avoids noisy events).
  6. Adds aipass:refresh and aipass_refresh for on-demand reloads (aipass: 23 models).

Source is ~130 lines in plugin/src/index.ts — easy to audit.

Try the refresh

In the opencode TUI, open the command palette and run aipass:refresh. From an agent, call the tool:

{ "tool": "aipass_refresh", "input": {} }

Response:

{ "content": "aipass: 23 models" }

Add a model in AIPass, run the command, and it appears without restarting opencode.

Migrate from the manual config

If you followed the first guide:

  1. Delete the entire provider.aipass block from opencode.json (including models).
  2. Add the plugins entry from above.
  3. Set "model": "aipass/<id>" to any id from curl http://localhost:47871/v1/models.
  4. Restart opencode. Run aipass:refresh once to prime the cache.

You can keep both configs side-by-side to compare, but the plugin will overwrite a manual aipass provider with the same id via editor.remove(providerID) before editor.add.

When not to use the plugin

  • Proxy not at http://localhost:47871 and you do not want to set baseURL.
  • You want a curated subset of models, not the full AIPass list. The plugin registers everything the proxy returns; manual models lets you hide entries.
  • Offline-first setups where GET /v1/models is never reachable and you prefer an explicit list over a cached one.

Ideas once it is running

Same ideas from the first post, now with less friction:

  • Pin aipass/gemini-3.1-flash-lite as your default for quick edits; switch to aipass/claude-opus-5@azure or aipass/gpt-5.6-sol for planning.
  • Test Thai-specific models without editing config — they just appear when AIPass makes them available.
  • Run aipass-proxy install-service on a home server and point every machine’s baseURL at that host. One plugin, one proxy, every device in sync.

Where to find it

pyyupsk/aipass-proxyGitHub

OpenAI-compatible chat-completions proxy in front of AIPass (https://de.aipass.net)

★ 0 · ⑂ 0 · TypeScript

← All writings