@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 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.28.tgz
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.28.tgz
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.28
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.28.tgz
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.28.tgz
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.28
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.28.tgz
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.28.tgz
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.28
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.28";
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 < CATALOG_TTL_MS) return cached.ids;
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-opencode-provider",
3
- "version": "0.5.28",
3
+ "version": "0.5.29",
4
4
  "description": "OpenCode / OpenCode Go LLM provider plugin for DeepSeek Harness (dsh).",
5
5
  "license": "MIT",
6
6
  "type": "module",