dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DeepSeek Harness (DSH) 插件:把本机 opencode 生态接入 DSH 模型菜单——OpenCode Go 订阅(经 opencode-api-plugin 网关)+ Zen 免费档(内嵌 opencode2api,无需网关运行)
English | 中文
A DeepSeek Harness (DSH) plugin that wires the local opencode ecosystem into DSH's model menu with two provider routes:
Route · Endpoint · What it carries opencode-gateway · http://127.0.0.1:8787/v1 (the local opencode-api-plugin gateway) · Everything the gateway aggregates: the OpenCode Go subscription (reading the machine's local opencode login state), OpenCode Zen, and any custom upstreams in gateway.config.json opencode-free · in-process loopback proxy (default http://127.0.0.1:10102/v1) · The Zen free tier via the companion gateway's Go opencode2api service (default http://127.0.0.1:18080) — full client fingerprinting, egress rotation, anonymous lane; mode: "embedded" falls back to the inlined opencode2api port driving the machine's own opencode serve
By design this plugin ships no usage panel; quota display lives in the gateway's /api/usage and the tray.
off and refuses none; grok/muse serve /responses only, which the gateway converts transparently).service mode (default) the free route spawns the companion gateway's bin\opencode2api.exe when port 18080 has no listener and reclaims only a process it spawned; a service already running (e.g. started by the gateway's own start scripts) is reused untouched. embedded mode does the same for opencode serve.service mode it is an authenticated passthrough to the Go service, in embedded mode it wraps the LocalProxy functions. The listener never holds the host's event loop, so headless tasks still exit.service mode) needs the companion gateway checkout next to this plugin — it resolves ..\opencode-api-plugin\bin\opencode2api.exe + bin\config.json automatically (server key included), and spawns the service when absent. A custom location or an already-running service works via free.serviceUrl / free.serviceExePath / OPENCODE2API_SERVER_KEYembedded mode needs the opencode CLI installed; opencode auth login is recommended (better model list and limits, anonymous works too)start-gateway.cmd, start-tray-hidden.vbs or the tray)opencode-go upstream to be enabled with `prefix: "go"` (the gateway's example config does this) and OPENCODE_GATEWAY_KEY= in ~/.dsh/.env. Missing either one makes the Go card's refresh report "missing credential / HTTP 401 / empty listing" explicitly instead of failing silently.Open the app, choose Plugins in the sidebar, click Add plugin and paste a local path:
D:/Agent/dsh-opencode-gatewayor a Git spec (once published):
github:<owner>/dsh-opencode-gateway#v0.1.0Click Install, wait for pnpm, then click Enable now.
pnpm dsh plugin --profile web add "D:/Agent/dsh-opencode-gateway"The installer runs a full compatibility sandbox install, which can take many minutes on this machine; hand-composing the profile is faster — see the Chinese README.
The plugin installs no settings UI; configuration lives in the composition layer (the profile's cordis.patch.yml, ~/.dsh/profiles/desktop/ on the desktop app) and takes effect on restart:
- id: opencode-gateway
name: dsh-opencode-gateway
config:
gateway:
provider: opencode-gateway
displayName: OpenCode Go
baseURL: http://127.0.0.1:8787/v1
apiKeyEnv: OPENCODE_GATEWAY_KEY
hiddenModels: ["free/"]
models: []
free:
provider: opencode-free
mode: service # "embedded" drives local opencode serve instead
serviceUrl: http://127.0.0.1:18080
serviceApiKeyEnv: OPENCODE2API_SERVER_KEY
manageService: true # spawn the companion bin/opencode2api.exe when absent
proxyPort: 10102
# embedded-mode only:
# serverUrl: http://127.0.0.1:10001
# opencodePath: opencode
# manageBackend: true
# disableTools: true
# internalAllowedTools: [webfetch, bash, websearch]DSH settings gain an OpenCode Gateway section (plugin v0.2.0+): one card per route (OpenCode Go / OpenCode Free) managing that route's model menu.
/v1/models, OpenCode Free via the inlined proxy's backend listing). A refresh waits up to 60s; 401/timeout/network failures report their reason, and only a 200 with an empty list shows the empty-listing notice. The Go card keeps only the `go/` prefix — the gateway's opencode-go upstream reading https://opencode.ai/zen/go/v1/models, which measured one for one against the official endpoint; the gateway-side upstream must therefore use prefix: "go" (the example config does). That same listing mixes in two other sources under look-alike prefixes, neither of which is the Go subscription, and both are dropped: opencode-go/ is the local opencode serve backend's catalog (passed through by the local-free upstream, whose modelRewrite maps only opencode/), and free/ is that same backend's Zen catalog, paid models included. Reading opencode-go/ as an alias spelling of go/ is what put opencode-go/step-5-preview-free — a local entry the official endpoint does not list — on the Go card.go/beam-free); one it drops disappears; hiddenModels remains available for extra exclusions inside the official set. There is no confirmed-unavailable blacklist — "this id cannot serve a turn" is a measurement of one moment, not a property of the model, and freezing it would both hide newly added official models and keep misreporting after the endpoint fixes something. An official id is kept even if a given request fails; the gateway's own error is then reported verbatim.service mode the Go service's /v1/models (its companion config sets models.free_only: true, so the listing is exactly the free tier, bare ids like big-pickle); in embedded mode it comes from opencode models, run with OPENCODE_API_KEY removed and reduced to the opencode/ prefix. Do not derive this from the local backend's provider config: that view is credentialed — with the key set, its opencode provider also reports the paid catalog (measured 84 models), and filtering it by cost both missed free models it does not list at all (beam-free, deepseek-v4.1-flash-free, glm-5.3-flash-free, nemotron-3.5-super-vl-free) and kept region-blocked ones (cat-jam-free, muse-spark-1.3-contributor-free). The answer is cached for 10 minutes and falls back to the last good result, so one failed read never reads as "the free tier vanished".models — the menu falls back to the shipped list (or the composition models, when configured).Precedence: saved settings models (even an empty array) > composition cordis.patch.yml models > shipped defaults. Saved entries that carry only id/name/context are enriched by the host from the measured registry (missing effort vocabulary and vision flags; an explicit reasoningEfforts: false is never touched). Backend-structural fields (serverUrl / proxyPort / opencodePath …) ask for a restart when changed on the page; the model menu applies live.
~/.dsh/.env)Variable · Meaning OPENCODE_GATEWAY_KEY · The gateway's Bearer token; any non-empty value works while the gateway runs without a token. A refresh without it reports "no credential resolved…" directly; a chat request fails before network I/O with MISSING_CREDENTIAL OPENCODE2API_SERVER_KEY · The Go opencode2api service's server key (free route, service mode); falls back to the companion gateway's bin/config.json server_keys[0] OPENCODE_ZEN_API_KEY · (embedded mode) Passed to the backend as OPENCODE_API_KEY to unlock paid models; falls back to the local opencode login state (v0.1 reads the process environment only, not the credentials seam)
models[] entriesField · Meaning id · The model id the endpoint accepts, forwarded verbatim name · Selector label contextWindow / maxTokens · Capacities; maxTokens also becomes that model's per-request default output cap reasoningEfforts · false declares a non-reasoning model; a dict {level: wire spelling}, where only off may be empty (send nothing) input · ["text","image"] declares image input
PiAiAdapter reads the current profiles snapshot once per operation; changed discovery facts atomically replace the registration — no restart, no disturbed in-flight stream.Request; responses stream back out. A mid-stream drop after the headers are flushed destroys the socket instead of throwing ERR_HTTP_HEADERS_SENT into the server callback.retryPolicy defaults to normal/maxRetries 0 — retries belong to the gateway (subscription routes) or the inlined proxy (free route), never both.reasoning_content, reasoning + reasoning_details, and inline think tags all flow through pi-ai's delta parser into DSH's reasoning-delta collapse (the gateway strips Go routes' inline tags server-side)./api/usage + the tray.That notice is a safe failure: the menu is never modified. Check these three things in order; any one of them being wrong yields an empty listing.
Check · Expected · If wrong The gateway's opencode-go upstream in gateway.config.json · "enabled": true, "prefix": "go"; apiKey either omitted (local login) or a Go-subscribed key · apply the template below, then tray → Restart service curl -H "Authorization: Bearer " http://127.0.0.1:8787/v1/models · contains go/... · only free/...: prefix or switch not applied; HTTP 401: token mismatch OPENCODE_GATEWAY_KEY in ~/.dsh/.env · equals the gateway token · set it, then restart DSH
Minimal gateway-side config:
{
"port": 8787,
"token": "<gateway token>",
"upstreams": [
{
"id": "opencode-go",
"type": "opencode-go",
"prefix": "go",
"enabled": true
}
]
}Notes:
prefix: "opencode-go" lists opencode-go/..., which the plugin drops as the local backend's catalog by design.apiKey the gateway reads the local opencode auth login state (~/.local/share/opencode/auth.json, the opencode-go entry, else opencode); switch accounts with opencode auth login, or set apiKey / OPENCODE_GO_API_KEY on the gateway.no credential resolved from OPENCODE_GATEWAY_KEY means .env is missing the variable.OpenCode Free card never goes through the 8787 gateway.Two common chat-side errors:
403 An active OpenCode Go subscription is required: the gateway's opencode-go key has no Go subscription (the listing may still be fine — the catalog is public). Omit apiKey to use the local opencode auth login state, or use a subscribed key, then restart the gateway.Connection error.: the gateway process is not running — not a key problem. curl http://127.0.0.1:8787/health must answer {"status":"ok"}.pnpm install
pnpm run build # tsdown → lib/index.js (Host, ESM); tsc -b → lib/types
pnpm run test # vitestThe lib/ build output is committed alongside the sources (git installs need no build) — run pnpm run build before committing code changes.
MIT © 2026 — contains the embedded opencode2api implementation ported from opencode-api-plugin (itself ported from TiaraBasori/opencode2api, MIT; license copy at assets/LICENSE.md), and the OpenCode Go registry measurements from dsh-protocom-api (MIT).