@a42null/dsh-opencode-zen-plugin
English | 简体中文
DeepSeek Harness plugin that auto-adapts the OpenCode Console (Zen) model catalog.
- GitHub: https://github.com/A42Null/dsh-opencode-zen-plugin
- CNB package registry: https://cnb.cool/A42Null/dsh-opencode-zen-plugin
- npm package:
@a42null/dsh-opencode-zen-plugin(published on npmjs; see Install below)
Paste an OpenCode Console API Key under Settings → Plugins and the Console's chat models become selectable in DSH. Model list, context windows, endpoint routing, and reasoning parameters are handled automatically.
What it does not do: it cannot make *-free models work (they only run inside the OpenCode client), it skips the jev-* SystemOne models (not chat-capable), and paid models still require a funded Console account.
Features
- Zero-config model catalog: fetches
https://opencode.ai/zen/v1/models(unauthenticated) on start and on a timer (default 300s); models appear in the DSH model picker automatically - Automatic endpoint routing (per the official docs table):
gpt-*/grok-*/muse-spark*→ OpenAI Responses API (/zen/v1/responses)claude-*plusqwen3.8-flash/qwen3.7-max/qwen3.7-plus/qwen3.6-plus/qwen3.5-plus→ Anthropic Messages (/zen/v1/messages)gemini-*→ Google GenerateContent (/zen/v1/models/<id>:streamGenerateContent?alt=sse)deepseek/glm/minimax/kimi/qwen3.8-max/big-pickle/*-free→ OpenAI-compatible (/zen/v1/chat/completions)
- Metadata sync: context windows, output budgets, and reasoning tiers come from models.dev with a 7-day disk cache (
~/.opencode-zen/models.dev.json); an embedded static catalog guarantees the picker is never empty offline - Free models hidden by default:
*-freebelongs to OpenCode's in-client free tier; through the Console API it returns403 FreeTierError("free tier can only be used from within OpenCode"), so it is excluded from the picker unless you opt in - Readable billing/permission errors: insufficient funds (402), model access disabled (403), and the free-tier gate (403 FreeTierError) map to distinct error codes with actionable advice instead of a blanket "invalid API key"
- Reasoning passthrough:
off/minimal/low/medium/high/xhigh/max, mapped to each protocol's native field (reasoning_effort,thinking.budget_tokens,thinkingConfig.thinkingBudget,reasoning.effort) - Full harness stream protocol: streamed text / reasoning / tool-call blocks, usage accounting, stop-reason mapping, and error classification (AUTH, QUOTA, FREE_TIER_BLOCKED, MODEL_ACCESS_DISABLED, RATE_LIMIT, INVALID_REQUEST, SERVER, TIMEOUT, TRANSPORT, CONTEXT_WINDOW_EXCEEDED, EMPTY_RESPONSE)
- Tool calls & image input: vision models (claude- / gemini- / deepseek-v4-flash-vision, ...) accept image attachments
- Stream watchdog: aborts after 120s first-byte / 300s idle to avoid hung requests
- UI language follows DSH: the settings card renders in Simplified Chinese or English, matching the interface language
Install
Option 1 is the recommended one: no git, no registry setup. Only option 3 (GitHub source) needs git on the machine.
Option 1 — install from npmjs (recommended)
dsh plugin add @a42null/dsh-opencode-zen-plugin
Package page: https://www.npmjs.com/package/@a42null/dsh-opencode-zen-plugin
Option 2 — prebuilt tarball (no git needed)
Paste this into DSH under Settings → Plugins → Add plugin, or run it on the command line:
dsh plugin add https://github.com/A42Null/dsh-opencode-zen-plugin/releases/latest/download/dsh-opencode-zen-plugin.tgz
The URL always points at the newest release (the asset name carries no version, so it never rots).
Option 3 — GitHub source (⚠️ requires git installed and on PATH)
dsh plugin add github:A42Null/dsh-opencode-zen-plugin
pnpm resolves that repository with git ls-remote. Without git on the machine the install fails with
Command failed: git ls-remote "git+ssh://git@github.com/…" / 'git' is not recognized as an internal or external command —
use option 1 or 2 instead, or install Git for Windows first.
Option 4 — local directory (development only)
dsh plugin add D:\DSH\插件开发\opencode-zen-dsh-plugin, or install the local package directory via the plugin manager.
Optional — CNB mirror
The same package is also published to the CNB registry https://cnb.cool/A42Null/dsh-opencode-zen-plugin. The name is scoped, so point that scope at CNB (don't change the default registry — that would affect every other package):
npm config set @a42null:registry https://npm.cnb.cool/A42Null/dsh-opencode-zen-plugin/-/packages/
dsh plugin add @a42null/dsh-opencode-zen-plugin
Optional — GitHub Packages (same name, token required to read)
The same package is also published to GitHub Packages.
GitHub's npm registry requires a token even to read a public package — so treat it as a mirror/archive rather than the primary install route:
npm config set @a42null:registry https://npm.pkg.github.com
npm config set //npm.pkg.github.com/:_authToken <GitHub classic token with read:packages>
dsh plugin add @a42null/dsh-opencode-zen-plugin
Both mirrors only redirect @a42null/*; every other package keeps using your default registry. Undo with npm config delete @a42null:registry.
Then
- Restart DSH (or reload plugins); the plugin registers provider
opencode-zen(override viaproviderIdincordis.patch.yml)
The package ships a dsh.bundle.patch (cordis.patch.yml), so installation writes the registration row into the profile patch automatically, and dsh.client provides the Web settings card.
Settings (Settings → Plugins → OpenCode Zen → Configure)
The DSH settings UI is contributed by the plugin's client half (
lib/client.js): this plugin registers into theplugins.row.configslot (key =<package name>#<row id>), which gives the opencode-zen row under Settings → Plugins a Configure control. With only the host half (lib/index.js) the models work, but no settings UI appears anywhere.
| Field | Description | Default |
|---|---|---|
apiKey |
OpenCode Console API Key — required. Get it at https://opencode.ai/console | empty |
baseUrl |
OpenCode Zen gateway base URL | https://opencode.ai/zen |
includeFreeModels |
Include free (*-free) models — off by default, since they only work inside the OpenCode client |
false |
refreshSeconds |
Catalog auto-refresh interval in seconds (30–86400) | 300 |
Saving takes effect immediately — no DSH restart. As a fallback the key can also come from the OPENCODE_ZEN_API_KEY environment variable (the setting wins).
Auth
All endpoints use Authorization: Bearer <apiKey>; Anthropic endpoints also send x-api-key + anthropic-version: 2023-06-01, Google endpoints also send x-goog-api-key. The model-list endpoint is unauthenticated, so models are visible before a key is set — actual calls then fail with a clear AUTH error.
Runtime status & cache
| Path | Content |
|---|---|
~/.opencode-zen/adapter-status.json |
Catalog health snapshot: status / total / updatedAt / lastError |
~/.opencode-zen/models.dev.json |
models.dev metadata cache (7-day TTL, atomic tmp+rename writes) |
~/.opencode-zen/plugin-load.json |
Last plugin-load marker (build / loadedAt / pid) — tells you which code version the running DSH actually loaded |
~/.opencode-zen/errors.log |
Raw adapter failures: time, HTTP status, protocol, model, error code, body excerpt (128 KB cap, auto-cleared) |
Error codes follow DSH's canonical taxonomy:
QUOTAfor an exhausted balance (drives the client's "quota used up" copy and the shell notice),AUTHfor key problems,FREE_TIER_BLOCKEDfor the free-tier gate,MODEL_ACCESS_DISABLEDfor missing model entitlement. The last two surface the plugin's own explanatory text in the UI.
Troubleshooting
| Symptom | Action |
|---|---|
Install fails with git ls-remote / 'git' is not recognized |
The machine has no git, and github: source installs need it → use option 1 (npmjs) or option 2 (prebuilt tarball), or install Git for Windows first |
| AUTH error | Missing or invalid API Key → paste one under Settings → Plugins → OpenCode Zen → Configure; the model list needs no auth, so models stay visible without a key |
| FREE_TIER_BLOCKED (403 FreeTierError) | A *-free model was selected; the free tier only works inside the OpenCode client → disable "include free models" or use a paid model |
| QUOTA (402 Insufficient account funds) | The key is valid but the Console balance is empty → top up at https://opencode.ai/console |
| MODEL_ACCESS_DISABLED (403 Model access is disabled) | The Console account lacks access to that model → pick a different model |
| RATE_LIMIT (429) | Too many requests → retry later |
| opencode-zen missing from Settings | The client half is not loaded (older install / no restart) → make sure lib/client.js exists and package.json declares dsh.client plus the "./client" export, then restart DSH and reload the page |
| SERVER / TRANSPORT / TIMEOUT | Gateway/network issue → retry later; check lastError in adapter-status.json |
| CONTEXT_WINDOW_EXCEEDED | Input exceeded the model's context window → shorten the session or pick a larger-window model |
| Empty or stale model list | Live-list failures fall back to the embedded static catalog; inspect adapter-status.json |
jev-* reports INVALID_REQUEST |
That family uses the SystemOne endpoint and is not chat-capable; excluded from the catalog |
| After upgrading to the scoped name: the Configure entry disappears / the API key reads as empty | The overlay row in ~/.dsh/profiles/desktop/cordis.patch.yml still names the old package → set its name to @a42null/dsh-opencode-zen-plugin and restart DSH (the API key is preserved) |
Startup fails with client-modules: duplicate factory registration / 1 entry did not activate |
0.3.5 shipped a client module id that no longer matched the package name → upgrade to 0.3.6 or newer; the boot-failure dialog's "disable third-party plugins, back up the profile patch and restart" button is the emergency recovery |
Uninstall/update fails with ERR_PNPM_TARBALL_URL_MISMATCH |
The lockfile's tarball source differs from the active registry → pin the scope: npm config set @a42null:registry https://registry.npmjs.org |
Releasing (maintainers)
Push a tag that matches the version in package.json and the package is published to npmjs, the CNB registry and GitHub Packages automatically (workflow .github/workflows/publish.yml):
npm version patch --no-git-tag-version # or edit version in package.json manually
git commit -am "chore: release vX.Y.Z"
git tag vX.Y.Z
git push origin main --follow-tags
All three registries are idempotent (an existing version is skipped, so re-pushing a tag never fails) and independent: one registry failing does not block the other two. The job verifies at the end that the version really is on npmjs.
npmjs auth: Trusted Publishing (recommended, no long-lived token)
On https://www.npmjs.com/package/@a42null/dsh-opencode-zen-plugin → Settings → Trusted Publisher:
| Field | Value |
|---|---|
| Publisher | GitHub Actions |
| Organization | A42Null |
| Repository | dsh-opencode-zen-plugin |
| Workflow name | publish.yml |
| Environment | cnb (must match the workflow's environment:; leave blank for any) |
CI then exchanges an OIDC token for publish credentials — no token needed (the workflow already declares permissions: id-token: write). This is npm's own migration target, since it plans to remove bypass-2FA direct publishing in January 2027.
Environment secrets (configured on GitHub; no token is ever stored in the repository)
Settings → Environments → cnb → Environment secrets:
| Secret | Required | Notes |
|---|---|---|
CNB_TOKEN |
yes | CNB access token (enable the package-registry scope when creating it) |
GH_TOKEN |
no (recommended) | GitHub classic token / PAT with write:packages, used to publish to GitHub Packages; when absent the built-in GITHUB_TOKEN is used (the repository's Actions workflow permissions must then be read/write) |
npmjs needs no secret at all (it uses OIDC trusted publishing; if the OIDC exchange fails the job prints a configuration checklist and fails). The three registries are independent and idempotent: one failing does not block the other two. The CNB npm username is always cnb (change CNB_USERNAME in the workflow if yours differs). A tag that does not match package.json fails the job, so a wrong version can never be published.
Docs
- OpenCode Console models & endpoints: https://opencode.ai/v2/docs/console/models
- OpenCode Console (paid gateway): https://opencode.ai/console
No comments yet. Be the first to write one.