@goodandready/dsh-opencode-provider 0.5.32 → 0.5.34
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/AUDIT.md +51 -0
- package/CHANGELOG.md +13 -0
- package/README.md +13 -4
- package/README.ru.md +9 -4
- package/README.zh.md +9 -4
- package/lib/client.js +77 -14
- package/lib/index.d.ts +4 -0
- package/lib/index.js +27 -5
- package/package.json +5 -3
package/AUDIT.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Project and security audit
|
|
2
|
+
|
|
3
|
+
Read-only review of `main` on 2026-11-05. Findings are recorded separately from
|
|
4
|
+
implementation; no production runtime or other plugin was modified during the
|
|
5
|
+
review.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
- Architecture: Host routes, adapter/transports, credentials, proxy, catalog,
|
|
10
|
+
analytics, settings/client bundle.
|
|
11
|
+
- Correctness: routing, account failover, streaming, tool-call normalization,
|
|
12
|
+
pricing/analytics, image policy.
|
|
13
|
+
- Security: HTTP boundary, secret handling, updater, dependency graph.
|
|
14
|
+
- Tests and prior issues: 149 tests, tracked parity/security backlog.
|
|
15
|
+
|
|
16
|
+
## Verified findings
|
|
17
|
+
|
|
18
|
+
| # | Severity | Finding | Status |
|
|
19
|
+
|---|---|---|---|
|
|
20
|
+
| A1 | High (dev-only) | `zod` 4.4.3 is pinned by every `@deepseek-ai/dsh-*` 0.2.2-rc.1 package; GHSA-mrhx-c7xc-m3w5 affects `<4.6.6`. | Residual. `npm audit --omit=dev` reports **0 vulnerabilities**; the plugin does not bundle zod. No DSH 0.2.2 release with 4.6.6 exists and no forced override is applied. Re-audit after upstream. |
|
|
21
|
+
| A2 | Medium (deployment) | Same-origin checks are CSRF protection, not authentication. DSH exposes no host auth for plugin routes. | Mitigated by optional `adminToken`/`OPENCODE_ADMIN_TOKEN` with constant-time comparison on every `/dsh-opencode/*` route and a browser prompt. If no token is configured, the existing loopback/same-origin guard remains; this is documented, not represented as authentication. |
|
|
22
|
+
| A3 | Low (operational) | Production profile `pnpm-lock.yaml` files were not regenerated during manual `node_modules` deployments. | Residual operational note. Packages installed by verified tarballs; regenerate lockfiles separately before any `--frozen-lockfile` install. |
|
|
23
|
+
| A4 | Low (scope) | Engine compatibility cannot run from a worktree or force-kill a runtime under project rules. | `npm run test:engine -- <candidate.tgz>` validates immutable candidates read-only; compatibility execution stays on the approved MiniPC contour. |
|
|
24
|
+
|
|
25
|
+
No critical findings were verified. No raw API keys, prompt text, image bytes
|
|
26
|
+
or proxy passwords are written to analytics, heatmap, request logs or sticky /
|
|
27
|
+
cooldown state. Credential values are masked in the UI and diagnostics.
|
|
28
|
+
|
|
29
|
+
## Evidence
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm run check # 149 tests + typecheck + build
|
|
33
|
+
npm audit --omit=dev --json # 0 production vulnerabilities
|
|
34
|
+
npm ls zod --all # zod 4.4.3 through DSH dev packages
|
|
35
|
+
npm run test:engine -- <candidate.tgz>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- Production package contents are limited by `files`; no tests, sources,
|
|
39
|
+
credentials or worktrees are published.
|
|
40
|
+
- HTTP writes require a JSON body, same-origin `Origin`/`Referer`, a trusted
|
|
41
|
+
Host, and the admin token when configured.
|
|
42
|
+
- The updater is loopback-only and also sits behind the admin-token guard.
|
|
43
|
+
- MOA and other plugins were not touched.
|
|
44
|
+
|
|
45
|
+
## Conclusion
|
|
46
|
+
|
|
47
|
+
The production dependency surface is clean; the only high advisory is a
|
|
48
|
+
dev/peer `zod` pin inherited from DSH and blocked on an upstream release. The
|
|
49
|
+
remaining deployment caveats (optional admin token, profile lockfiles) are
|
|
50
|
+
documented and tracked. No open critical or correctness issue was found in the
|
|
51
|
+
reviewed scope.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.34 — release 11: engine matrix and audit closure
|
|
4
|
+
|
|
5
|
+
- **#55 — engine matrix**: `npm run test:engine -- <candidate.tgz>` validates one immutable candidate against `engines.dsh` and `dsh.compatibility.dshReleases`, and rejects test/source content. It never installs DSH, starts a runtime, extracts the tarball, uses a worktree path, or performs forced cleanup; compatibility execution remains on the approved MiniPC contour.
|
|
6
|
+
- **#39 — project/security audit**: added `AUDIT.md` with verified findings. Production dependencies audit clean; the dev/peer `zod` advisory and optional-auth/lockfile caveats are recorded as residuals.
|
|
7
|
+
- **#82 — Duskriver parity**: all 18 child issues are closed; the parity parent is closed with the evidence index.
|
|
8
|
+
- 149 tests.
|
|
9
|
+
|
|
10
|
+
## 0.5.33 — release 10: admin token and dependency audit
|
|
11
|
+
|
|
12
|
+
- **#41 — authenticated HTTP routes**: optional owner `adminToken` (profile config or `OPENCODE_ADMIN_TOKEN`). When set, every `/dsh-opencode/*` route requires `x-opencode-token` or a Bearer token with constant-time comparison; forged Host/Origin alone no longer passes. The settings UI prompts for the token and keeps it in this browser.
|
|
13
|
+
- **#42 — zod audit**: confirmed `zod` 4.4.3 is pinned by all DSH 0.2.2-rc.1 packages; GHSA-mrhx-c7xc-m3w5 affects `<4.6.6`. No DSH release with the fix exists, this plugin does not bundle zod, and no forced override was added. Residual exposure is documented and must be re-audited after the upstream update.
|
|
14
|
+
- 145 tests.
|
|
15
|
+
|
|
3
16
|
## 0.5.32 — release 9: proxy auth and route switches
|
|
4
17
|
|
|
5
18
|
- **#71 — authenticated proxy**: `proxyUsername` plus a password stored in the credential service enable Basic auth for HTTP/HTTPS and SOCKS5 proxies. The authenticated URL exists only in memory; `/state` returns the credential-free address and a `configured` flag, never the password.
|
package/README.md
CHANGED
|
@@ -133,12 +133,12 @@ npm pack
|
|
|
133
133
|
Install the **packed tarball** into a profile:
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
136
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.34.tgz
|
|
137
137
|
# DSH Desktop:
|
|
138
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
138
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.34.tgz
|
|
139
139
|
|
|
140
140
|
# Or install the published package:
|
|
141
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
141
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.34
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
Restart dsh (or DSH Desktop), then pick **OpenCode (plugin)** or **OpenCode Go (plugin)** in the model picker.
|
|
@@ -304,6 +304,10 @@ npm run sync:models # regenerate src/models.generated.ts from models.dev
|
|
|
304
304
|
|
|
305
305
|
GitHub Actions runs `npm run check` and `npm pack` on push/PR (`.github/workflows/ci.yml`).
|
|
306
306
|
|
|
307
|
+
### Engine matrix
|
|
308
|
+
|
|
309
|
+
`npm run test:engine -- <candidate.tgz>` validates one immutable, already-built package candidate against `engines.dsh` and `dsh.compatibility.dshReleases`, and checks that only production files are packaged. The command is read-only: it does not install DSH, start a runtime, extract the tarball, touch another plugin, or perform cleanup. Compatibility execution runs on the approved MiniPC test contour against the published candidate — never against a worktree path and never with forced processes or recursive deletion.
|
|
310
|
+
|
|
307
311
|
## Limitations
|
|
308
312
|
|
|
309
313
|
- File attachments are forwarded as DSH handle text, not inline bytes; the harness normally supplies the read-only execution path. Tool-result images are inline on Anthropic, OpenAI Responses and Google; OpenAI Chat uses a placeholder.
|
|
@@ -315,7 +319,12 @@ GitHub Actions runs `npm run check` and `npm pack` on push/PR (`.github/workflow
|
|
|
315
319
|
- `opencode-direct` (Console) billing shows no per-model allowance; the Go route is the primary target.
|
|
316
320
|
- The anonymous free lane (`Bearer public` + CLI impersonation) is not implemented. A keyless `big-pickle` probe on 2026-11-05 returned HTTP 429 `FreeUsageLimitError`; use the keyed Console/Zen route for free models.
|
|
317
321
|
- The UI requires a web profile (the Host needs `ctx.webServer`); the TUI keeps the `/opencode` command and the model routes.
|
|
318
|
-
-
|
|
322
|
+
- Same-origin checks are CSRF protection, not authentication. Set an owner admin token in the profile config (`adminToken`) or `OPENCODE_ADMIN_TOKEN`; every `/dsh-opencode/*` route then requires `x-opencode-token` or `Authorization: Bearer`. The settings UI asks for the token once and keeps it in this browser. Without a token, routes retain the loopback/same-origin guard only.
|
|
323
|
+
- `npm audit` still reports `zod < 4.6.6` (GHSA-mrhx-c7xc-m3w5) because every `@deepseek-ai/dsh-*` 0.2.2-rc.1 package pins `zod` 4.4.3. No DSH 0.2.2 release with 4.6.6 exists yet; this plugin does not bundle zod and no forced override is used. Re-audit after the upstream DSH update.
|
|
324
|
+
|
|
325
|
+
## Audit
|
|
326
|
+
|
|
327
|
+
The read-only project and security review, including residual dependency and deployment findings, is in [AUDIT.md](./AUDIT.md).
|
|
319
328
|
|
|
320
329
|
## License
|
|
321
330
|
|
package/README.ru.md
CHANGED
|
@@ -132,12 +132,12 @@ npm pack
|
|
|
132
132
|
Установка из **упакованного tarball**:
|
|
133
133
|
|
|
134
134
|
```bash
|
|
135
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
135
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.34.tgz
|
|
136
136
|
# DSH Desktop:
|
|
137
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
137
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.34.tgz
|
|
138
138
|
|
|
139
139
|
# Либо установка опубликованного пакета из npm:
|
|
140
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
140
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.34
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
После установки перезапустите dsh (или DSH Desktop) и выберите **OpenCode (plugin)** или **OpenCode Go (plugin)** в списке моделей.
|
|
@@ -211,9 +211,14 @@ modelAccountRules:
|
|
|
211
211
|
|
|
212
212
|
Язык вывода задаётся в **Настройки → OpenCode → `/opencode` command language** (`中文` включает китайский). По умолчанию английский.
|
|
213
213
|
|
|
214
|
+
### Матрица движков
|
|
215
|
+
|
|
216
|
+
`npm run test:engine -- <candidate.tgz>` проверяет неизменяемый уже собранный пакет по `engines.dsh` и `dsh.compatibility.dshReleases`, а также состав production-файлов. Команда только читает: не ставит DSH, не запускает runtime, не распаковывает архив, не трогает другие плагины и не требует cleanup. Прогон совместимости выполняется на согласованном тестовом контуре MiniPC с опубликованным кандидатом — не из worktree и без принудительных процессов или рекурсивного удаления.
|
|
217
|
+
|
|
214
218
|
## Безопасность и надежность
|
|
215
219
|
|
|
216
|
-
-
|
|
220
|
+
- Same-origin — это защита от CSRF, а не аутентификация. Задайте admin-токен владельца в конфиге профиля (`adminToken`) или `OPENCODE_ADMIN_TOKEN`; тогда все маршруты `/dsh-opencode/*` требуют `x-opencode-token` или `Authorization: Bearer`. UI спросит токен один раз и сохранит его в браузере. Без токена остаются только loopback и same-origin проверки.
|
|
221
|
+
- `npm audit` по-прежнему показывает `zod < 4.6.6` (GHSA-mrhx-c7xc-m3w5): пакеты `@deepseek-ai/dsh-*` 0.2.2-rc.1 фиксируют zod 4.4.3. Релиза DSH 0.2.2 с 4.6.6 пока нет; плагин не бандлит zod и не применяет принудительный override. Повторить аудит после обновления DSH.
|
|
217
222
|
- Тексты запросов и ответов пользователей никогда не записываются в файлы журнала.
|
|
218
223
|
- При исчерпании квоты на одном ключе диалог пользователя не прерывается благодаря бесшовной ротации.
|
|
219
224
|
|
package/README.zh.md
CHANGED
|
@@ -127,12 +127,12 @@ npm pack
|
|
|
127
127
|
通过**打包文件 (.tgz)**安装:
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
130
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.34.tgz
|
|
131
131
|
# DSH 桌面端:
|
|
132
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
132
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.34.tgz
|
|
133
133
|
|
|
134
134
|
# 或从 npm 安装已发布版本:
|
|
135
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
135
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.34
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
安装完成后重启 dsh 或 DSH 桌面端,在模型选择器中选择 **OpenCode (plugin)** 或 **OpenCode Go (plugin)** 即可。
|
|
@@ -186,9 +186,14 @@ modelAccountRules:
|
|
|
186
186
|
|
|
187
187
|
在 **设置 → OpenCode → `/opencode` 命令语言** 中选择 `中文` 即可切换输出语言,默认英语。
|
|
188
188
|
|
|
189
|
+
### 引擎矩阵
|
|
190
|
+
|
|
191
|
+
`npm run test:engine -- <candidate.tgz>` 使用 `engines.dsh` 与 `dsh.compatibility.dshReleases` 校验不可变的已构建包,并检查只打包生产文件。该命令只读:不安装 DSH、不启动运行时、不解压归档、不修改其他插件,也不需要清理。兼容性执行在批准的 MiniPC 测试环境上使用已发布候选包完成,绝不使用 worktree 路径,也不强制结束进程或递归删除。
|
|
192
|
+
|
|
189
193
|
## 安全规范
|
|
190
194
|
|
|
191
|
-
-
|
|
195
|
+
- 同源检查用于防御 CSRF,并非身份认证。在配置文件(`adminToken`)或 `OPENCODE_ADMIN_TOKEN` 中设置管理员令牌后,所有 `/dsh-opencode/*` 路由都要求 `x-opencode-token` 或 `Authorization: Bearer`;设置界面会要求输入一次并保存在当前浏览器。未设置令牌时仍只有 Loopback 与同源检查。
|
|
196
|
+
- `npm audit` 仍报告 `zod < 4.6.6`(GHSA-mrhx-c7xc-m3w5):`@deepseek-ai/dsh-*` 0.2.2-rc.1 固定使用 zod 4.4.3。目前没有使用 4.6.6 的 DSH 0.2.2 版本;本插件不打包 zod,也不使用强制 override。待上游 DSH 更新后重新审计。
|
|
192
197
|
- 密钥冷却状态通过 SHA-256 哈希指纹持久化,本地日志不保存提示词与明文密钥。
|
|
193
198
|
|
|
194
199
|
## 许可证
|
package/lib/client.js
CHANGED
|
@@ -202,6 +202,10 @@ window.__ModuleLoader__.load({
|
|
|
202
202
|
routeConsole: "OpenCode Console / Zen",
|
|
203
203
|
routeGo: "OpenCode Go / Go Plus",
|
|
204
204
|
enabledRoutesHint: "Disabled routes are removed from the picker; at least one route stays enabled.",
|
|
205
|
+
adminToken: "Admin token",
|
|
206
|
+
adminTokenHint: "This Host requires the admin token configured by its owner. It is stored in this browser only.",
|
|
207
|
+
adminTokenPlaceholder: "Paste the admin token",
|
|
208
|
+
continue: "Continue",
|
|
205
209
|
maxOutputTokens: "Output limit (empty = model default, 0 = omit)",
|
|
206
210
|
contextOverrideShort: "Context",
|
|
207
211
|
maxOutputOverrideShort: "Max output",
|
|
@@ -449,6 +453,10 @@ window.__ModuleLoader__.load({
|
|
|
449
453
|
routeConsole: "OpenCode Console / Zen",
|
|
450
454
|
routeGo: "OpenCode Go / Go Plus",
|
|
451
455
|
enabledRoutesHint: "禁用的路由会从选择器中移除;至少保留一个启用的路由。",
|
|
456
|
+
adminToken: "管理令牌",
|
|
457
|
+
adminTokenHint: "此 Host 要求所有者配置的管理令牌。令牌仅保存在当前浏览器中。",
|
|
458
|
+
adminTokenPlaceholder: "粘贴管理令牌",
|
|
459
|
+
continue: "继续",
|
|
452
460
|
maxOutputTokens: "输出上限(空 = 模型默认,0 = 省略)",
|
|
453
461
|
contextOverrideShort: "上下文",
|
|
454
462
|
maxOutputOverrideShort: "最大输出",
|
|
@@ -505,6 +513,23 @@ window.__ModuleLoader__.load({
|
|
|
505
513
|
}
|
|
506
514
|
};
|
|
507
515
|
const GO_PROVIDER = "opencode-go-direct";
|
|
516
|
+
// src/client/admin-token.ts
|
|
517
|
+
const KEY = "dsh-opencode-admin-token";
|
|
518
|
+
function storage() {
|
|
519
|
+
try {
|
|
520
|
+
return typeof localStorage === "undefined" ? void 0 : localStorage;
|
|
521
|
+
} catch {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function getAdminToken() {
|
|
526
|
+
return storage()?.getItem(KEY) ?? "";
|
|
527
|
+
}
|
|
528
|
+
function setAdminToken(token) {
|
|
529
|
+
const value = token.trim();
|
|
530
|
+
if (value === "") storage()?.removeItem(KEY);
|
|
531
|
+
else storage()?.setItem(KEY, value);
|
|
532
|
+
}
|
|
508
533
|
// src/client/api.ts
|
|
509
534
|
async function call(path, init) {
|
|
510
535
|
const response = await fetch(path, {
|
|
@@ -513,6 +538,7 @@ window.__ModuleLoader__.load({
|
|
|
513
538
|
headers: {
|
|
514
539
|
accept: "application/json",
|
|
515
540
|
...init?.body === void 0 ? {} : { "content-type": "application/json" },
|
|
541
|
+
...getAdminToken() === "" ? {} : { "x-opencode-token": getAdminToken() },
|
|
516
542
|
...init?.headers ?? {}
|
|
517
543
|
}
|
|
518
544
|
});
|
|
@@ -2056,6 +2082,7 @@ window.__ModuleLoader__.load({
|
|
|
2056
2082
|
const [keyDraft, setKeyDraft] = (0, react.useState)("");
|
|
2057
2083
|
const [proxyPassword, setProxyPassword] = (0, react.useState)("");
|
|
2058
2084
|
const [proxyBusy, setProxyBusy] = (0, react.useState)(false);
|
|
2085
|
+
const [tokenDraft, setTokenDraft] = (0, react.useState)("");
|
|
2059
2086
|
const [keyDrafts, setKeyDrafts] = (0, react.useState)({});
|
|
2060
2087
|
const [search, setSearch] = (0, react.useState)("");
|
|
2061
2088
|
const [testModel, setTestModel] = (0, react.useState)("deepseek-v4.1-flash");
|
|
@@ -2195,20 +2222,56 @@ window.__ModuleLoader__.load({
|
|
|
2195
2222
|
className: "oc-hint",
|
|
2196
2223
|
children: t("loading")
|
|
2197
2224
|
});
|
|
2198
|
-
if (state === null || draft === null)
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2225
|
+
if (state === null || draft === null) {
|
|
2226
|
+
if (error?.includes("admin token required") === true) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2227
|
+
className: "oc-page",
|
|
2228
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2229
|
+
className: "oc-card",
|
|
2230
|
+
children: [
|
|
2231
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2232
|
+
className: "oc-card-title",
|
|
2233
|
+
children: t("adminToken")
|
|
2234
|
+
}),
|
|
2235
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2236
|
+
className: "oc-hint",
|
|
2237
|
+
children: t("adminTokenHint")
|
|
2238
|
+
}),
|
|
2239
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2240
|
+
className: "oc-input",
|
|
2241
|
+
type: "password",
|
|
2242
|
+
value: tokenDraft,
|
|
2243
|
+
placeholder: t("adminTokenPlaceholder"),
|
|
2244
|
+
onChange: (event) => setTokenDraft(event.target.value)
|
|
2245
|
+
}),
|
|
2246
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2247
|
+
type: "button",
|
|
2248
|
+
className: "oc-button",
|
|
2249
|
+
disabled: tokenDraft.trim() === "",
|
|
2250
|
+
onClick: () => {
|
|
2251
|
+
setAdminToken(tokenDraft);
|
|
2252
|
+
setTokenDraft("");
|
|
2253
|
+
load();
|
|
2254
|
+
},
|
|
2255
|
+
children: t("continue")
|
|
2256
|
+
})
|
|
2257
|
+
]
|
|
2258
|
+
})
|
|
2259
|
+
});
|
|
2260
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2261
|
+
className: "oc-page",
|
|
2262
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2263
|
+
className: "oc-error",
|
|
2264
|
+
children: error ?? t("failed")
|
|
2265
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2266
|
+
type: "button",
|
|
2267
|
+
className: "oc-button",
|
|
2268
|
+
onClick: () => {
|
|
2269
|
+
load();
|
|
2270
|
+
},
|
|
2271
|
+
children: t("refresh")
|
|
2272
|
+
})]
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2212
2275
|
const dirty = !safeSame(draft, draftOf(state));
|
|
2213
2276
|
const activeAccount = state.accounts.find((account) => account.active);
|
|
2214
2277
|
const defaultAccount = state.accounts.find((account) => account.default);
|
package/lib/index.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ interface Config {
|
|
|
24
24
|
apiKeyEnv?: string;
|
|
25
25
|
/** Literal key for composition config only; stripped from settings reads by `role('secret')`. */
|
|
26
26
|
apiKey?: string;
|
|
27
|
+
/** Optional admin token for the Host HTTP routes; also read from `OPENCODE_ADMIN_TOKEN`. */
|
|
28
|
+
adminToken?: string;
|
|
27
29
|
/** Console/Zen inference root; defaults to `https://opencode.ai/inference`. */
|
|
28
30
|
apiBase?: string;
|
|
29
31
|
/** Go inference root; defaults to `https://opencode.ai/inference/go`. */
|
|
@@ -101,6 +103,7 @@ declare const Config: z<Config>;
|
|
|
101
103
|
interface ResolvedConfig {
|
|
102
104
|
apiKeyEnv: string;
|
|
103
105
|
apiKey?: string;
|
|
106
|
+
adminToken: string;
|
|
104
107
|
apiBase: string;
|
|
105
108
|
goApiBase: string;
|
|
106
109
|
goZenBase: string;
|
|
@@ -421,6 +424,7 @@ interface RouteDeps {
|
|
|
421
424
|
readonly heatmap?: (days: number) => Promise<RequestHeatmap>;
|
|
422
425
|
readonly refreshTui?: () => void;
|
|
423
426
|
readonly syncRoutes?: () => void;
|
|
427
|
+
readonly adminToken?: () => string;
|
|
424
428
|
/** Test seam; defaults to the local managed-credentials registry. */
|
|
425
429
|
readonly managedPath?: string;
|
|
426
430
|
}
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
2
|
-
import {
|
|
2
|
+
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
3
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
3
4
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
4
5
|
import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
5
6
|
import { homedir, networkInterfaces } from "node:os";
|
|
@@ -7,7 +8,6 @@ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
|
7
8
|
import { LlmAdapter, LlmError, ReasoningEffortId, ToolCallId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
8
9
|
import z from "@deepseek-ai/schemastery";
|
|
9
10
|
import { ProxyAgent, Socks5ProxyAgent } from "undici";
|
|
10
|
-
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
11
11
|
import { spawn } from "node:child_process";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { isIP } from "node:net";
|
|
@@ -99,7 +99,7 @@ const DEFAULT_GO_API_BASE = "https://opencode.ai/inference/go";
|
|
|
99
99
|
const DEFAULT_GO_ZEN_BASE = "https://opencode.ai/zen/go/v1";
|
|
100
100
|
const DEFAULT_USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
|
|
101
101
|
const DEFAULT_AUTH_ENTRY = "opencode-go";
|
|
102
|
-
const DEFAULT_USER_AGENT = "dsh-opencode-provider/0.5.
|
|
102
|
+
const DEFAULT_USER_AGENT = "dsh-opencode-provider/0.5.34";
|
|
103
103
|
const DEFAULT_CONTEXT_WINDOW = 262144;
|
|
104
104
|
const DEFAULT_MAX_TOKENS = 32768;
|
|
105
105
|
const DEFAULT_GENERATE_MAX_TOKENS = 65536;
|
|
@@ -115,6 +115,7 @@ function markVolatile(schema) {
|
|
|
115
115
|
const Config = z.object({
|
|
116
116
|
apiKeyEnv: markVolatile(z.string().role("credential-ref").default(DEFAULT_API_KEY_ENV)),
|
|
117
117
|
apiKey: z.string().role("secret"),
|
|
118
|
+
adminToken: z.string().role("secret").default(""),
|
|
118
119
|
apiBase: markVolatile(z.string().default(DEFAULT_API_BASE)),
|
|
119
120
|
goApiBase: markVolatile(z.string().default(DEFAULT_GO_API_BASE)),
|
|
120
121
|
goZenBase: markVolatile(z.string().default(DEFAULT_GO_ZEN_BASE)),
|
|
@@ -239,6 +240,7 @@ function resolveConfig(config) {
|
|
|
239
240
|
return {
|
|
240
241
|
apiKeyEnv: typeof config.apiKeyEnv === "string" && config.apiKeyEnv.trim() !== "" ? config.apiKeyEnv.trim() : DEFAULT_API_KEY_ENV,
|
|
241
242
|
...apiKey === void 0 ? {} : { apiKey },
|
|
243
|
+
adminToken: typeof config.adminToken === "string" ? config.adminToken.trim() : "",
|
|
242
244
|
apiBase: typeof config.apiBase === "string" && config.apiBase !== "" ? config.apiBase : DEFAULT_API_BASE,
|
|
243
245
|
goApiBase: typeof config.goApiBase === "string" && config.goApiBase !== "" ? config.goApiBase : DEFAULT_GO_API_BASE,
|
|
244
246
|
goZenBase: typeof config.goZenBase === "string" && config.goZenBase !== "" ? config.goZenBase : DEFAULT_GO_ZEN_BASE,
|
|
@@ -5441,6 +5443,16 @@ async function buildState(ctx, deps) {
|
|
|
5441
5443
|
revision: settingsRevision(ctx)
|
|
5442
5444
|
};
|
|
5443
5445
|
}
|
|
5446
|
+
function adminAllowed(req, deps) {
|
|
5447
|
+
const expected = deps.adminToken?.() ?? "";
|
|
5448
|
+
if (expected === "") return true;
|
|
5449
|
+
const header = req.headers["x-opencode-token"];
|
|
5450
|
+
const authorization = req.headers.authorization;
|
|
5451
|
+
const provided = typeof header === "string" && header !== "" ? header : typeof authorization === "string" && authorization.startsWith("Bearer ") ? authorization.slice(7).trim() : "";
|
|
5452
|
+
const left = Buffer.from(expected);
|
|
5453
|
+
const right = Buffer.from(provided);
|
|
5454
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
5455
|
+
}
|
|
5444
5456
|
/** Register the Host HTTP surface consumed by the browser settings page. */
|
|
5445
5457
|
function applyRoutes(ctx, web, deps) {
|
|
5446
5458
|
const disposers = [];
|
|
@@ -5448,7 +5460,16 @@ function applyRoutes(ctx, web, deps) {
|
|
|
5448
5460
|
disposers.push(web.register({
|
|
5449
5461
|
kind: "exact",
|
|
5450
5462
|
path,
|
|
5451
|
-
handler
|
|
5463
|
+
handler: async (req, res) => {
|
|
5464
|
+
if (!adminAllowed(req, deps)) {
|
|
5465
|
+
json(res, 401, {
|
|
5466
|
+
error: "admin token required",
|
|
5467
|
+
authRequired: true
|
|
5468
|
+
});
|
|
5469
|
+
return;
|
|
5470
|
+
}
|
|
5471
|
+
await handler(req, res);
|
|
5472
|
+
}
|
|
5452
5473
|
}));
|
|
5453
5474
|
};
|
|
5454
5475
|
route("/dsh-opencode/state", async (req, res) => {
|
|
@@ -6311,7 +6332,8 @@ function apply(ctx, config) {
|
|
|
6311
6332
|
usage,
|
|
6312
6333
|
heatmap: readRequestHeatmap,
|
|
6313
6334
|
refreshTui: () => refreshTuiSettings?.(),
|
|
6314
|
-
syncRoutes
|
|
6335
|
+
syncRoutes,
|
|
6336
|
+
adminToken: () => current().adminToken || launchEnvironmentOf(ctx).get("OPENCODE_ADMIN_TOKEN")?.value || ""
|
|
6315
6337
|
});
|
|
6316
6338
|
webCtx.effect(() => registerPluginUpdater(webCtx, {
|
|
6317
6339
|
endpoint: "/dsh-opencode/update",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-opencode-provider",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.34",
|
|
4
4
|
"description": "OpenCode / OpenCode Go LLM provider plugin for DeepSeek Harness (dsh).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"CHANGELOG.md",
|
|
28
28
|
"LICENSE",
|
|
29
29
|
"NOTICE",
|
|
30
|
+
"AUDIT.md",
|
|
30
31
|
"media/visual-verification.png"
|
|
31
32
|
],
|
|
32
33
|
"publishConfig": {
|
|
@@ -70,10 +71,11 @@
|
|
|
70
71
|
"build": "tsdown",
|
|
71
72
|
"prepare": "tsdown 1>&2",
|
|
72
73
|
"typecheck": "tsc --noEmit",
|
|
73
|
-
"test": "node --import tsx --test tests/core.test.ts tests/routes.test.ts tests/limits.test.ts tests/analytics.test.ts tests/adapter.test.ts tests/usage.test.ts tests/request-log.test.ts tests/account-rules.test.ts tests/commands.test.ts tests/tool-schema.test.ts tests/tui-settings.test.ts tests/plan-filter.test.ts tests/image-budget.test.ts tests/tools.test.ts tests/plugin-updater.test.ts tests/client-render.test.tsx tests/sse.test.ts tests/proxy.test.ts tests/tool-call-validation.test.ts tests/pricing.test.ts",
|
|
74
|
+
"test": "node --import tsx --test tests/core.test.ts tests/routes.test.ts tests/limits.test.ts tests/analytics.test.ts tests/adapter.test.ts tests/usage.test.ts tests/request-log.test.ts tests/account-rules.test.ts tests/commands.test.ts tests/tool-schema.test.ts tests/tui-settings.test.ts tests/plan-filter.test.ts tests/image-budget.test.ts tests/tools.test.ts tests/plugin-updater.test.ts tests/client-render.test.tsx tests/sse.test.ts tests/proxy.test.ts tests/tool-call-validation.test.ts tests/pricing.test.ts tests/engine-matrix.test.ts",
|
|
74
75
|
"check": "npm run typecheck && npm test && npm run build",
|
|
75
76
|
"release": "npm run check && npm pack",
|
|
76
|
-
"sync:models": "node scripts/sync-models.mjs"
|
|
77
|
+
"sync:models": "node scripts/sync-models.mjs",
|
|
78
|
+
"test:engine": "node scripts/test-engines.mjs"
|
|
77
79
|
},
|
|
78
80
|
"keywords": [
|
|
79
81
|
"dsh",
|