# dsh-opencode-go-sub **Full OpenCode Go subscription + OpenCode Zen free-tier model catalog directly in DeepSeek Harness โ€” same experience as the official opencode client.** Server-side plugin that registers two provider routes: `opencode-go` (subscription) and `opencode-zen` (free). - ๐Ÿงพ **Live model list** โ€” `listModels()` fetches `https://opencode.ai/zen/go/v1/models` in real time (same source as the opencode client's `/models`). Models added/removed upstream sync automatically; if the endpoint is unreachable it falls back to a built-in snapshot, so the list is never empty. - ๐Ÿ”‘ **Zero-config key (core)** โ€” the plugin discovers the subscription key from DeepSeek Harness itself, **with no dependency on any cordis service injection** (never fails with `without inject`). Resolution chain: 1. Environment variables `OPENCODE_GO_API_KEY` / `OPENCODE_ZEN_API_KEY` / `OPENCODE_API_KEY` (env wins, matching the DSH credentials layer); 2. `~/.dsh/.credentials.yaml` โ€” the DSH credentials file. Enter the key once inside DSH and you're done; **no opencode client, no terminal env vars needed**; 3. dsh-api-key-pool's `pool-config.json` (keys under `pools.opencode-go` / `pools.opencode`). On startup the plugin logs the key source (e.g. `key resolved from dsh-credentials:OPENCODE_GO_API_KEY`) so you can confirm. - โšก **Full capability** โ€” streaming, `reasoning_content` passthrough, tool calls, token usage (incl. cache hits), 429/5xx auto-retry. - ๐Ÿšฆ **Errors surface verbatim** โ€” 403s (region block, "Enable models hosted in China" not enabled, etc.) show the gateway's original message in the GUI. - ๐Ÿ”ง **Version compatibility** โ€” implements the newer DSH adapter interface (`prepareCall`), compatible with `dsh-llm` 0.1.0-rc.6 through 0.1.1-rc.x (works on both older and newer DeepSeek Harness releases). ## Installation From GitHub: ```sh dsh plugin --profile web add github:Yuanloss/dsh-opencode-go-sub ``` From a local clone: ```sh dsh plugin --profile web add file:/path/to/your/clone/dsh-opencode-go-sub ``` After a **restart of `dsh web`**, the model picker shows two groups: `OpenCode Go๏ผˆ่ฎข้˜…๏ผ‰` (subscription) and `OpenCode Zen๏ผˆๅ…่ดน๏ผ‰` (free), with models addressed as `opencode-go/` and `opencode-zen/`. ## Key configuration **As long as you enter the key once anywhere inside DSH, the plugin reads it automatically โ€” no manual file editing needed.** An API key entered in the DSH settings/credentials UI is written by the DSH credentials service into `~/.dsh/.credentials.yaml`; the plugin reads that file directly (key name `OPENCODE_GO_API_KEY`). Any of the three entry points below works โ€” they all end up the same way: 1. **Enter it in the DSH UI (recommended)** โ€” paste the key in the credentials/model settings (equivalent to writing one line into `~/.dsh/.credentials.yaml`): ```yaml OPENCODE_GO_API_KEY: sk-your-go-subscription-key ``` Get the key at opencode.ai console โ†’ your workspace โ†’ OpenCode Go. 2. Environment variable (set before restarting `dsh web`): ```sh OPENCODE_GO_API_KEY=sk-your-go-subscription-key ``` 3. Install [dsh-api-key-pool](https://github.com/xiaozhe7772222/dsh-api-key-pool) and add the key under `pools.opencode-go.keys` in its `pool-config.json`. > Resolution priority: **env vars > `~/.dsh/.credentials.yaml` > key pool**. The plugin reads the credentials file directly and does not depend on any cordis service injection. > โš ๏ธ **Important โ€” configuring the key vs. adding a provider route:** > - **Configuring the key**: entering the key in any UI / credential setting is fine; the plugin picks it up automatically. > - **Adding a provider route**: do **not** manually add a custom provider named **`opencode-go`** in Settings โ†’ Models. The plugin already registers the `opencode-go` (subscription) and `opencode-zen` (free) routes; a same-named entry triggers a `DUPLICATE_ADAPTER` conflict and can prevent the plugin from loading. > - **DSH's built-in pi-ai ships a same-named catalog entry (the common trap)**: DSH's built-in multi-provider adapter pi-ai itself ships a provider whose id is `opencode-go`, and the Models page offers it as a one-click "installed provider". Enabling it writes an `opencode-go` entry (usually only `apiKeyEnv`) under `llm-pi-ai.providers` in `~/.dsh/settings.yaml` โ€” colliding with this plugin's `opencode-go` route. Because llm-pi-ai registers **all** of its providers (e.g. the Volcano Engine (Agent Plan) / SiliconFlow providers you added) **in one all-or-nothing batch**, one duplicate `opencode-go` makes the whole batch fail โ€” your other added model providers silently disappear from the picker. **Fix**: delete that same-named `opencode-go` provider on the Models page (or remove the whole `llm-pi-ai.providers.opencode-go` block from `settings.yaml`, keeping `OPENCODE_GO_API_KEY` in `.credentials.yaml`). The plugin scans for this conflict at startup and prints deletion guidance; pi-ai re-registers automatically on save, no restart needed. > - The plugin ships both groups โ€” **no manual provider entry needed**. If you do want to reach another OpenAI-compatible endpoint, use a different route name (e.g. `ocgo`, `my-gateway`). ## Model presentation (two groups, ordered, friendly names) The picker shows two groups by provider (matching the official opencode client): - **OpenCode Go (subscription)** โ€” 30 models, ordered by vendor, with friendly names and descriptions. - **OpenCode Zen (free)** โ€” 5 free models (key=`public`, **no subscription required**), marked "๏ผˆๅ…่ดน๏ผ‰". The subscription list syncs the live gateway catalog (`/zen/go/v1/models`); known models render in the built-in order, **newly appearing models append to the end of the group**; offline fallback to the snapshot. ## Known limitations - **gpt-5.6-luna / grok-4.5 go through the Responses API** โ€” the gateway's chat/completions route returns 500 / "Endpoint is unavailable" for them, so the plugin auto-routes them to `/v1/responses`. Both are hosted by OpenAI / xAI and are **blocked from mainland China** (403 `unsupported_country_region_territory`) โ€” you need an exit in a supported region (the VPN must be system-wide TUN mode; a browser-only proxy does not affect the dsh web process). - **qwen3.7-max** is served upstream through the Anthropic `/messages` dialect and may fail via chat/completions in this version โ€” extend the `MESSAGES_ONLY` logic yourself, or use qwen3.8-max / qwen3.7-plus instead. - **`x-opencode-session` header (mandatory for OpenCode Go since 2026-09-05)**: the gateway requires a stable per-conversation id on every inference request; missing it returns `400 MissingSessionID`. The plugin automatically reads the conversation id DSH's agent-loop injects (`GenerateOptions.sessionId`, shaped like `session-`), normalizes it to the bare UUID, and sends it on every request; non-conversation direct calls fall back to a process-stable UUID, so no configuration is needed. If you reach opencode-go through DSH's built-in pi-ai route instead of this plugin, discussion [#5495](https://github.com/deepseek-ai/deepseek-harness/discussions/5495) offers a `sessionHeader: x-opencode-session` profile option. - **Image attachments are not forwarded** (image blocks are ignored, same as dsh-opencode-zen); text-only conversations are unaffected. - Calls are subject to subscription quotas and upstream region policy: GPT / Grok models return 403 from mainland China; newer DeepSeek versions require enabling "Enable models hosted in China" in the opencode.ai console first. ## DSH version compatibility The adapter contract is verified against the **real registries of `dsh-llm` 0.1.2-rc.1 and 0.1.5-rc.1**: ```sh npm test # contract + conflict + session-header + real-registry integration node test-integration.mjs "" # regress a candidate version ``` - Covers five real call paths: `registerAdapter` (incl. `DUPLICATE_ADAPTER` semantics), `listProviders`, `imageRequestPricing`, `listModels`, `prepareCall`. - **`imageRequestPricing` must be implemented**: `dsh-token-meter`'s `measure()` calls it unconditionally, and `dsh-compaction-basic`'s `prepareCompaction()` reaches it โ€” so a missing method throws `TypeError: adapter.imageRequestPricing is not a function` and **breaks context compaction**. Adapters extending `dsh-llm`'s abstract class inherit a default; this zero-dependency plugin declares it explicitly (returning `undefined` = no image pricing declared). Fixed in v0.1.10. - After upgrading DSH, run the integration test first: `xxx is not a function` or `INVALID_*` means the contract drifted and the adapter needs updating. - Note: DSH 0.1.5-rc.1 itself does **not** ship the `x-opencode-session` fix yet โ€” opencode-go working depends on this plugin's implementation, not on the DSH version. --- [ไธญๆ–‡็‰ˆ README](README.md) ยท [English README](README.en.md)