@goodandready/dsh-opencode-provider 0.5.28 → 0.5.29
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 +4 -4
- package/README.ru.md +3 -3
- package/README.zh.md +3 -3
- package/lib/client.js +10 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +6 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.29 — release 6: catalog refresh and Free-lane decision
|
|
4
|
+
|
|
5
|
+
- **#77 — catalog refresh**: `catalogRefreshMinutes` (1–10,080, default 10) controls the live `/models` cache; explicit `/models/refresh` still bypasses it. Exposed in Settings.
|
|
6
|
+
- **#76 — Free-lane decision**: re-probed keyless `big-pickle` on 2026-11-05; HTTP 429 `FreeUsageLimitError`. No keyless route or header-spoofing lane is added; the keyed Console/Zen path remains supported. Evidence recorded in DESIGN/README.
|
|
7
|
+
- 137 tests.
|
|
8
|
+
|
|
3
9
|
## 0.5.28 — release 5: image policy and preparation timeout
|
|
4
10
|
|
|
5
11
|
- **#73 — image policy**: `maxImages`, `maxRequestImageBytes`, `requestImagePixelBudget` and `requestImageMaxBytes` are configurable in Settings; defaults preserve the previous 50 images / 24 MiB / 1568px / 1 MiB behavior. Count and pixel policies are covered by tests.
|
package/README.md
CHANGED
|
@@ -132,12 +132,12 @@ npm pack
|
|
|
132
132
|
Install the **packed tarball** into a profile:
|
|
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.29.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.29.tgz
|
|
138
138
|
|
|
139
139
|
# Or install the published package:
|
|
140
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
140
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.29
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
Restart dsh (or DSH Desktop), then pick **OpenCode (plugin)** or **OpenCode Go (plugin)** in the model picker.
|
|
@@ -310,7 +310,7 @@ GitHub Actions runs `npm run check` and `npm pack` on push/PR (`.github/workflow
|
|
|
310
310
|
- 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.
|
|
311
311
|
- 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.
|
|
312
312
|
- `opencode-direct` (Console) billing shows no per-model allowance; the Go route is the primary target.
|
|
313
|
-
- The anonymous free lane (`Bearer public` + CLI impersonation) is not implemented.
|
|
313
|
+
- 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.
|
|
314
314
|
- The UI requires a web profile (the Host needs `ctx.webServer`); the TUI keeps the `/opencode` command and the model routes.
|
|
315
315
|
- 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.
|
|
316
316
|
|
package/README.ru.md
CHANGED
|
@@ -131,12 +131,12 @@ npm pack
|
|
|
131
131
|
Установка из **упакованного tarball**:
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
134
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.29.tgz
|
|
135
135
|
# DSH Desktop:
|
|
136
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
136
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.29.tgz
|
|
137
137
|
|
|
138
138
|
# Либо установка опубликованного пакета из npm:
|
|
139
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
139
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.29
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
После установки перезапустите dsh (или DSH Desktop) и выберите **OpenCode (plugin)** или **OpenCode Go (plugin)** в списке моделей.
|
package/README.zh.md
CHANGED
|
@@ -126,12 +126,12 @@ npm pack
|
|
|
126
126
|
通过**打包文件 (.tgz)**安装:
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
129
|
+
dsh plugin --profile web add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.29.tgz
|
|
130
130
|
# DSH 桌面端:
|
|
131
|
-
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.
|
|
131
|
+
dsh plugin --profile desktop add /absolute/path/to/goodandready-dsh-opencode-provider-0.5.29.tgz
|
|
132
132
|
|
|
133
133
|
# 或从 npm 安装已发布版本:
|
|
134
|
-
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.
|
|
134
|
+
dsh plugin --profile web add @goodandready/dsh-opencode-provider@0.5.29
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
安装完成后重启 dsh 或 DSH 桌面端,在模型选择器中选择 **OpenCode (plugin)** 或 **OpenCode Go (plugin)** 即可。
|
package/lib/client.js
CHANGED
|
@@ -186,6 +186,7 @@ window.__ModuleLoader__.load({
|
|
|
186
186
|
maxRequestImageBytes: "Request image budget (bytes)",
|
|
187
187
|
requestImagePixelBudget: "Image pixel budget",
|
|
188
188
|
requestImageMaxBytes: "Per-image max bytes",
|
|
189
|
+
catalogRefreshMinutes: "Catalog refresh (minutes)",
|
|
189
190
|
proxyUrl: "Proxy URL",
|
|
190
191
|
proxyUrlHint: "HTTP, HTTPS or SOCKS5 proxy for inference, catalog and usage. Empty disables it. Credentials are not supported.",
|
|
191
192
|
maxOutputTokens: "Output limit (empty = model default, 0 = omit)",
|
|
@@ -419,6 +420,7 @@ window.__ModuleLoader__.load({
|
|
|
419
420
|
maxRequestImageBytes: "请求图片预算(字节)",
|
|
420
421
|
requestImagePixelBudget: "图片像素预算",
|
|
421
422
|
requestImageMaxBytes: "单图最大字节数",
|
|
423
|
+
catalogRefreshMinutes: "目录刷新间隔(分钟)",
|
|
422
424
|
proxyUrl: "代理地址",
|
|
423
425
|
proxyUrlHint: "用于推理、目录和用量的 HTTP、HTTPS 或 SOCKS5 代理。留空则禁用。不支持代理认证。",
|
|
424
426
|
maxOutputTokens: "输出上限(空 = 模型默认,0 = 省略)",
|
|
@@ -1712,6 +1714,7 @@ window.__ModuleLoader__.load({
|
|
|
1712
1714
|
requestImagePixelBudget: String(state.config.requestImagePixelBudget),
|
|
1713
1715
|
requestImageMaxBytes: String(state.config.requestImageMaxBytes),
|
|
1714
1716
|
proxyUrl: state.config.proxyUrl,
|
|
1717
|
+
catalogRefreshMinutes: String(state.config.catalogRefreshMinutes),
|
|
1715
1718
|
maxOutputTokens: state.config.maxOutputTokens === null ? "" : String(state.config.maxOutputTokens),
|
|
1716
1719
|
contextWindow: state.config.contextWindow === null ? "" : String(state.config.contextWindow),
|
|
1717
1720
|
contextWindowOverrides: Object.fromEntries(Object.entries(state.config.contextWindowOverrides).map(([id, value]) => [id, String(value)])),
|
|
@@ -1764,6 +1767,7 @@ window.__ModuleLoader__.load({
|
|
|
1764
1767
|
requestImagePixelBudget: numberOrNull(draft.requestImagePixelBudget) ?? 2458624,
|
|
1765
1768
|
requestImageMaxBytes: numberOrNull(draft.requestImageMaxBytes) ?? 1048576,
|
|
1766
1769
|
proxyUrl: draft.proxyUrl.trim(),
|
|
1770
|
+
catalogRefreshMinutes: numberOrNull(draft.catalogRefreshMinutes) ?? 10,
|
|
1767
1771
|
maxOutputTokens: numberOrNull(draft.maxOutputTokens),
|
|
1768
1772
|
contextWindow: numberOrNull(draft.contextWindow),
|
|
1769
1773
|
contextWindowOverrides: numberRecordOrNull(draft.contextWindowOverrides),
|
|
@@ -3198,6 +3202,12 @@ window.__ModuleLoader__.load({
|
|
|
3198
3202
|
value: draft.requestImageMaxBytes,
|
|
3199
3203
|
onChange: (value) => update({ requestImageMaxBytes: value })
|
|
3200
3204
|
}),
|
|
3205
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3206
|
+
label: t("catalogRefreshMinutes"),
|
|
3207
|
+
type: "number",
|
|
3208
|
+
value: draft.catalogRefreshMinutes,
|
|
3209
|
+
onChange: (value) => update({ catalogRefreshMinutes: value })
|
|
3210
|
+
}),
|
|
3201
3211
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
3202
3212
|
label: t("proxyUrl"),
|
|
3203
3213
|
hint: t("proxyUrlHint"),
|
package/lib/index.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ interface Config {
|
|
|
49
49
|
requestImageMaxBytes?: number;
|
|
50
50
|
/** HTTP/HTTPS/SOCKS5 proxy for inference, catalog and usage requests. */
|
|
51
51
|
proxyUrl?: string;
|
|
52
|
+
/** Live catalog cache lifetime in minutes; defaults to 10. */
|
|
53
|
+
catalogRefreshMinutes?: number;
|
|
52
54
|
/** Visible-model allowlist; an empty list means "show everything". */
|
|
53
55
|
visibleModels?: string[];
|
|
54
56
|
/** Per-model visibility overrides; wins over `visibleModels`. */
|
|
@@ -101,6 +103,7 @@ interface ResolvedConfig {
|
|
|
101
103
|
requestImagePixelBudget: number;
|
|
102
104
|
requestImageMaxBytes: number;
|
|
103
105
|
proxyUrl: string;
|
|
106
|
+
catalogRefreshMinutes: number;
|
|
104
107
|
visibleModels: readonly string[];
|
|
105
108
|
modelVisibility: Readonly<Record<string, boolean>>;
|
|
106
109
|
filterModelsByPlan: boolean;
|
package/lib/index.js
CHANGED
|
@@ -81,7 +81,7 @@ const DEFAULT_GO_API_BASE = "https://opencode.ai/inference/go";
|
|
|
81
81
|
const DEFAULT_GO_ZEN_BASE = "https://opencode.ai/zen/go/v1";
|
|
82
82
|
const DEFAULT_USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
|
|
83
83
|
const DEFAULT_AUTH_ENTRY = "opencode-go";
|
|
84
|
-
const DEFAULT_USER_AGENT = "dsh-opencode-provider/0.5.
|
|
84
|
+
const DEFAULT_USER_AGENT = "dsh-opencode-provider/0.5.29";
|
|
85
85
|
const DEFAULT_CONTEXT_WINDOW = 262144;
|
|
86
86
|
const DEFAULT_MAX_TOKENS = 32768;
|
|
87
87
|
const DEFAULT_GENERATE_MAX_TOKENS = 65536;
|
|
@@ -109,6 +109,7 @@ const Config = z.object({
|
|
|
109
109
|
requestImagePixelBudget: markVolatile(z.number().min(1).max(1e8).step(1).default(DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET)),
|
|
110
110
|
requestImageMaxBytes: markVolatile(z.number().min(1).max(1e8).step(1).default(DEFAULT_REQUEST_IMAGE_MAX_BYTES)),
|
|
111
111
|
proxyUrl: markVolatile(z.string().default("")),
|
|
112
|
+
catalogRefreshMinutes: markVolatile(z.number().min(1).max(10080).step(1).default(10)),
|
|
112
113
|
visibleModels: markVolatile(z.array(z.string()).default([])),
|
|
113
114
|
modelVisibility: markVolatile(z.dict(z.boolean()).default({})),
|
|
114
115
|
filterModelsByPlan: markVolatile(z.boolean().default(true)),
|
|
@@ -227,6 +228,7 @@ function resolveConfig(config) {
|
|
|
227
228
|
requestImagePixelBudget: positiveInt(config.requestImagePixelBudget, DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET),
|
|
228
229
|
requestImageMaxBytes: positiveInt(config.requestImageMaxBytes, DEFAULT_REQUEST_IMAGE_MAX_BYTES),
|
|
229
230
|
proxyUrl: normalizeProxyUrl(config.proxyUrl),
|
|
231
|
+
catalogRefreshMinutes: positiveInt(config.catalogRefreshMinutes, 10),
|
|
230
232
|
visibleModels: Array.isArray(config.visibleModels) ? config.visibleModels.filter((id) => typeof id === "string" && id !== "") : [],
|
|
231
233
|
modelVisibility: booleanRecord(config.modelVisibility),
|
|
232
234
|
filterModelsByPlan: config.filterModelsByPlan !== false,
|
|
@@ -1066,7 +1068,6 @@ function describeLimitsShort(limits) {
|
|
|
1066
1068
|
return `Go ${formatRequests(limits.go.fiveHour)}/5h · ${formatRequests(limits.go.weekly)}/wk`;
|
|
1067
1069
|
}
|
|
1068
1070
|
// src/catalog.ts
|
|
1069
|
-
const CATALOG_TTL_MS = 6e5;
|
|
1070
1071
|
const CATALOG_TIMEOUT_MS = 15e3;
|
|
1071
1072
|
const catalogCache = /* @__PURE__ */ new Map();
|
|
1072
1073
|
const FALLBACK_META = {
|
|
@@ -1144,7 +1145,7 @@ async function fetchLiveIds(provider, config, apiKey, fetchImpl) {
|
|
|
1144
1145
|
const url = modelsUrlFor(provider, config);
|
|
1145
1146
|
const cacheKey = `${provider}|${url}`;
|
|
1146
1147
|
const cached = catalogCache.get(cacheKey);
|
|
1147
|
-
if (cached !== void 0 && Date.now() - cached.at <
|
|
1148
|
+
if (cached !== void 0 && Date.now() - cached.at < config.catalogRefreshMinutes * 6e4) return cached.ids;
|
|
1148
1149
|
const headers = {
|
|
1149
1150
|
accept: "application/json",
|
|
1150
1151
|
"user-agent": config.userAgent,
|
|
@@ -4794,6 +4795,7 @@ function publicConfig(config) {
|
|
|
4794
4795
|
requestImagePixelBudget: config.requestImagePixelBudget,
|
|
4795
4796
|
requestImageMaxBytes: config.requestImageMaxBytes,
|
|
4796
4797
|
proxyUrl: config.proxyUrl,
|
|
4798
|
+
catalogRefreshMinutes: config.catalogRefreshMinutes,
|
|
4797
4799
|
visibleModels: config.visibleModels,
|
|
4798
4800
|
modelVisibility: config.modelVisibility,
|
|
4799
4801
|
filterModelsByPlan: config.filterModelsByPlan,
|
|
@@ -4995,6 +4997,7 @@ function configOps(patch) {
|
|
|
4995
4997
|
if ("requestImagePixelBudget" in patch) set("requestImagePixelBudget", patch.requestImagePixelBudget);
|
|
4996
4998
|
if ("requestImageMaxBytes" in patch) set("requestImageMaxBytes", patch.requestImageMaxBytes);
|
|
4997
4999
|
if ("proxyUrl" in patch) set("proxyUrl", normalizeProxyUrl(typeof patch.proxyUrl === "string" ? patch.proxyUrl : ""));
|
|
5000
|
+
if ("catalogRefreshMinutes" in patch) set("catalogRefreshMinutes", patch.catalogRefreshMinutes);
|
|
4998
5001
|
if ("visibleModels" in patch) set("visibleModels", cleanStringArray(patch.visibleModels));
|
|
4999
5002
|
if ("modelVisibility" in patch) set("modelVisibility", cleanBooleanRecord(patch.modelVisibility));
|
|
5000
5003
|
if ("filterModelsByPlan" in patch) set("filterModelsByPlan", patch.filterModelsByPlan === true);
|