@goodandready/dsh-opencode-provider 0.5.32 → 0.5.33
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 +6 -0
- package/README.md +5 -4
- package/README.ru.md +5 -4
- package/README.zh.md +5 -4
- package/lib/client.js +77 -14
- package/lib/index.d.ts +4 -0
- package/lib/index.js +27 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.33 — release 10: admin token and dependency audit
|
|
4
|
+
|
|
5
|
+
- **#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.
|
|
6
|
+
- **#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.
|
|
7
|
+
- 145 tests.
|
|
8
|
+
|
|
3
9
|
## 0.5.32 — release 9: proxy auth and route switches
|
|
4
10
|
|
|
5
11
|
- **#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.33.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.33.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.33
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
Restart dsh (or DSH Desktop), then pick **OpenCode (plugin)** or **OpenCode Go (plugin)** in the model picker.
|
|
@@ -315,7 +315,8 @@ GitHub Actions runs `npm run check` and `npm pack` on push/PR (`.github/workflow
|
|
|
315
315
|
- `opencode-direct` (Console) billing shows no per-model allowance; the Go route is the primary target.
|
|
316
316
|
- 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
317
|
- The UI requires a web profile (the Host needs `ctx.webServer`); the TUI keeps the `/opencode` command and the model routes.
|
|
318
|
-
-
|
|
318
|
+
- 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.
|
|
319
|
+
- `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.
|
|
319
320
|
|
|
320
321
|
## License
|
|
321
322
|
|
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.33.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.33.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.33
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
После установки перезапустите dsh (или DSH Desktop) и выберите **OpenCode (plugin)** или **OpenCode Go (plugin)** в списке моделей.
|
|
@@ -213,7 +213,8 @@ modelAccountRules:
|
|
|
213
213
|
|
|
214
214
|
## Безопасность и надежность
|
|
215
215
|
|
|
216
|
-
-
|
|
216
|
+
- Same-origin — это защита от CSRF, а не аутентификация. Задайте admin-токен владельца в конфиге профиля (`adminToken`) или `OPENCODE_ADMIN_TOKEN`; тогда все маршруты `/dsh-opencode/*` требуют `x-opencode-token` или `Authorization: Bearer`. UI спросит токен один раз и сохранит его в браузере. Без токена остаются только loopback и same-origin проверки.
|
|
217
|
+
- `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
218
|
- Тексты запросов и ответов пользователей никогда не записываются в файлы журнала.
|
|
218
219
|
- При исчерпании квоты на одном ключе диалог пользователя не прерывается благодаря бесшовной ротации.
|
|
219
220
|
|
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.33.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.33.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.33
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
安装完成后重启 dsh 或 DSH 桌面端,在模型选择器中选择 **OpenCode (plugin)** 或 **OpenCode Go (plugin)** 即可。
|
|
@@ -188,7 +188,8 @@ modelAccountRules:
|
|
|
188
188
|
|
|
189
189
|
## 安全规范
|
|
190
190
|
|
|
191
|
-
-
|
|
191
|
+
- 同源检查用于防御 CSRF,并非身份认证。在配置文件(`adminToken`)或 `OPENCODE_ADMIN_TOKEN` 中设置管理员令牌后,所有 `/dsh-opencode/*` 路由都要求 `x-opencode-token` 或 `Authorization: Bearer`;设置界面会要求输入一次并保存在当前浏览器。未设置令牌时仍只有 Loopback 与同源检查。
|
|
192
|
+
- `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
193
|
- 密钥冷却状态通过 SHA-256 哈希指纹持久化,本地日志不保存提示词与明文密钥。
|
|
193
194
|
|
|
194
195
|
## 许可证
|
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.33";
|
|
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",
|