Install
Inside DeepSeek Harness, with dsh-market
dsh plugin --profile web add dshmarket
Or from the command line
dsh plugin --profile web add @astervolans/dsh-opencode-go-usage
Installing runs third-party code with your own permissions — it can read your files, use your credentials and reach the network. Review the source first, and pin a commit (github:owner/repo#sha) when you can.
README
English | 中文
⚡ Get an OpenCode GO plan: buycodingplan.com
This is a fork of yumusb/dsh-opencode-go-usage (Astervolans/dsh-opencode-go-usage), published as the scoped package
@astervolans/dsh-opencode-go-usage. The difference from upstream: a time bar under each usage bar (see Features). The settings namespace, HTTP routes and command name are stilldsh-opencode-go-usage(unchanged), so migrating from upstream needs no configuration changes; only thedsh.bundle.patchentry'snamemust be the resolvable scoped package name, ordsh webaborts at startup withCannot find package.
A DSH (DeepSeek Harness) plugin that watches your OpenCode GO plan quota — the $10/month subscription that gives you usage limits on open-source models (rolling 5-hour, weekly, and monthly windows).
Compatible with DSH 0.1.7 and above, including the 0.2.0 line — declared in
peerDependencies as >=0.1.7-rc.1 <0.3.0, so DSH enforces it. See
Compatibility for the version matrix, what changed in 1.5.0, and what
to do on any other runtime.
Features
- Sidebar widget — a live widget pinned at the bottom of the DSH web sidebar (
sidebar.footer.actionslot) showing three usage bars: rolling (5h), weekly, and monthly, each with a relative countdown to its window reset. When the sidebar is collapsed it shrinks to a compact percentage badge. - Time bars — under each usage bar sits a time bar splitting the window into two: gray on the left is time already elapsed (growing rightward) and blue on the right is time remaining (shrinking leftward); at reset the gray fills up and starts over. The time bar matches the usage bar exactly in height and radius (5 px / 3 px) and differs only in color and direction; both segments are rounded on all four corners, so the outer ends and the junction where they meet are rounded. The gray reuses the quota bar's track token (
--dsw-alias-border-l2), so both stacked grays are the same shade; the blue segment uses--dsw-static-deepseek-450(#5686fe). Reading quota used against time elapsed shows at a glance whether you are burning quota faster or slower than the clock. Hover for the elapsed/remaining percentages. /opencode-gochat command — prints the same numbers as text inside any conversation.- Same-origin proxy — the host registers
GET /opencode-go/usage, forwards to the official GO gateway with your API key. The key never reaches the browser and no CORS is involved. x-opencode-sessionfix — at runtime, injects the real harness session id into OpenCode GO gateway chat requests (the gateway 400s requests without it). No DSH file patching; survives upgrades.
The x-opencode-session fix
The GO gateway rejects chat-completion requests that lack the x-opencode-session header (HTTP 400 MissingSessionID), and DSH's llm-pi-ai adapter never sends it. Instead of patching DSH's installed files (which every DSH upgrade overwrites), this plugin:
- wraps
globalThis.fetchonce, and - listens to DSH's official
llm/streamwaterfall event to capture the per-call harness session id (options.sessionId, filled bydsh-agent-loop).
The gateway base is resolved from the called provider's own live config (llm-pi-ai.providers.<route>.baseURL, falling back to this plugin's baseUrl) — no host names are hard-coded — so only requests to that call's gateway receive the header, with the real per-conversation session id.
- Toggle:
injectSessionHeaderin the plugin config (defaulttrue). - Observability:
GET /opencode-go/usagereturnssessionHeader: { active, count, diag };diagreports what the runtime saw, e.g.streamSeen(handledllm/streamcalls),lastStream(provider, session-id presence, base and its source),requests/injected/missed(wire fetches that carried the context, got the header, or fell through on a URL mismatch).
How it works
The plugin is a dual-half DSH package:
| half | file | role |
|---|---|---|
| host (Node) | lib/index.js |
registers the /opencode-go/usage web route (ctx.webServer) and the /opencode-go command (ctx.commands); resolves the key through DSH credentials; caches the upstream call (30 s) |
| browser | lib/client.js |
a hand-authored window.__ModuleLoader__.load({ id, factory }) bundle that waits for and registers into the sidebar.footer.action list slot, then polls the same-origin route every 60 s |
How the sidebar widget loads
DSH 0.1.7-rc composes the widget through the official client-modules scan: the
package's dsh.client declaration + exports["./client"] are picked up per
loader entry, the bundle is served from the /plugins/... combo routes, and
its boot-graph row lands in the window.__DSH_BOOT__ manifest by the host
itself — no manual bundling or index injection. (The 0.1.1/0.1.2 line used a
self-hosted /dsh-opencode-go-usage/client.js route + webServer.tapIndex row
injection; that was removed because 0.1.7-rc replaced the array-shaped boot
manifest with a { rev, entries, batches } object that must come from
client-modules only.)
Compatibility
| plugin | DSH runtime | host half | browser half |
|---|---|---|---|
| 1.5.0 (current) | 0.1.7 and above, including the 0.2.0 line |
Config schema read by the loader; config taken from the plugin entry (no ctx.settings.register/settings.get) |
composed by client-modules from the package's dsh.client declaration |
| 1.4.0 | 0.1.7-rc.1, 0.1.7-rc.2 |
same as 1.5.0 | same as 1.5.0 |
| 1.3.3 | 0.1.1-rc.2, 0.1.2-alpha.2 (the pre-0.1.7 line) |
ctx.settings.register namespace + ctx.settings.get reads |
self-hosted bundle route + webServer.tapIndex boot-graph row |
The supported runtimes are declared in peerDependencies:
"peerDependencies": {
"@deepseek-ai/schemastery": "^3.18.4",
"@deepseek-ai/dsh": ">=0.1.7-rc.1 <0.3.0"
}
DSH validates the @deepseek-ai/dsh range against the running runtime before an entry
activates. From 0.2.0 on that validation is a hard gate: every @deepseek-ai/dsh /
@deepseek-ai/dsh-* peer is tested with includePrerelease, and a mismatch disables the
whole profile row (the host logs disabling profile plugin row …) rather than
half-working. The >=0.1.7-rc.1 <0.3.0 window therefore spans the 0.1.7 line and the
whole 0.2.0 line; the 1.4.0 value 0.1.7-rc.1 || 0.1.7-rc.2 cannot, because any
0.1.x range stops at <0.2.0. On an unlisted runtime, either install the plugin version
that targets it (1.3.3 for the pre-0.1.7 line) or accept the risk explicitly and
restart DSH:
dsh plugin allow-version # exact-version exemption for name@version on this dsh
Two runtime notes for the supported versions:
- Settings page / hot-edit / legacy import — the config fields are marked volatile,
which needs schemastery ≥
3.18.4. Both official packages are declared as peers rather than dependencies, so a fresh install resolves the copy DSH itself ships; a profile that still hoists an older3.18.2gets no auto-generated settings page, no hot-edit, and no migration of a legacy~/.dsh/settings.yamlsection (that section stays behind insettings.yaml.imported). Either way the plugin activates and reads its config from the profile entry (see Config reference). - Widget — no manual bundling or index injection is involved: the host composes it
from the package's
dsh.clientdeclaration and serves it over/plugins/...(see How the sidebar widget loads).
Requirements
- DSH
0.1.7or newer, including the0.2.0line, installed and thewebprofile booted at least once (~/.dsh/profiles/webexists) - Node.js ≥ 18 (for
fetch) - An OpenCode GO subscription and its API key
Install (official DSH flow)
Requirements: DSH 0.1.7 or newer, including the 0.2.0 line, installed with the web
profile booted once, Node.js ≥ 18, an OpenCode GO subscription.
# 1. install the package into your web profile (pnpm; enable via corepack if needed)
dsh plugin --profile web add @astervolans/dsh-opencode-go-usage
# 2. store your GO API key as a DSH credential
# (create the key at https://opencode.ai/auth)
# → add to ~/.dsh/.credentials.yaml:
# OPENCODE_GO_API_KEY: sk-...
# 3. restart `dsh web` and hard-refresh the browser page
That's it for the quota widget and /opencode-go command. The CLI reconciles
the package's dsh.bundle.patch into the profile's bundle stack automatically
— no manual cordis.patch.yml editing, no symlinks.
From GitHub Packages
Every release also publishes to GitHub Packages
(https://npm.pkg.github.com), automatically, from the release workflow. That
registry requires authentication even for public packages, so add an .npmrc
— project-level, or ~/.npmrc — with a personal access token that has at least
read:packages:
@astervolans:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_PAT
dsh plugin --profile web add @astervolans/dsh-opencode-go-usage
Two things to know before choosing this route:
- The package is private by default on GitHub, even though the repository is public. The first release has to be flipped to public once, at package settings → Danger Zone → Change visibility.
- Because installation needs a token, npmjs.org (no token, no scope line) stays the friendlier path. GitHub Packages is a mirror, not a replacement.
From a checkout
dsh plugin --profile web add /path/to/dsh-opencode-go-usage
New to DSH plugins? Follow the user guide (Chinese, step-by-step).
Usage
- Widget: read it. Collapsed sidebar → percentage badge; expanded → three progress bars with reset countdowns.
- Command:
/opencode-goin any conversation prints the three windows as text.
Config reference
All fields are marked volatile, so they show up in Settings → Plugins → Plugin configuration and hot-apply without a restart:
| key | default | description |
|---|---|---|
apiKeyEnv |
OPENCODE_GO_API_KEY |
credential reference / env var name for the API key |
baseUrl |
https://opencode.ai/zen/go |
gateway base URL |
cacheMs |
30000 |
host-side upstream cache TTL |
updateCheck |
true |
check npm for newer versions (widget + command show a hint; the plugin never self-upgrades) |
injectSessionHeader |
true |
inject the runtime x-opencode-session header for GO gateway chat calls |
The volatile marking is guarded:
.volatile()exists from schemastery 3.18.4, and DSH resolves a plugin's dependencies from the profile first. With an older hoisted copy (3.18.2) the plugin still activates and reads its config from the profile entry, but the auto-generated settings page and hot-edit stay off — reinstall the plugin (or update@deepseek-ai/schemasteryin the profile) to get them. Either way the config can be set in the profile patch:
- id: dsh-opencode-go-usage
config:
cacheMs: 60000
The usage API
GET https://opencode.ai/zen/go/v1/usage with Authorization: Bearer <key>:
{
"usage": {
"rolling": { "status": "ok", "percent": 0, "resetsAt": "2026-08-14T07:51:13Z" },
"weekly": { "status": "ok", "percent": 1, "resetsAt": "2026-08-17T00:00:00Z" },
"monthly": { "status": "ok", "percent": 22, "resetsAt": "2026-08-21T13:05:13Z" }
}
}
Developing / modifying the widget
The browser half is a hand-authored factory bundle (window.__ModuleLoader__.load), because out-of-tree client plugins have no public build pipeline yet. It may only require() modules from the shell module table (react, react/jsx-runtime, and the registered client packages). Edit lib/client.js directly, then restart dsh web and refresh the page — the bundle revision hash changes and the shell loads the new file.
Host changes (lib/index.js) need only a dsh web restart.
Troubleshooting
- Widget missing after restart → hard-refresh the page (
Cmd/Ctrl+Shift+R); the boot graph is injected per page load. /opencode-go/usagereturns 502 with "no API key" → configure the key in~/.dsh/.credentials.yaml.- Gateway 401/403 → the key is invalid or the subscription lapsed; check the credential.
- Widget shows an error string → hover the collapsed badge or read the error line in the expanded widget.
License
MIT
Comments
Comments live in GitHub Discussions. Sign in with GitHub to post or react.