@goodandready/dsh-opencode-provider 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -0
- package/LICENSE +21 -0
- package/NOTICE +15 -0
- package/README.md +268 -2
- package/README.ru.md +87 -0
- package/README.zh.md +87 -0
- package/cordis.patch.yml +11 -0
- package/lib/client.js +2739 -0
- package/lib/index.d.ts +380 -0
- package/lib/index.js +5078 -0
- package/media/visual-verification.png +0 -0
- package/package.json +123 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
- **Parity release**: TUI settings section, Models-page key card, plan-aware Go picker, request-wide image budget and tool-schema normalization.
|
|
6
|
+
- Package identity is now scoped: `@goodandready/dsh-opencode-provider` (unscoped npm name belongs to an unrelated package).
|
|
7
|
+
- Custom composer cost pill and custom analytics cost columns removed; cost is shown only by DSH's native display.
|
|
8
|
+
- One-click plugin update in Settings → OpenCode: registry status, badge, exact `dsh plugin add`, restart notice, loopback/same-origin POST guard.
|
|
9
|
+
- English, Chinese and Russian READMEs and a visual-verification asset ship in the package and GitHub tree.
|
|
10
|
+
- **Usage analytics dashboard** in the quota panel: KPI grid, 7/30/90 window, and Models / Days / Sessions tables; success/errors show `—` because the local projection does not expose failed attempts. Cost is not duplicated: DSH's native display is the only cost surface.
|
|
11
|
+
- **Tool limit fix**: tools are deduplicated by name and capped at OpenCode's 128-item maximum before every transport, fixing `HTTP 400: maximum number of items is 128` in tool-heavy profiles.
|
|
12
|
+
- **Test connection** button in the key card: pick a model, run a minimal real request, see latency and the reply.
|
|
13
|
+
- `POST /dsh-opencode/test` Host route with a 60-second budget.
|
|
14
|
+
- Sidebar quota bars now change color at 75% (warning) and 90% (critical).
|
|
15
|
+
- Write routes require a literal loopback `Host` (not just a loopback socket) in addition to same-origin and JSON checks, closing DNS-rebinding key exfiltration.
|
|
16
|
+
- Analytics no longer fabricates `100%` success / `0` errors; the local projection returns `null` and the UI shows `—`.
|
|
17
|
+
- Go plan filtering now fails open for models that exist only in the live catalog; known Console models stay hidden.
|
|
18
|
+
- Tool truncation at the 128-item provider limit logs a one-time warning instead of failing silently.
|
|
19
|
+
- TUI settings refresh after `/config` account changes; analytics requests have an error state and a generation guard against the 7/30/90 race.
|
|
20
|
+
- Projection analytics reads run with bounded parallelism; `.commandcode/` is ignored; provider registration logs at boot.
|
|
21
|
+
- Tool-result images are replaced with an offload placeholder instead of failing the whole request in every transport.
|
|
22
|
+
- Core harness tools (`read`, `write`, `edit`, `bash`, `glob`, `grep`, …) survive the 128-tool cap even when a large MCP set is present.
|
|
23
|
+
- `GET /dsh-opencode/health` reports provider registration, account count and model count for startup diagnostics.
|
|
24
|
+
- The TUI settings section polls live config, so accounts/allowlist changes made from the terminal refresh without a restart.
|
|
25
|
+
- Client colors moved to DSH theme variables; the `zh` dictionary is a real translation; draft/route/widget helpers moved to `settings-draft.ts`, `settings-widgets.tsx`, `routes-public.ts` and `routes-values.ts`; quota triggers expose real `aria-expanded` panel state.
|
|
26
|
+
- Tests grew to 84, including TUI polling, health, core-tool preservation and tool-result image placeholders.
|
|
27
|
+
- Rotation now also triggers on quota/usage-limit bodies (400/402/403/429), with a 10-minute or `Retry-After` cooldown for quota rejections.
|
|
28
|
+
- The last account is marked on rejection as well; account rows expose `mark`/`coolingUntil`, show a cooldown badge, and the active marker follows the first non-cooling key.
|
|
29
|
+
- Per-account quota reads: `/state.accounts[].usage` is fetched for each distinct key (`UsageReader` caches per key), the active account drives the sidebar and quota panel, Accounts rows show 5h/week/month chips, and the panel has an accounts table. Verified live: `default` 1/5/2 and `Go #2` 9/4/100 with `rate-limited` monthly until 22 Oct 08:21.
|
|
30
|
+
- Request heatmap (Monday-first weekday × local hour) in the quota panel. The adapter appends `{at, provider, model, purpose?}` to `~/.dsh/storages/dsh-opencode/requests.jsonl` and `/analytics` merges the aggregate; prompts, responses and keys are never stored. Live: two `/test` calls produced `Tue 13:00 = 2`.
|
|
31
|
+
- Heatmap accuracy: `session-title` / `compaction` calls are excluded and exact duplicate records collapse. Live after two real chat messages: analytics shows 2 OpenCode sessions (`deepseek-v4.1-flash` ≈$0.00079, `beam-free` Free), while the heatmap counts 6 main requests instead of 8 raw records.
|
|
32
|
+
- A 24-hour aggregate strip under the heatmap sums the selected window by hour across all days; its bars use the same intensity scale and tooltips.
|
|
33
|
+
- Per-model heatmap: `/analytics` returns a `models[]` series (provider/model, total, hours, grid), and the panel offers an **All models / model** selector when more than one model has traffic. Live: `deepseek-v4.1-flash` 2, `minimax-m3.1-flash-free` 1.
|
|
34
|
+
- Account cooldowns persist across restarts in `~/.dsh/storages/dsh-opencode/cooldowns.json`, keyed by a SHA-256 fingerprint (raw keys are never written). Expired entries are dropped and the file is capped at 50. Live: after a restart, `Go #2` kept its `quota` mark until 22 Oct 08:21 while the restored default key was active.
|
|
35
|
+
- Model → account rules: first matching glob (case-insensitive `*`/`?`) pins a preferred account for that model, with the other accounts kept as fallbacks. Config is normalized and exposed through `/state`; Settings has an editor. Live: `*free* → Go #2` round-tripped through `/config` and `/state`, then cleared.
|
|
36
|
+
- Sidebar quota card redesigned to match the clinebot meter: theme-native ring, active account name, highest-window percent, and 5-hour / weekly / monthly rows with remaining quota, reset countdown and thin bars. Uses DSH CSS variables and has a narrow rail variant. The header percentage is right-aligned with the row values.
|
|
37
|
+
- Quota panel now has its own vertical scroll (`height: 100%`, viewport max-height, `overflow-y: auto`), so content below the fold is reachable; verified in Desktop by opening the panel and scrolling to **Usage by model**.
|
|
38
|
+
- Quota panel polish: the badge now says **Active account** (not "Pinned account" when `activeAccount` is empty), Accounts rows show persisted cooldown / provider rate-limit badges (for example `Quota cooldown · 2d`), and the 5-hour/weekly/monthly bars show date+time with a relative reset (`1% · 20.10, 17:11 (2h)`).
|
|
39
|
+
- `/opencode` gained `accounts`, `models`, `rules` and `test [model]`; `status` now includes per-account quotas and rules.
|
|
40
|
+
- Request log compaction at 2 MB (newest 5000 lines) and a `npm run check` / `npm run release` workflow plus GitHub Actions CI (`typecheck`, tests, build, pack).
|
|
41
|
+
- Live-verified two-account rotation in DSH Desktop: with an invalid default key, `/test` switched to `Go #2` and returned `OK`. A later run found `Go #2` at its monthly limit (98% before the tests) and showed `quota` with the provider's reset.
|
|
42
|
+
- `GET /dsh-opencode/analytics` and the **Usage by model** table in the quota panel (7/30/90 days). It aggregates only numeric metadata from the local session projection cache; a session is attributed to its last-used model.
|
|
43
|
+
- Verified live in DSH Desktop 0.2.1-rc.2; install there from the packed tarball (a directory path triggers the Desktop CLI's `dsh-desktop-runtime` alias bug).
|
|
44
|
+
|
|
45
|
+
## 0.4.1
|
|
46
|
+
|
|
47
|
+
- Accounts card can set each account's key from the page; unconfigured accounts are listed with a missing-key badge.
|
|
48
|
+
- Live catalog refresh button (`POST /dsh-opencode/models/refresh`).
|
|
49
|
+
- Quota warning banners at 80% and 95% in the settings page.
|
|
50
|
+
- Expandable subscription-limits table: per model, Go and Go Plus monthly allowance plus 5h/week/month request estimates.
|
|
51
|
+
|
|
52
|
+
## 0.4.0
|
|
53
|
+
|
|
54
|
+
- Per-model OpenCode Go subscription limits: monthly dollar allowance and estimated requests for the 5-hour, weekly and monthly windows, for Go and Go Plus.
|
|
55
|
+
- Limits appear in the model picker description and as a badge in the settings model list; hovering shows both plans.
|
|
56
|
+
- Added `src/subscription-limits.ts` with the official table and formatting helpers.
|
|
57
|
+
- Tests: limits table integrity, formatting, and route-gated model descriptions.
|
|
58
|
+
|
|
59
|
+
## 0.3.1
|
|
60
|
+
|
|
61
|
+
- Composer session-cost estimate (`≈`/`≥`/`Free`) from the generated price snapshot and the `tokenUsage`/`modelSelection` projections.
|
|
62
|
+
- Model price badges in the settings page; model picker descriptions now include per-1M input/output rates.
|
|
63
|
+
- OpenCode auth-entry autocomplete from the entries actually present in `auth.json`.
|
|
64
|
+
- Broadened live tool-loop coverage (Grok, Muse, Qwen).
|
|
65
|
+
|
|
66
|
+
## 0.3.0
|
|
67
|
+
|
|
68
|
+
- Browser settings page (`Settings → OpenCode`): key management, quota bars, accounts, model allowlist, per-model transport overrides, connection settings.
|
|
69
|
+
- Sidebar quota card (opt-in) and the center-column quota panel.
|
|
70
|
+
- Host HTTP routes under `/dsh-opencode/*` with same-origin enforcement.
|
|
71
|
+
- Key import from OpenCode's `auth.json` through the credentials seam.
|
|
72
|
+
- Client bundle built as a `window.__ModuleLoader__` module with React externalized.
|
|
73
|
+
|
|
74
|
+
## 0.2.0
|
|
75
|
+
|
|
76
|
+
- Native Anthropic Messages transport (thinking blocks, signatures, tool use).
|
|
77
|
+
- Native OpenAI Responses transport.
|
|
78
|
+
- Native Google Generative AI transport with `thoughtSignature` replay.
|
|
79
|
+
- Image input on every transport, through `ctx.attachments.readImageRequest`.
|
|
80
|
+
- Multi-account pool with pre-stream 401/403/429 rotation and per-key cooldowns.
|
|
81
|
+
- `/opencode` command with status and quota windows.
|
|
82
|
+
- Reasoning-effort mapping and output caps.
|
|
83
|
+
- Expanded unit tests and live smoke scripts.
|
|
84
|
+
|
|
85
|
+
## 0.1.0
|
|
86
|
+
|
|
87
|
+
- Initial P1 skeleton.
|
|
88
|
+
- Registers `opencode` and `opencode-go` routes on the dsh `llm` service.
|
|
89
|
+
- OpenAI Chat transport over the Go unified gateway.
|
|
90
|
+
- Stable `x-opencode-session` derived from the durable session id.
|
|
91
|
+
- Key resolution from config, the credential seam, OpenCode `auth.json`, and the launch environment.
|
|
92
|
+
- Live model catalog with a generated `models.dev` fallback.
|
|
93
|
+
- Text, reasoning, streamed tool calls, usage, and classified provider errors.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-opencode-provider contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
dsh-opencode-provider
|
|
2
|
+
=====================
|
|
3
|
+
|
|
4
|
+
This project is an unofficial community integration. It is not affiliated
|
|
5
|
+
with OpenCode, Anomaly, or DeepSeek Harness.
|
|
6
|
+
|
|
7
|
+
Architectural ideas and wire-contract research were informed by these MIT
|
|
8
|
+
projects:
|
|
9
|
+
|
|
10
|
+
- dsh-commandcode-provider — https://github.com/Mars-Sea/dsh-commandcode-provider
|
|
11
|
+
- dsh-clinebot — https://github.com/GooDAnDReaDY/dsh-clinebot
|
|
12
|
+
- opencode2dsh — https://github.com/FishBottle7/opencode2dsh (reference for the
|
|
13
|
+
anonymous free-lane header requirements; that bypass is not implemented here)
|
|
14
|
+
|
|
15
|
+
Model metadata is generated from https://models.dev.
|
package/README.md
CHANGED
|
@@ -1,3 +1,269 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @goodandready/dsh-opencode-provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OpenCode / OpenCode Go provider plugin for [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/) (dsh).
|
|
4
|
+
|
|
5
|
+
It registers two native LLM routes:
|
|
6
|
+
|
|
7
|
+
| Route | Product | Base |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `opencode` | OpenCode Console / Zen (pay-as-you-go) | `https://opencode.ai/inference` |
|
|
10
|
+
| `opencode-go` | OpenCode Go / Go Plus subscription | `https://opencode.ai/inference/go` |
|
|
11
|
+
|
|
12
|
+
Requests resolve the key per call, so a changed credential reaches the next request without a restart.
|
|
13
|
+
|
|
14
|
+
## What works
|
|
15
|
+
|
|
16
|
+
- **Four wire transports**, chosen per model:
|
|
17
|
+
- OpenAI Chat Completions (DeepSeek, GLM, Kimi, MiMo, LongCat, Hy3, Step, …).
|
|
18
|
+
- Anthropic Messages (Claude Haiku, MiniMax, Qwen).
|
|
19
|
+
- OpenAI Responses (GPT, Grok, Muse).
|
|
20
|
+
- Google Generative AI (Gemini).
|
|
21
|
+
- **Text, reasoning, streamed tool calls, usage, finish reasons.**
|
|
22
|
+
- **Images** through the durable attachment service, rendered natively per transport.
|
|
23
|
+
- **Replay of provider metadata** where the endpoint requires it:
|
|
24
|
+
- Anthropic thinking signatures.
|
|
25
|
+
- Gemini `thoughtSignature` on historical function calls.
|
|
26
|
+
- **Multi-account rotation** before a stream starts: 401/403/429 and quota/usage-limit bodies (400/402/403/429). An invalid key cools down for 24h, a quota rejection for 10 minutes or `Retry-After`, a rate limit for 60s or `Retry-After`. A request already streaming is never replayed on another account; the next request uses the next usable key.
|
|
27
|
+
- **Per-account quotas**: each account row shows its own 5-hour / weekly / monthly usage, the active account drives the sidebar and quota panel, and the panel lists every account side by side.
|
|
28
|
+
- **Model → account rules**: first matching glob pins a preferred account for that model; the other accounts remain fallbacks if it fails or is cooling.
|
|
29
|
+
- **Request heatmap** in the quota panel: a Monday-first weekday × hour grid in local time, plus a 24-hour aggregate strip underneath. A selector switches between all requests and any single model. The adapter appends `{at, provider, model, purpose?}` to `~/.dsh/storages/dsh-opencode/requests.jsonl`; no prompt, response or key is stored. Auxiliary `session-title`/`compaction` calls are excluded from the heatmap and exact duplicate records collapse. The log compacts at 2 MB to the newest 5000 lines. Collection starts with this build — the projection cache only has `lastPromptAt`, not per-request timestamps.
|
|
30
|
+
- **Stable `x-opencode-session`** derived from the durable session id, so retries and every turn of a conversation reuse the same routing key.
|
|
31
|
+
- **Live catalog** from `/v1/models` with a generated `models.dev` fallback.
|
|
32
|
+
- **Per-model subscription limits** from the OpenCode Go pricing table: monthly dollar allowance plus estimated requests for the 5-hour, weekly and monthly windows, for both Go and Go Plus. Shown in the model picker and the settings page; estimates only — the gateway remains the authority.
|
|
33
|
+
- **`/opencode` command** for status, usage, accounts, models, rules, and `test [model]`.
|
|
34
|
+
- **TUI settings section** for the terminal profile (`/settings → OpenCode`) with the key, endpoints, allowlist, active account and timeouts.
|
|
35
|
+
- **Models-page key card** in Settings → Models.
|
|
36
|
+
- **One-click plugin update** in Settings → OpenCode: current/latest version, an update badge, and an **Update now** button that installs the exact npm version through the DSH CLI and asks for a restart. The host endpoint is `GET/POST /dsh-opencode/update`.
|
|
37
|
+
- **Plan-aware Go picker**: hides models outside the Go table unless turned off; descriptions carry `Go`, `Console` or `FREE`.
|
|
38
|
+
- **Request-wide image budget** (newest images win, oldest become an omitted placeholder) and **object-root tool-schema normalization** for MCP/third-party tools.
|
|
39
|
+
- **Usage analytics dashboard** in the quota panel: KPI cards (requests, tokens, success, input/output, reasoning, cache, TTFT, latency, errors), a 7/30/90 selector, and Models / Days / Sessions tables. Success and error cards show `—`: the local projection does not expose failed attempts or reasoning tokens.
|
|
40
|
+
- **Usage by model** from the local session projection cache: sessions and tokens per OpenCode model. Only numeric metadata is read; titles and message content stay local.
|
|
41
|
+
- **Browser UI**:
|
|
42
|
+
- **Settings → OpenCode**: key status, paste/import/clear, quota bars, accounts, model allowlist, per-model transport overrides, connection settings.
|
|
43
|
+
- **Sidebar quota card** (off by default): a ring with the active account and the highest window, then 5-hour / weekly / monthly rows with remaining quota, reset countdown and bars; clicking opens the full quota panel in the center column. The panel scrolls internally, so the heatmap and usage table below the fold stay reachable.
|
|
44
|
+
- **Cost** is shown only by DSH's native cost display; the plugin does not add a second composer pill.
|
|
45
|
+
- Model price badges in the settings page; the OpenCode auth-entry field autocompletes from `auth.json`.
|
|
46
|
+
- Host routes under `/dsh-opencode/*` serve state and accept writes only from a loopback Host with same-origin and JSON checks; `GET /dsh-opencode/health` reports provider registration, accounts and model count.
|
|
47
|
+
- The provider keeps core harness tools (`read`, `write`, `edit`, `bash`, `glob`, `grep`, …) when a large MCP set is trimmed to OpenCode's 128-tool limit.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
Build and pack first:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install
|
|
55
|
+
npm run build
|
|
56
|
+
npm pack
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Install the **packed tarball** into a profile:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.0.tgz
|
|
63
|
+
# DSH Desktop:
|
|
64
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.0.tgz
|
|
65
|
+
|
|
66
|
+
# Or install the published package:
|
|
67
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.0
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Restart dsh (or DSH Desktop), then pick `OpenCode` or `OpenCode Go` in the model picker.
|
|
71
|
+
|
|
72
|
+
> **Use the tarball, not the directory.** On dsh 0.2.1-rc.2 the Desktop CLI aliases a local
|
|
73
|
+
> directory passed to `dsh plugin add` as `@deepseek-ai/dsh-desktop-runtime` and overwrites
|
|
74
|
+
> the target's `package.json`. Installing the packed `.tgz` avoids that path and installs
|
|
75
|
+
> the package under its real name.
|
|
76
|
+
|
|
77
|
+
## Verified in DSH Desktop
|
|
78
|
+
|
|
79
|
+
Tested against DSH Desktop **0.2.1-rc.2**:
|
|
80
|
+
|
|
81
|
+
- the plugin loads and `/dsh-opencode/state` returns `200`;
|
|
82
|
+
- the key is picked up from `~/.local/share/opencode/auth.json`;
|
|
83
|
+
- the live catalog returned 37 models with prices and Go limits;
|
|
84
|
+
- quota windows returned `rolling / weekly / monthly`;
|
|
85
|
+
- `/dsh-opencode/analytics` scanned 7 local sessions (0 OpenCode sessions yet, as expected before the first OpenCode chat);
|
|
86
|
+
- `/dsh-opencode/test` against `deepseek-v4.1-flash` returned `{"ok":true,"text":"OK"}`;
|
|
87
|
+
- a second Go account was added, the default key was temporarily replaced with an invalid one, and `/test` rotated to the second account and still returned `OK`;
|
|
88
|
+
- the second account was already at ~98% of its monthly allowance; the rotation test pushed it over, so a later run surfaced `QUOTA`, marked the default as `invalid-credential` and `Go #2` as `quota` until its reset (22 Oct 08:21), with no active account;
|
|
89
|
+
- per-account usage returned `default` 1% / 5% / 2% and `Go #2` 9% / 4% / **100%** monthly (`rate-limited`, reset 22 Oct 08:21), so the exhausted account is visible without a failed request;
|
|
90
|
+
- two `/test` requests produced `Tue 13:00 = 2` in the heatmap and two lines in `requests.jsonl`; the 24-hour strip shows the same value at 13:00;
|
|
91
|
+
- a third request with `minimax-m3.1-flash-free` added a second per-model series (`deepseek-v4.1-flash` 2, `minimax-m3.1-flash-free` 1), so the model selector is populated;
|
|
92
|
+
- after restarting Desktop, `Go #2` still showed its persisted `quota` cooldown (until 22 Oct 08:21) while the restored default key was active;
|
|
93
|
+
- setting `*free* → Go #2` through `/config` returned the normalized rule from `/state`, and clearing it returned `[]`; the account list stayed unchanged;
|
|
94
|
+
- the redesigned sidebar card was checked in Desktop: `OpenCode Go` with a 5% ring, `5-Hour Window 99% remaining · 3h`, `Weekly Window 95% remaining · 6d`, `Monthly Window 98% remaining · 29d`; it uses the DSH theme variables and matches the clinebot meter's row/ring layout;
|
|
95
|
+
- a real chat produced two OpenCode sessions: `deepseek-v4.1-flash` (4105 in / 243 out, ≈$0.00079) and `beam-free` (10670 in / 171 out, Free); the heatmap counted 6 main requests (the two title calls and exact duplicates are filtered);
|
|
96
|
+
- the renderer loaded the client bundle (`settings.opencode` is present in its code cache).
|
|
97
|
+
|
|
98
|
+
Open **Settings → OpenCode** and enable *Show the quota card in the sidebar* if you want the
|
|
99
|
+
sidebar meter.
|
|
100
|
+
|
|
101
|
+
## Configure
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
- id: llm-opencode
|
|
105
|
+
name: "@goodandready/dsh-opencode-provider"
|
|
106
|
+
config:
|
|
107
|
+
apiKeyEnv: OPENCODE_API_KEY # default
|
|
108
|
+
# apiKey: oc_sk_... # composition only, never commit
|
|
109
|
+
openCodeAuthEntry: opencode-go
|
|
110
|
+
requestTimeoutMs: 300000
|
|
111
|
+
streamIdleTimeoutMs: 300000
|
|
112
|
+
visibleModels: []
|
|
113
|
+
modelTransports: {}
|
|
114
|
+
# contextWindowOverrides: { deepseek-v4-pro: 65536 }
|
|
115
|
+
# maxOutputTokens: 32768
|
|
116
|
+
accounts:
|
|
117
|
+
- label: Go #2
|
|
118
|
+
apiKeyEnv: OPENCODE_API_KEY_2
|
|
119
|
+
activeAccount: '' # label or credential name to pin
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Every field except `apiKey` is volatile and appears in dsh settings.
|
|
123
|
+
|
|
124
|
+
### Keys
|
|
125
|
+
|
|
126
|
+
1. `config.apiKey` (composition only, `role('secret')`).
|
|
127
|
+
2. The dsh credential seam under `apiKeyEnv`.
|
|
128
|
+
3. `~/.local/share/opencode/auth.json` → entry `openCodeAuthEntry` (default `opencode-go`), written by OpenCode's own `/connect`.
|
|
129
|
+
4. The launch environment (process, project `.env`, `$DSH_HOME/.env`).
|
|
130
|
+
|
|
131
|
+
Extra `accounts` resolve the same way (literal key or credential reference). If you already use OpenCode, the plugin picks up the stored Go key automatically.
|
|
132
|
+
|
|
133
|
+
### Accounts and rotation
|
|
134
|
+
|
|
135
|
+
Add a second account in **Settings → OpenCode → Accounts**: give it a name and a credential reference (for example `OPENCODE_API_KEY_2`), paste its key, then Save. The default account (the key card at the top) always serves first unless `activeAccount` pins another one.
|
|
136
|
+
|
|
137
|
+
Rotation happens **before any content is streamed**. When the serving key is rejected with an invalid credential, a rate limit, or an exhausted quota, the adapter marks that key and retries the same request with the next usable account. An exhausted pinned account still falls back to the others; it returns to service once its cooldown passes. A request that has already started streaming is not replayed on another account.
|
|
138
|
+
|
|
139
|
+
Each account row shows the current cooldown (`Invalid key`, `Quota cooldown · 23h`, `Rate limited · 30s`) and its own quota chips. The active marker moves to the first account that is not cooling down. The last account is marked too, so a failed turn explains which key ended it. Quota cooldowns honor the provider's reset (`Retry-After`) when present. The quota panel repeats the numbers in an accounts table, and the sidebar meter uses the active account.
|
|
140
|
+
|
|
141
|
+
Cooldowns survive restarts: deadlines and reasons are stored in `~/.dsh/storages/dsh-opencode/cooldowns.json` keyed by a SHA-256 fingerprint — raw keys are never written. Expired entries are dropped on load, and the file keeps at most 50 entries.
|
|
142
|
+
|
|
143
|
+
### Model → account rules
|
|
144
|
+
|
|
145
|
+
Pin a model to a preferred account with a glob. The first matching rule wins; `*` and `?` are supported and matching is case-insensitive. The named account is tried first, and the other accounts remain fallbacks if it is cooling, missing, or rejected — a rule is a preference, not a hard filter. An unknown account label is ignored, and rules apply to both `opencode` and `opencode-go`.
|
|
146
|
+
|
|
147
|
+
Configure them in **Settings → OpenCode → Model → account rules**, or in YAML:
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
modelAccountRules:
|
|
151
|
+
- pattern: "*free*"
|
|
152
|
+
account: "free"
|
|
153
|
+
- pattern: "deepseek-*"
|
|
154
|
+
account: "go"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The account label is the same one shown in **Accounts** (`default` or the extra account's name).
|
|
158
|
+
|
|
159
|
+
### Request heatmap
|
|
160
|
+
|
|
161
|
+
The quota panel has a **Request heatmap**: rows are Monday–Sunday, columns are local-time hours, and each cell counts calls made through this adapter (including tool-loop steps and `Test connection`). Auxiliary `session-title` and `compaction` calls are excluded, and exact duplicate records collapse; tool-loop steps are separate adapter requests, so one chat message is not necessarily one cell. Hover a cell to see the exact count. A 24-hour strip below the grid aggregates the same window by hour across all days, so the daily shape stays readable even when the grid is sparse. When more than one model has traffic, a selector switches the grid and strip to a single model; each option shows its total. The data is append-only metadata in `~/.dsh/storages/dsh-opencode/requests.jsonl`; it never includes prompts, responses or keys. Because the local projection cache stores only the last prompt time per session, the heatmap starts collecting with this build and grows as you use the plugin.
|
|
162
|
+
|
|
163
|
+
## Settings page
|
|
164
|
+
|
|
165
|
+
Open **Settings → OpenCode**:
|
|
166
|
+
|
|
167
|
+
- **Key** — status, paste a new key, import the key OpenCode already stored, clear it, or run a **Test connection** against a chosen model and see the latency and reply.
|
|
168
|
+
- **Quota** — 5-hour, weekly and monthly bars with reset dates, plus warning banners at 80% and 95%.
|
|
169
|
+
- **Accounts** — add extra accounts by credential reference, set each account's key from the page, see whether it is configured, pin one, or return to automatic rotation.
|
|
170
|
+
- **Models** — search, refresh the live catalog, uncheck models to build an allowlist, override the wire transport per model, and see each model's input/output price plus its Go request estimate (`Go 34.7K/5h`). Hover the estimate for the full Go / Go Plus monthly allowance and 5h/week/month request counts; expand **Subscription limits** for a table of every model with both plans.
|
|
171
|
+
- **Connection** — API bases, usage endpoint, timeouts, output cap, context override, sidebar-card switch. The OpenCode auth-entry field autocompletes from the keys present in `auth.json`.
|
|
172
|
+
- **Plugin update** — current/latest version, update badge, and **Update now**. It installs the exact registry version through the normal `dsh plugin add` path, blocks a second click while installing, and reports when DSH must be restarted.
|
|
173
|
+
|
|
174
|
+
The sidebar quota card is **off by default**. Enable *Show the quota card in the sidebar* in the settings page; clicking the card opens the full quota panel, which also carries the **Usage by model** table (7/30/90-day window).
|
|
175
|
+
|
|
176
|
+
The Host serves `/dsh-opencode/state`, `/health`, `/analytics`, `/config`, `/key`, `/key/clear`, `/auth-import`, `/refresh`, `/models/refresh`, `/test` and `/update`. Writes require JSON, are refused for cross-origin requests, and are restricted to loopback (`127.0.0.1` / `::1`); reads stay same-origin. This keeps key and configuration writes off a LAN-exposed web server until the host's own auth token can be integrated.
|
|
177
|
+
|
|
178
|
+
## Updates
|
|
179
|
+
|
|
180
|
+
The **Plugin update** card in Settings → OpenCode calls `GET /dsh-opencode/update`, which compares the installed `package.json` with the npm registry. When a newer version exists, **Update now** sends the same-origin request with `x-dsh-plugin-update: 1`; the Host runs the normal DSH CLI:
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
dsh plugin --profile <profile> add --config.minimumReleaseAge=0 @goodandready/dsh-opencode-provider@<exact-version>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The update is blocked while another update is running, never uses `--force`, and never installs from a worktree or a `file:` path. The card reports that DSH must be restarted after a successful install. If the runtime does not expose the CLI entry, the card says that automatic update is unavailable and points to the DSH plugin manager; the status check still works.
|
|
187
|
+
|
|
188
|
+
## Visual verification
|
|
189
|
+
|
|
190
|
+

|
|
191
|
+
|
|
192
|
+
Captured from the exact 0.5.0 release candidate in DSH Desktop in the dark theme.
|
|
193
|
+
|
|
194
|
+
## Command
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
/opencode status # accounts, per-account quotas, rules
|
|
198
|
+
/opencode usage # quota windows only
|
|
199
|
+
/opencode accounts # labels, source, active/cooling, 5h/wk/mo
|
|
200
|
+
/opencode models # ids, FREE/Go limits
|
|
201
|
+
/opencode rules # model → account rules
|
|
202
|
+
/opencode test [model] # minimal real request (default deepseek-v4.1-flash)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Example:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
OpenCode provider
|
|
209
|
+
- opencode-go: 1 account(s) · auth.json:opencode-go
|
|
210
|
+
- opencode: 1 account(s) · auth.json:opencode-go
|
|
211
|
+
5h : [░░░░░░░░░░] 1% · resets 2026-10-19 22:05Z
|
|
212
|
+
weekly : [░░░░░░░░░░] 1% · resets 2026-10-26 00:00Z
|
|
213
|
+
monthly: [░░░░░░░░░░] 0% · resets 2026-11-18 13:38Z
|
|
214
|
+
Auth: opencode-go · OPENCODE_API_KEY
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Verified API facts
|
|
218
|
+
|
|
219
|
+
- `GET /inference/go/v1/models` is public and returns the live Go roster.
|
|
220
|
+
- `GET /zen/go/v1/usage` returns `rolling` (5h), `weekly`, `monthly` with `percent` and `resetsAt`. It is undocumented; the plugin treats it as fail-open.
|
|
221
|
+
- The unified `POST /zen/go/v1/chat/completions` accepts OpenAI-compatible **and** Anthropic-backed models, but **not** Responses-only or Google models.
|
|
222
|
+
- Responses-only models (GPT, Grok, Muse) and Gemini need their native endpoints, which the plugin uses.
|
|
223
|
+
- Gemini 4 requires replaying `thoughtSignature` on historical function calls; the Google transport stores it in dsh's replay state.
|
|
224
|
+
- `x-opencode-session` is mandatory. A stable opaque value is enough for a Go key; the canonical `ses_*` shape and CLI impersonation are only needed for the anonymous free lane, which this plugin deliberately does not implement.
|
|
225
|
+
- Free-tier anonymous keys are rejected with `403 FreeTierError`; free models work normally with an active Go subscription.
|
|
226
|
+
|
|
227
|
+
## Development
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
npm run typecheck
|
|
231
|
+
npm run build
|
|
232
|
+
npm test
|
|
233
|
+
npm run check # typecheck + tests + build
|
|
234
|
+
npm run release # check + packed tarball
|
|
235
|
+
npm run sync:models # regenerate src/models.generated.ts from models.dev
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
GitHub Actions runs `npm run check` and `npm pack` on push/PR (`.github/workflows/ci.yml`).
|
|
239
|
+
|
|
240
|
+
Live smoke scripts (read your Go key from `auth.json`; no key is stored):
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
node scripts/smoke.mjs # OpenAI Chat text/reasoning/usage
|
|
244
|
+
node scripts/smoke-tools.mjs # OpenAI Chat tool call
|
|
245
|
+
node scripts/smoke-transports.mjs # all four transports, text
|
|
246
|
+
node scripts/smoke-transport-tools.mjs # all four transports, tool call
|
|
247
|
+
node scripts/smoke-tool-loop.mjs # full tool loop, including Gemini replay
|
|
248
|
+
node scripts/smoke-images.mjs # vision on all four transports
|
|
249
|
+
node scripts/smoke-rotation.mjs # invalid key → valid key rotation
|
|
250
|
+
node scripts/smoke-command.mjs # /opencode output
|
|
251
|
+
node scripts/smoke-client.mjs # client bundle loader + slot registration
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Limitations
|
|
255
|
+
|
|
256
|
+
- Verbatim file attachments are not forwarded yet; images are.
|
|
257
|
+
- The usage endpoint is unofficial and may change; chat never depends on it.
|
|
258
|
+
- Cost is displayed only by DSH's native cost display; this plugin has no second composer cost pill.
|
|
259
|
+
- Automatic update requires the DSH CLI entry in the runtime. When it is not exposed, the update card shows the current/latest status and directs the user to the DSH plugin manager.
|
|
260
|
+
- Subscription request counts are OpenCode's published estimates, not enforced quotas. They can change when OpenCode updates the Go pricing table; the gateway is the final authority.
|
|
261
|
+
- Usage-by-model aggregates the local projection cache and attributes a whole session to its last-used model; a session that switched models is therefore approximate. It never reads prompt text or titles.
|
|
262
|
+
- `opencode` (Console) billing shows no per-model allowance; the Go route is the primary target.
|
|
263
|
+
- The anonymous free lane (`Bearer public` + CLI impersonation) is not implemented.
|
|
264
|
+
- The UI requires a web profile (the Host needs `ctx.webServer`); the TUI keeps the `/opencode` command and the model routes.
|
|
265
|
+
- Write routes are loopback-only; a web profile exposed to a LAN can read state but cannot save configuration or keys until the host auth token is integrated.
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
MIT. See [LICENSE](./LICENSE) and [NOTICE](./NOTICE).
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @goodandready/dsh-opencode-provider
|
|
2
|
+
|
|
3
|
+
Плагин-провайдер OpenCode / OpenCode Go для [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/) (dsh).
|
|
4
|
+
|
|
5
|
+
Плагин регистрирует два нативных LLM-маршрута:
|
|
6
|
+
|
|
7
|
+
| Маршрут | Продукт | База |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `opencode` | OpenCode Console / Zen (pay-as-you-go) | `https://opencode.ai/inference` |
|
|
10
|
+
| `opencode-go` | OpenCode Go / Go Plus | `https://opencode.ai/inference/go` |
|
|
11
|
+
|
|
12
|
+
Ключ разрешается на каждый запрос, поэтому смена credential применяется без перезапуска.
|
|
13
|
+
|
|
14
|
+
## Возможности
|
|
15
|
+
|
|
16
|
+
- Четыре транспорта: OpenAI Chat, OpenAI Responses, Anthropic, Google; выбор по модели.
|
|
17
|
+
- Живой каталог `/v1/models` с резервным generated-каталогом models.dev.
|
|
18
|
+
- Оценки лимитов Go и Go Plus для 5 часов / недели / месяца.
|
|
19
|
+
- Plan-aware picker (Go / Console / FREE).
|
|
20
|
+
- Пул аккаунтов: ротация на 401/403/429 и исчерпанной квоте; cooldown хранится по fingerprint ключа.
|
|
21
|
+
- Health аккаунтов, квоты по каждому аккаунту, правила «модель → аккаунт».
|
|
22
|
+
- Аналитика использования, heatmap запросов, таблицы Models / Days / Sessions.
|
|
23
|
+
- Core-инструменты сохраняются при лимите 128; изображения в tool-result заменяются placeholder.
|
|
24
|
+
- One-click обновление плагина в Settings → OpenCode.
|
|
25
|
+
|
|
26
|
+
## Установка
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.0
|
|
30
|
+
# DSH Desktop:
|
|
31
|
+
dsh plugin --profile desktop add @goodandready/dsh-opencode-provider@0.5.0
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Либо из упакованного файла:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.0.tgz
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
После установки перезапустите dsh / DSH Desktop и выберите **OpenCode** или **OpenCode Go** в picker.
|
|
41
|
+
|
|
42
|
+
## Настройка
|
|
43
|
+
|
|
44
|
+
Ключ берётся из DSH credentials, `OPENCODE_API_KEY` или `~/.local/share/opencode/auth.json`. В Settings → OpenCode можно вставить, импортировать или очистить ключ и выполнить **Test connection**.
|
|
45
|
+
|
|
46
|
+
Аккаунт `default` используется первым; дополнительные аккаунты служат fallback. `modelAccountRules` использует регистронезависимые `*` / `?` glob: первое совпадение задаёт предпочтительный аккаунт, остальные остаются fallback.
|
|
47
|
+
|
|
48
|
+
Go-маршрут показывает оценки лимитов на 5 часов, неделю и месяц, включая месячный долларовый allowance. Шлюз остаётся источником истины; значения в плагине — оценки.
|
|
49
|
+
|
|
50
|
+
## Квоты и аналитика
|
|
51
|
+
|
|
52
|
+
Карточка квоты в sidebar по умолчанию выключена; включите её в Settings → OpenCode. По клику открывается панель с аккаунтами, остатками 5h/неделя/месяц, временем сброса, аналитикой и heatmap запросов.
|
|
53
|
+
|
|
54
|
+
Heatmap агрегирует реальные вызовы адаптера по локальному времени, исключает `session-title` и `compaction`, а точные дубликаты схлопывает. Данные пишутся только в `~/.dsh/storages/dsh-opencode/requests.jsonl`; промптов, ответов и ключей там нет. Стоимость показывает только встроенный DSH; второй composer-пилюли плагин не добавляет.
|
|
55
|
+
|
|
56
|
+
## Обновление плагина
|
|
57
|
+
|
|
58
|
+
Карточка **Plugin update** в Settings → OpenCode:
|
|
59
|
+
|
|
60
|
+
1. `GET /dsh-opencode/update` сравнивает установленную версию с npm registry.
|
|
61
|
+
2. При наличии новой версии показывает badge и **Update now**.
|
|
62
|
+
3. Кнопка ставит точную версию через обычный `dsh plugin add`; параллельная установка блокируется, `--force` не используется, установка из worktree или `file:` запрещена.
|
|
63
|
+
4. После успеха карточка просит перезапустить DSH.
|
|
64
|
+
|
|
65
|
+
Если runtime не предоставляет DSH CLI, карточка сообщает, что автообновление недоступно, и направляет в plugin manager; проверка версии продолжает работать.
|
|
66
|
+
|
|
67
|
+
## Безопасность
|
|
68
|
+
|
|
69
|
+
- Write-маршруты требуют JSON, same-origin и literal loopback Host (`127.0.0.1` / `::1`) — это блокирует DNS rebinding.
|
|
70
|
+
- POST обновления дополнительно требует loopback, same-origin и `x-dsh-plugin-update: 1`.
|
|
71
|
+
- Ключ не возвращается из `/state` и не пишется в логи.
|
|
72
|
+
- Тело записи ограничено 256 KiB; request log хранит только `{at, provider, model, purpose}`.
|
|
73
|
+
|
|
74
|
+
## Совместимость
|
|
75
|
+
|
|
76
|
+
Проверено на DSH **0.2.1-rc.2**. UI требует web profile (Host нужен `ctx.webServer`); в TUI остаются команда `/opencode` и модельные маршруты.
|
|
77
|
+
|
|
78
|
+
## Ограничения
|
|
79
|
+
|
|
80
|
+
- Файловые вложения пока не передаются; изображения поддерживаются.
|
|
81
|
+
- Официальный usage endpoint неофициальный и может измениться; чат от него не зависит.
|
|
82
|
+
- Автообновление требует DSH CLI в runtime; иначе используйте DSH plugin manager.
|
|
83
|
+
- Анонимный free lane (`Bearer public` + CLI impersonation) не реализован.
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT. См. `LICENSE` и `NOTICE`.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @goodandready/dsh-opencode-provider
|
|
2
|
+
|
|
3
|
+
面向 [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/)(dsh)的 OpenCode / OpenCode Go 提供商插件。
|
|
4
|
+
|
|
5
|
+
插件注册两个原生 LLM 路由:
|
|
6
|
+
|
|
7
|
+
| 路由 | 产品 | 基础地址 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `opencode` | OpenCode Console / Zen(按量付费) | `https://opencode.ai/inference` |
|
|
10
|
+
| `opencode-go` | OpenCode Go / Go Plus 订阅 | `https://opencode.ai/inference/go` |
|
|
11
|
+
|
|
12
|
+
密钥在每次请求时解析,因此更换凭据后无需重启即可生效。
|
|
13
|
+
|
|
14
|
+
## 功能
|
|
15
|
+
|
|
16
|
+
- 支持 OpenAI Chat、OpenAI Responses、Anthropic 和 Google 四种传输,可按模型选择。
|
|
17
|
+
- 从 `/v1/models` 读取实时目录,并有 models.dev 生成目录作为回退。
|
|
18
|
+
- Go 与 Go Plus 的 5 小时 / 每周 / 每月订阅额度估算。
|
|
19
|
+
- 计划感知的模型选择器(Go / Console / FREE)。
|
|
20
|
+
- 多账户轮换:401/403/429 或配额耗尽时切换到下一个账户;冷却按密钥指纹持久化。
|
|
21
|
+
- 账户健康、每账户配额、模型 → 账户规则。
|
|
22
|
+
- 用量分析面板、请求热力图、Models / Days / Sessions 表。
|
|
23
|
+
- 核心工具在 128 个工具上限内优先保留;工具结果图片转为占位符。
|
|
24
|
+
- Settings → OpenCode 中的一键插件更新。
|
|
25
|
+
|
|
26
|
+
## 安装
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.0
|
|
30
|
+
# DSH Desktop:
|
|
31
|
+
dsh plugin --profile desktop add @goodandready/dsh-opencode-provider@0.5.0
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
也可以安装打包文件:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.0.tgz
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
安装后重启 dsh / DSH Desktop,在模型选择器中选择 **OpenCode** 或 **OpenCode Go**。
|
|
41
|
+
|
|
42
|
+
## 配置
|
|
43
|
+
|
|
44
|
+
密钥来自 DSH 凭据服务、`OPENCODE_API_KEY` 或 `~/.local/share/opencode/auth.json`。Settings → OpenCode 可以粘贴、导入、清除密钥,并运行 **Test connection**。
|
|
45
|
+
|
|
46
|
+
默认账户优先使用;额外账户按顺序作为回退。账户可按 credential reference 配置。`modelAccountRules` 使用大小写不敏感的 `*` / `?` glob,把匹配模型优先路由到指定账户,其余账户仍作为回退。
|
|
47
|
+
|
|
48
|
+
Go 路由使用配额表:5 小时、每周、每月窗口,以及每月美元额度。网关仍是最终权威;插件中的请求数是估算值。
|
|
49
|
+
|
|
50
|
+
## 配额和分析
|
|
51
|
+
|
|
52
|
+
侧边栏配额卡默认关闭,可在 Settings → OpenCode 打开。点击卡片打开完整面板,显示账户、5h/周/月剩余额度、重置时间、用量分析和请求热力图。
|
|
53
|
+
|
|
54
|
+
热力图按本地时间聚合真实适配器请求,排除 `session-title` 和 `compaction`,精确重复记录会折叠。数据只写到 `~/.dsh/storages/dsh-opencode/requests.jsonl`,不包含提示词、响应或密钥。成本由 DSH 原生成本显示提供,本插件不添加第二个 composer 成本胶囊。
|
|
55
|
+
|
|
56
|
+
## 插件更新
|
|
57
|
+
|
|
58
|
+
Settings → OpenCode 的 **Plugin update** 卡片:
|
|
59
|
+
|
|
60
|
+
1. `GET /dsh-opencode/update` 比较已安装版本与 npm registry。
|
|
61
|
+
2. 有更新时显示徽章和 **Update now**。
|
|
62
|
+
3. 点击后通过正常的 `dsh plugin add` 安装精确版本;并发安装会被拒绝,不使用 `--force`,不会从 worktree 或 `file:` 安装。
|
|
63
|
+
4. 成功后提示重启 DSH。
|
|
64
|
+
|
|
65
|
+
如果运行时没有暴露 DSH CLI,卡片会说明自动更新不可用,并指向 DSH 插件管理器;状态检查仍然可用。
|
|
66
|
+
|
|
67
|
+
## 安全
|
|
68
|
+
|
|
69
|
+
- 写入路由要求 JSON、同源和 literal loopback Host(`127.0.0.1` / `::1`),阻止 DNS rebinding。
|
|
70
|
+
- 更新 POST 额外要求 loopback、同源和 `x-dsh-plugin-update: 1`。
|
|
71
|
+
- 密钥不会从 `/state` 返回,也不会写入日志。
|
|
72
|
+
- 写入请求体上限 256 KiB;请求日志只保存 `{at, provider, model, purpose}`。
|
|
73
|
+
|
|
74
|
+
## 兼容性
|
|
75
|
+
|
|
76
|
+
已针对 DSH **0.2.1-rc.2** 验证。UI 需要 web profile(Host 需要 `ctx.webServer`);TUI 保留 `/opencode` 命令和模型路由。
|
|
77
|
+
|
|
78
|
+
## 限制
|
|
79
|
+
|
|
80
|
+
- 文件附件暂不转发;图片支持。
|
|
81
|
+
- 官方用量端点是非官方的,可能变化;聊天不依赖它。
|
|
82
|
+
- 自动更新需要运行时提供 DSH CLI;否则使用 DSH 插件管理器。
|
|
83
|
+
- 匿名免费通道(`Bearer public` + CLI 伪装)未实现。
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT。见 `LICENSE` 和 `NOTICE`。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# dsh-opencode-provider bundle layer.
|
|
2
|
+
# Registers the `opencode` (Console/Zen) and `opencode-go` (Go subscription)
|
|
3
|
+
# provider routes. All fields are optional — schema defaults apply.
|
|
4
|
+
#
|
|
5
|
+
# `name` must be the FULL package specifier as installed in the profile's
|
|
6
|
+
# node_modules. Quote it: an unquoted YAML scalar starting with `@` fails.
|
|
7
|
+
- insert:
|
|
8
|
+
- id: llm-opencode
|
|
9
|
+
name: "@goodandready/dsh-opencode-provider"
|
|
10
|
+
config:
|
|
11
|
+
apiKeyEnv: OPENCODE_API_KEY
|