dsh-opencode-go 0.1.12 → 0.1.14
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/README.en.md +16 -6
- package/README.md +16 -7
- package/THIRD_PARTY_NOTICES.md +2 -0
- package/docs/verification.md +46 -0
- package/lib/client.js +100 -22
- package/lib/index.js +130 -23
- package/lib/types/adapter.d.ts +3 -3
- package/lib/types/catalog.d.ts +10 -2
- package/lib/types/client/locales.d.ts +11 -0
- package/lib/types/client/section-controller.d.ts +2 -0
- package/lib/types/config.d.ts +1 -1
- package/lib/types/conversion/context.d.ts +1 -1
- package/lib/types/models-contract.d.ts +11 -1
- package/package.json +23 -22
package/README.en.md
CHANGED
|
@@ -8,7 +8,7 @@ The plugin automatically adds the session headers required by OpenCode Go, reads
|
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
- **Session headers**: Every request includes the Harness User-Agent and `x-opencode-session`. A session keeps
|
|
11
|
+
- **Session headers**: Every request includes the Harness User-Agent and `x-opencode-session`. A session keeps a stable ID to support gateway routing and prompt-cache optimization; actual cache hits depend on the upstream service.
|
|
12
12
|
- **Streaming and history**: Supports streaming output, tool calls, and history replay through pi-ai.
|
|
13
13
|
- **Image input**: Supports models that advertise image capability in the catalog.
|
|
14
14
|
- **Model capacity overrides**: Override the context window and maximum output per model, with blank values inheriting the online catalog.
|
|
@@ -17,7 +17,9 @@ The plugin automatically adds the session headers required by OpenCode Go, reads
|
|
|
17
17
|
|
|
18
18
|
## Installation and usage
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Supports DSH `0.1.5-rc.1` and later, including alpha, rc, and stable releases. Compatibility will be maintained as new host versions are released.
|
|
21
|
+
|
|
22
|
+
Verified versions: `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.6-alpha.1`, `0.1.6-alpha.2`, `0.1.7-alpha.1`, `0.1.7-alpha.2`, `0.1.7-rc.1`, and `0.1.7-rc.2`.
|
|
21
23
|
|
|
22
24
|
### Install from DSH (recommended)
|
|
23
25
|
|
|
@@ -34,7 +36,7 @@ If your DSH version does not have an **Add plugin** entry, use the command-line
|
|
|
34
36
|
### Command-line installation (alternative)
|
|
35
37
|
|
|
36
38
|
```sh
|
|
37
|
-
dsh plugin --profile web add dsh-opencode-go@0.1.
|
|
39
|
+
dsh plugin --profile web add dsh-opencode-go@0.1.14
|
|
38
40
|
```
|
|
39
41
|
|
|
40
42
|
Start or restart `dsh web`, then:
|
|
@@ -48,7 +50,7 @@ Start or restart `dsh web`, then:
|
|
|
48
50
|
Install the plugin into the Headless profile:
|
|
49
51
|
|
|
50
52
|
```sh
|
|
51
|
-
dsh plugin --profile headless add dsh-opencode-go@0.1.
|
|
53
|
+
dsh plugin --profile headless add dsh-opencode-go@0.1.14
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
Save the following as `headless.patch.yml` to select a default model:
|
|
@@ -75,7 +77,7 @@ To build from source and install a local package:
|
|
|
75
77
|
```sh
|
|
76
78
|
npm ci --legacy-peer-deps
|
|
77
79
|
npm pack
|
|
78
|
-
dsh plugin --profile web add ./dsh-opencode-go-0.1.
|
|
80
|
+
dsh plugin --profile web add ./dsh-opencode-go-0.1.14.tgz
|
|
79
81
|
```
|
|
80
82
|
|
|
81
83
|
The development dependencies include real test packages from multiple DSH generations, so installation requires `--legacy-peer-deps`. For Headless, replace `web` with `headless`.
|
|
@@ -134,11 +136,19 @@ Models that are present in the gateway and have an entry using Anthropic Message
|
|
|
134
136
|
|
|
135
137
|
A gateway model ID with no usable protocol or capability configuration is marked “Configuration missing” in Settings, with its switch off and disabled, and is kept out of the conversation picker, so one unconfigured model cannot block the rest of the list. Direct requests report the reason. Refresh after the upstream configuration is corrected. A model ID alone is not enough to reliably infer its transport; new protocols or protocol-specific exceptions may still require adapter changes.
|
|
136
138
|
|
|
139
|
+
If online configuration cannot be loaded, models without usable cached or built-in configuration are marked “Configuration unavailable”, with the configuration source error shown on the page. Check access to `https://models.dev/api.json` from the machine running DSH and review the error details, then refresh. When retrying a retained model list, its previous failure stays visible until a successful refresh clears it; configured models remain usable.
|
|
140
|
+
|
|
137
141
|
A reasoning-capable model without adjustable reasoning levels (for example, `union-alpha`) remains selectable and usable; it simply has no reasoning-strength control.
|
|
138
142
|
|
|
139
143
|
A model that does offer adjustable levels also declares a default effort (`high` when the model offers it, otherwise the highest level it offers) whenever its transport would answer an unset effort with an explicit disable (`deepseek`, `zai`, `qwen`, `qwen-chat-template`). DSH uses that default when no level has been chosen, so leaving the control unset still sends a reasoning level instead of turning thinking off. Transports that leave the choice to the provider declare no default and are unchanged, and an explicitly chosen level always wins.
|
|
140
144
|
|
|
141
|
-
If the online configuration is temporarily unavailable, the plugin prefers a configuration fetched successfully earlier in the process and falls back to pi-ai's built-in metadata. If a gateway catalog refresh fails, ongoing requests and Settings retain the last successful list.
|
|
145
|
+
If the online configuration is temporarily unavailable, the plugin prefers a configuration fetched successfully earlier in the process and falls back to pi-ai's built-in metadata. If a gateway catalog refresh fails, ongoing requests and Settings retain the last successful list. A failure from either source shows a warning, its cause, and the last successful checks of the model listing and model configuration separately. An initial failure without a cache never invents a model list.
|
|
146
|
+
|
|
147
|
+
`refreshMinutes` controls the cache lifetime after a successful refresh. Failed refreshes become eligible for retry on the next read after 5, 10, 20, 40, then at most 60 seconds; there is no background polling when nothing reads the catalog. Explicit Settings refreshes and previously unknown model requests bypass this delay. Cancelling model resolution or generation immediately ends that caller's catalog wait while other callers can continue sharing the same refresh.
|
|
148
|
+
|
|
149
|
+
Verified model configurations can serve generation immediately for five minutes after their cache lifetime expires, while triggering one shared background refresh. This window is measured from each source's last successful check; failed retries do not extend it. Beyond that window, requests wait for the next due refresh, retaining the existing fallback behavior if it fails. Initial loads, unknown models, and manual refreshes wait for results. Listing requests have a 10-second deadline; the larger model metadata download has its own 30-second deadline.
|
|
150
|
+
|
|
151
|
+
Continuation, retries, and restored sessions reuse the Host's durable session ID. Forks and subagent sessions use their own IDs, separate from the parent. Standalone requests without a session ID receive a fresh random identifier each time.
|
|
142
152
|
|
|
143
153
|
## Uninstall
|
|
144
154
|
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
## 功能说明
|
|
10
10
|
|
|
11
|
-
- **会话请求头**:每次请求包含 Harness User-Agent 和 `x-opencode-session
|
|
11
|
+
- **会话请求头**:每次请求包含 Harness User-Agent 和 `x-opencode-session`。同一会话保持稳定 ID,支持网关路由和提示词缓存优化;实际命中率取决于上游服务。
|
|
12
12
|
- **流式与历史**:支持流式输出、工具调用及历史回放,协议请求由 pi-ai 执行。
|
|
13
13
|
- **图片输入**:支持目录中声明图片能力的模型。
|
|
14
14
|
- **模型容量覆盖**:可按模型覆盖上下文窗口和最大输出,空值继承在线目录。
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
|
|
18
18
|
## 安装与使用
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
支持 DSH `0.1.5-rc.1` 及以上版本,包括 alpha、rc 和正式版,并持续跟随宿主版本更新维护兼容性。
|
|
21
|
+
|
|
22
|
+
已验证版本:`0.1.5-rc.1`、`0.1.5-rc.2`、`0.1.6-alpha.1`、`0.1.6-alpha.2`、`0.1.7-alpha.1`、`0.1.7-alpha.2`、`0.1.7-rc.1` 和 `0.1.7-rc.2`。
|
|
22
23
|
|
|
23
24
|
### 在 DSH 中安装(推荐)
|
|
24
25
|
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
### 命令行安装(备选)
|
|
36
37
|
|
|
37
38
|
```sh
|
|
38
|
-
dsh plugin --profile web add dsh-opencode-go@0.1.
|
|
39
|
+
dsh plugin --profile web add dsh-opencode-go@0.1.14
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
安装后启动或重启 `dsh web`,然后:
|
|
@@ -49,7 +50,7 @@ dsh plugin --profile web add dsh-opencode-go@0.1.12
|
|
|
49
50
|
安装到 Headless profile:
|
|
50
51
|
|
|
51
52
|
```sh
|
|
52
|
-
dsh plugin --profile headless add dsh-opencode-go@0.1.
|
|
53
|
+
dsh plugin --profile headless add dsh-opencode-go@0.1.14
|
|
53
54
|
```
|
|
54
55
|
|
|
55
56
|
将以下内容保存为 `headless.patch.yml`,选择默认模型:
|
|
@@ -76,7 +77,7 @@ dsh --profile headless --patch ./headless.patch.yml "你好"
|
|
|
76
77
|
```sh
|
|
77
78
|
npm ci --legacy-peer-deps
|
|
78
79
|
npm pack
|
|
79
|
-
dsh plugin --profile web add ./dsh-opencode-go-0.1.
|
|
80
|
+
dsh plugin --profile web add ./dsh-opencode-go-0.1.14.tgz
|
|
80
81
|
```
|
|
81
82
|
|
|
82
83
|
开发依赖包含多代 DSH 的真实测试包,安装时需要 `--legacy-peer-deps`。Headless 用户将 `web` 换成 `headless`。
|
|
@@ -135,11 +136,19 @@ modelVisibility:
|
|
|
135
136
|
|
|
136
137
|
网关已公布 ID 但尚无有效协议/能力配置的模型会在设置页标注“配置缺失”,开关保持关闭且不可操作,不进入对话模型选择器,避免一个未配置的模型阻断整个列表;直接调用时会说明原因。配置补齐后,刷新列表即可使用。仅凭模型 ID 无法可靠推断调用方式。上游新增全新协议或协议特例时,仍可能需要适配。
|
|
137
138
|
|
|
139
|
+
如果在线配置获取失败,没有可用缓存或内置配置的模型会标注“配置暂不可用”,页面会显示配置源错误。请检查运行 DSH 的设备能否访问 `https://models.dev/api.json`,并查看具体错误后刷新重试。已有模型列表在重试期间保留上次的失败提示,成功后自动清除;已配置的模型继续可用。
|
|
140
|
+
|
|
138
141
|
模型具有推理能力但没有可调节的推理档位时(如 `union-alpha`),仍可正常选择和使用,只是不显示推理强度选项。
|
|
139
142
|
|
|
140
143
|
提供可调节档位的模型,如果其传输协议在未选择档位时会显式关闭思考(`deepseek`、`zai`、`qwen`、`qwen-chat-template`),还会声明一个默认档位(模型支持 `high` 时用 `high`,否则用它提供的最高档位)。用户未选择档位时 DSH 使用该默认值,因此留空也会带上推理档位,而不是关闭思考。由服务商自行决定的协议不声明默认值,行为不变;显式选择的档位始终优先。
|
|
141
144
|
|
|
142
|
-
在线配置暂时不可达时,优先复用本次运行中成功获取的配置,以 pi-ai
|
|
145
|
+
在线配置暂时不可达时,优先复用本次运行中成功获取的配置,以 pi-ai 内置配置作为备用。网关目录刷新失败时,已有请求和设置页均保留上次成功获取的列表。任一来源刷新失败,设置页都会显示警告、失败原因,以及模型名单和模型配置各自上次成功检查的时间。首次读取就失败且没有缓存时,不会凭空生成模型列表。
|
|
146
|
+
|
|
147
|
+
`refreshMinutes` 控制成功刷新后的目录缓存时长。刷新失败后,下一次读取会按 5、10、20、40、60 秒的退避间隔尝试恢复,最长等待间隔为 60 秒;没有读取时不会后台轮询。设置页主动刷新和未知模型的即时发现不受这个间隔限制。取消模型解析或生成时,会立即结束当前调用的目录等待,其他调用仍可继续使用同一次共享刷新。
|
|
148
|
+
|
|
149
|
+
已验证的模型配置在缓存到期后的 5 分钟内可直接用于生成,同时触发一次共享后台刷新。这个窗口按两种来源各自上次成功检查的时间计算,失败重试不会延长它;超过窗口后,请求等待下一次到期刷新,刷新仍失败时保留已有回退行为。首次加载、未知模型和手动刷新会等待结果。模型名单请求限时 10 秒,体积更大的模型配置下载单独限时 30 秒。
|
|
150
|
+
|
|
151
|
+
同一会话继续、重试以及恢复后的请求沿用宿主保存的会话 ID。分叉和子会话使用各自的 ID,不复用父会话标识;没有会话 ID 的独立请求每次生成随机标识。
|
|
143
152
|
|
|
144
153
|
## 卸载
|
|
145
154
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
# Third-party notices
|
|
2
2
|
|
|
3
3
|
The adapter, settings UI, and `src/conversion/{context,stream,replay}.ts` derive from DeepSeek Harness, copyright (c) 2026 DeepSeek, under the MIT license included in LICENSE. The conversion modules are maintained in this package because the published DSH pi-ai package does not export its conversion helpers. Wire protocol implementations remain supplied by `@earendil-works/pi-ai`.
|
|
4
|
+
|
|
5
|
+
The GitHub icon in the settings footer comes from Simple Icons, distributed under CC0 1.0 Universal.
|
package/docs/verification.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Verification
|
|
2
2
|
|
|
3
|
+
## Account usage panel and unsupported-history guards (plugin 0.1.14, 2026-09-26)
|
|
4
|
+
|
|
5
|
+
The OpenCode Go usage panel again shows only the account's rolling, weekly, and monthly limits. Session cache statistics introduced in 0.1.13 have been removed, including their event subscription, component, styles, and translations; token statistics remain available in DSH's own interface. The original account polling, refresh failure handling, and retry behavior are retained. This supersedes the session-cache panel behavior described in the earlier verification entry below.
|
|
6
|
+
|
|
7
|
+
PR #11 adds explicit `UNSUPPORTED_CONTENT` errors for developer-role history, tool-addition/tool-removal blocks, and tools with `deferLoading: true`, on both text-only and image conversion paths. Existing image-role validation and normal tool conversion are retained. These guards are preventive hardening for inputs the adapter cannot represent; no known production source for those inputs was reproduced. Dependencies and the minimum Host version are unchanged.
|
|
8
|
+
|
|
9
|
+
Validation: `npm test` on the combined release changes passed both TypeScript checks, the production build, and **323 tests in 23 files**, including the existing Host compatibility and distributed client fixtures. Additional in-memory checks of PR #11 passed 15 scenarios on both conversion paths, covering tool-removal, empty history with deferred tools, ordinary tools, and `deferLoading: false`. The published UI dependencies still emit their existing missing-source-map warnings. These checks use local fixtures; no installed-profile update or live inference was performed.
|
|
10
|
+
|
|
11
|
+
## Issue #14: distinguish unavailable metadata from missing model configuration (2026-09-26)
|
|
12
|
+
|
|
13
|
+
The existing source diagnostics now drive a specific Settings warning when model configuration fails while the gateway listing succeeds, including an empty successful listing. Unconfigured models show “Configuration unavailable” during a metadata-source failure and explain how to check connectivity from the machine running DSH. A retained model list keeps its warning and source details visible while retrying. Successful metadata recovery clears the warning; models still absent from usable metadata return to “Configuration missing”, and models with newly usable configuration become selectable.
|
|
14
|
+
|
|
15
|
+
Two controller/component regressions failed before the UI changes and pass afterward. Four additional cases exercise the real Host RPC with cold-start HTTP 503, timeout, invalid JSON, and invalid configuration-document responses. They assert source-specific failures, continued availability of built-in models, and successful recovery of DeepSeek V4.1 Flash without restarting or changing credentials. Existing cached-data and legacy-response checks remain in place.
|
|
16
|
+
|
|
17
|
+
`npm test` passed the Host/Client type checks, production build, and **326 tests in 24 files**, including the existing host compatibility and distributed client fixtures. The installed upstream UI packages still emit their existing missing-source-map warnings. This verifies local fixture failures and recovery, not the issue reporter's network environment.
|
|
18
|
+
|
|
19
|
+
## Minimum-host compatibility policy (2026-09-25)
|
|
20
|
+
|
|
21
|
+
The compatibility policy is DSH `0.1.5-rc.1` and later, including alpha, rc, and stable releases, with continued maintenance as the host evolves. `engines.dsh` and all 19 DSH peers now use `>=0.1.5-rc.1`; the Cordis peer uses `>=4.0.2`. The eight existing host fixtures are verified versions, not an exhaustive allowlist. This supersedes the release-specific declaration limits recorded below.
|
|
22
|
+
|
|
23
|
+
DSH's published bundle gate and the market compare host ranges with `includePrerelease: true`. Declaration tests now follow that policy, rejecting hosts below the minimum and accepting later prereleases and stable releases. These checks do not use npm's default peer-range semantics. The published `0.1.7-rc.2` gate also accepted 16 current/future version strings and rejected three below the minimum; future strings check admission policy only. `npm test -- tests/package-compatibility.spec.ts tests/host-compatibility.spec.ts` passed the Host/Client build checks and all **31 tests**, including activation and streaming across the eight existing hosts and Web/Headless bundle admission on rc.1 and rc.2.
|
|
24
|
+
|
|
3
25
|
## Issue #9: usage progress bar painting (plugin 0.1.12, 2026-09-24)
|
|
4
26
|
|
|
5
27
|
The progress elements retained `appearance: auto`, preventing the custom WebKit fill from taking effect ([MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::-webkit-progress-value)). The fix sets `appearance: none`, removes the native border, and gives the progress element the rounded track background. The WebKit track stays transparent to avoid applying the translucent track color twice; the existing WebKit and Firefox fill rules remain green. Percentage values and accessible progress semantics are unchanged.
|
|
@@ -284,3 +306,27 @@ Read-only JSON requests now retry connection reset/socket closure/temporary DNS
|
|
|
284
306
|
Usage failures now carry safe Host diagnostics through the Remote error contract. The UI retains a prior reading only when its opaque account/endpoint source matches the failed request, labels it as old data, and shows its original timestamp and a manual retry. Authentication failures and different/missing source identities clear old data. A source is a Host-generated random identifier, never a credential or credential hash. Polling and manual retries coalesce. These changes improve recovery and diagnostics; they cannot guarantee availability during a network outage.
|
|
285
307
|
|
|
286
308
|
Validation: 289 tests in 22 files and both Host/Client type checks passed, including the published-host compatibility fixtures and the real usage RPC codec.
|
|
309
|
+
|
|
310
|
+
## Catalog failure recovery, cancellation, and source diagnostics (2026-09-25)
|
|
311
|
+
|
|
312
|
+
The catalog previously cached an initial failed listing for the normal successful refresh lifetime (60 minutes by default), ignored caller cancellation during model discovery, and reported fresh Settings data when the gateway listing succeeded but models.dev failed. Five new regressions reproduced these behaviors before the fix: two retry/recovery cases, one metadata diagnostic case, and independent cancellation of model resolution and generation while a second caller shared the pending refresh.
|
|
313
|
+
|
|
314
|
+
Failed catalog refreshes now use demand-driven backoff of 5, 10, 20, 40, then at most 60 seconds. Successful refreshes reset the failure count and restore `refreshMinutes`. Manual discovery bypasses backoff; existing immediate discovery for unknown models is preserved. Repeated failures retain usable models without resurrecting retired entries. No timer polls in the absence of catalog reads.
|
|
315
|
+
|
|
316
|
+
Cancellation ends the individual model-resolution or generation wait before metadata completes. Shared HTTP reads retain their own bounded timeout so other callers still complete and populate the cache. A generation cancelled before discovery starts sends no catalog or inference request. The regression holds the metadata response pending, observes cancellation, then releases it and verifies that the other caller succeeds using the same single listing fetch.
|
|
317
|
+
|
|
318
|
+
Settings RPC responses now optionally carry separate listing/configuration diagnostics and timestamps of their last successful checks, including 304 revalidation. A metadata-only failure marks the catalog stale, retains usable configuration, and shows the failed source and its previous timestamp. The controller/component integration test verifies the warning, enabled model switches, clearing source status after a failed Host RPC, and recovery. Older responses without source fields remain accepted. Malformed source timestamps are rejected by the codec.
|
|
319
|
+
|
|
320
|
+
Validation: `npm test` rebuilt the Host and Client (including both TypeScript checks) and passed **309 tests in 22 files**. The existing missing-source-map warnings from the published Harness UI dependencies remain. `git diff --check` passed. These checks use local fixtures and mock HTTP gateways; no paid inference, installed-profile update, or package publication was performed.
|
|
321
|
+
|
|
322
|
+
## Background catalog refresh, session cache diagnostics, and session identity (2026-09-26)
|
|
323
|
+
|
|
324
|
+
Known, verified models can now use the cached configuration for five minutes after its normal lifetime while one shared refresh runs. A held metadata response regression confirms that generation lookup returns the known configuration before the response arrives, concurrent lookups coalesce, and manual/unknown-model reads still wait. Once the refresh completes, retired models are no longer routed from the old snapshot. Another regression checks that failed attempts do not extend the grace window. The model listing retains its 10-second deadline; metadata downloads have a separate 30-second deadline. The nonblocking lookup and independent-deadline regressions failed before the change and passed afterward.
|
|
325
|
+
|
|
326
|
+
The usage panel derives session cache statistics from the Host's existing loaded event window, with no new RPC or persistent counters. Cached input share is `cacheReadTokens / (inputTokens + cacheReadTokens + cacheWriteTokens)`; output tokens do not contribute. Durable Go responses are counted once by sequence. Transient chunks, attempts without a settled message, other providers, malformed usage, and inherited fork responses are excluded. Missing attribution/accounting and incomplete history are indicated. A zero denominator shows no ratio. Statistics can be reconstructed from restored history and remain available when account-usage reads fail. Hosts without the optional event feed retain the existing account panel.
|
|
327
|
+
|
|
328
|
+
The derived store preserves its snapshot for transient-only updates, recomputes on settlement/history replacement, and subscribes only while the details panel is open. Unit/component tests cover token weighting, cache writes, omitted cache counts, malformed and missing usage, duplicate events, fork boundaries, serialized restoration, history replacement, account failure, switching sessions, and subscription cleanup. Session statistics describe the loaded, settled responses, not all billable gateway attempts or an account-wide cache metric.
|
|
329
|
+
|
|
330
|
+
Lifecycle tests use the real `@deepseek-ai/dsh-session` 0.1.6-alpha.1 Session/SessionStore and a local HTTP gateway. Continuation, a 503 followed by a caller retry, serialized Host header/log restoration, and adapter recreation retain the same `x-opencode-session`. Real forks and subagent-marked child sessions have distinct identities during concurrent calls and keep their own IDs after restoration. These are persistence-boundary tests, not a full installed agent-loop process restart. Existing protocol and published-host compatibility fixtures remain part of the full suite.
|
|
331
|
+
|
|
332
|
+
Validation: `npm test` rebuilt both artifacts and passed **320 tests in 24 files**, including both TypeScript checks. An isolated browser fixture loaded the actual built `lib/client.js` usage slot with local Host-service data. Chinese and English panels displayed the expected 70% ratio and 7,000 / 10,000 input-token counts; a 320px viewport had no horizontal overflow, and account errors did not hide session statistics. Escape closed the dialog; captured browser errors were empty. This validates the shipped client artifact with fixture services, not a live installed Host or upstream cache-hit rate. No paid inference or installed-profile update was required.
|
package/lib/client.js
CHANGED
|
@@ -90,9 +90,27 @@ function parseGoModelCatalog(value) {
|
|
|
90
90
|
return {
|
|
91
91
|
models: parseGoModels(catalog.models),
|
|
92
92
|
stale: catalog.stale,
|
|
93
|
-
...catalog.error === void 0 ? {} : { error: catalog.error }
|
|
93
|
+
...catalog.error === void 0 ? {} : { error: catalog.error },
|
|
94
|
+
...catalog.sources === void 0 ? {} : { sources: parseSources(catalog.sources) }
|
|
94
95
|
};
|
|
95
96
|
}
|
|
97
|
+
function parseSources(value) {
|
|
98
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid OpenCode Go catalog sources");
|
|
99
|
+
const sources = value;
|
|
100
|
+
const parse = (value2) => {
|
|
101
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid OpenCode Go catalog source status");
|
|
102
|
+
const source = value2;
|
|
103
|
+
if (source.updatedAt !== void 0 && (typeof source.updatedAt !== "number" || !Number.isSafeInteger(source.updatedAt) || source.updatedAt < 0 || source.updatedAt > 864e13)) {
|
|
104
|
+
throw new Error("Invalid OpenCode Go catalog source timestamp");
|
|
105
|
+
}
|
|
106
|
+
if (source.error !== void 0 && typeof source.error !== "string") throw new Error("Invalid OpenCode Go catalog source error");
|
|
107
|
+
return {
|
|
108
|
+
...source.updatedAt === void 0 ? {} : { updatedAt: source.updatedAt },
|
|
109
|
+
...source.error === void 0 ? {} : { error: source.error }
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
return { listing: parse(sources.listing), metadata: parse(sources.metadata) };
|
|
113
|
+
}
|
|
96
114
|
var codec = {
|
|
97
115
|
mode: "strict",
|
|
98
116
|
typeSymbol: "dsh-opencode-go#GoModelCatalog",
|
|
@@ -112,16 +130,16 @@ var modelsRemote = {
|
|
|
112
130
|
}]
|
|
113
131
|
};
|
|
114
132
|
|
|
115
|
-
// plugin-css:/Users/tanglei/
|
|
133
|
+
// plugin-css:/Users/tanglei/.codex/worktrees/release-0-1-14/dsh-opencode-go/src/client/Section.module.css
|
|
116
134
|
var id = "dsh-opencode-go/Section.module.css";
|
|
117
135
|
if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id) + "]")) {
|
|
118
136
|
const style = document.createElement("style");
|
|
119
137
|
style.dataset.plugin = "dsh-opencode-go";
|
|
120
138
|
style.dataset.pluginCss = id;
|
|
121
|
-
style.textContent = '.
|
|
139
|
+
style.textContent = '.LKs06q_page{flex-direction:column;min-width:0;height:100%;min-height:0;display:flex;container-type:size}.LKs06q_pageBody{scrollbar-gutter:stable;flex:1;min-height:0;padding-right:4px;overflow-y:auto}.LKs06q_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.LKs06q_field+.LKs06q_field{border-top:.5px solid var(--dsw-alias-border-l2)}.LKs06q_head{align-items:center;gap:8px;display:flex}.LKs06q_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.LKs06q_badges{align-items:center;gap:8px;display:inline-flex}.LKs06q_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.LKs06q_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.LKs06q_reset:disabled{cursor:default}.LKs06q_reset:focus-visible,.LKs06q_disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.LKs06q_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.LKs06q_input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px;border-color:var(--dsw-alias-brand-primary)}.LKs06q_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.LKs06q_inputInvalid{border-color:var(--dsw-alias-label-error);}.LKs06q_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.LKs06q_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.LKs06q_intro{color:var(--dsw-alias-label-secondary);margin:0 0 8px;font-size:13px;line-height:1.5}.LKs06q_disclosure{font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;margin:0;padding:0;display:flex}.LKs06q_disclosure:hover .LKs06q_label{color:var(--dsw-alias-brand-primary)}.LKs06q_chevron,.LKs06q_chevronOpen{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none}.LKs06q_chevron{transform:rotate(-90deg)}.LKs06q_advanced{flex-direction:column;gap:4px;padding:4px 0 0 22px;display:flex}.LKs06q_advanced .LKs06q_field+.LKs06q_field{border-top:none}.LKs06q_chips{flex-wrap:wrap;gap:6px;display:flex}.LKs06q_limitsEditor{flex-direction:column;gap:10px;padding-top:4px;display:flex}.LKs06q_limitsToolbar{grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:6px 10px;display:grid}.LKs06q_limitsFilterLabel{color:var(--dsw-alias-label-secondary);grid-column:1/-1;font-size:12px;font-weight:500;line-height:1.5}.LKs06q_limitsSummary{color:var(--dsw-alias-label-tertiary);white-space:nowrap;padding-bottom:8px;font-size:12px;line-height:1.5}.LKs06q_limitsId,.LKs06q_limitsModelId{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.LKs06q_limitsId{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.4}.LKs06q_limitsModelId{color:var(--dsw-alias-label-tertiary);margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1.4}.LKs06q_visuallyHidden{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@media (width<=560px){.LKs06q_limitsToolbar{grid-template-columns:1fr}.LKs06q_limitsSummary{padding-bottom:0}}.LKs06q_actions{border-top:.5px solid var(--dsw-alias-border-l2);flex-wrap:wrap;flex:none;align-items:center;gap:8px;padding:12px 0;display:flex}.LKs06q_starLink{max-width:100%;color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;gap:7px;margin-left:auto;padding:6px 0;font-size:12px;line-height:1.5;text-decoration:none;display:inline-flex}.LKs06q_starLink svg{flex:none}.LKs06q_starLink:hover{color:var(--dsw-alias-brand-primary)}.LKs06q_starLink:hover span{text-underline-offset:3px;text-decoration:underline}.LKs06q_starLink:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.LKs06q_failedNote{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.LKs06q_pageTop{align-items:flex-start;gap:16px;display:flex}.LKs06q_pageTop .LKs06q_intro{flex:1}.LKs06q_advancedTrigger{flex-shrink:0;margin-left:auto;}.LKs06q_advancedTrigger .LKs06q_label{color:var(--dsw-alias-label-tertiary);font-size:12px}.LKs06q_filters{flex-wrap:wrap;gap:6px;display:flex}.LKs06q_filter{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;padding:6px 9px;font-size:12px}.LKs06q_filter[aria-pressed=true]{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3)}.LKs06q_filter span{color:var(--dsw-alias-label-tertiary);margin-left:4px}.LKs06q_modelLayout{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;grid-template-columns:minmax(0,43fr) minmax(0,57fr);height:clamp(240px,100cqh - 460px,480px);display:grid;overflow:hidden}.LKs06q_modelList{border-right:.5px solid var(--dsw-alias-border-l2);min-height:0;padding:6px;overflow-y:auto}.LKs06q_modelRow{align-items:center;gap:8px;padding-right:6px;display:flex}.LKs06q_modelRow>[role=switch]{flex-shrink:0}.LKs06q_modelChoice{text-align:left;min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:1;padding:12px 9px;display:block}.LKs06q_modelChoice[aria-pressed=true],.LKs06q_modelChoice:hover{background:var(--dsw-alias-bg-layer-3)}.LKs06q_modelName{overflow-wrap:anywhere;flex-wrap:wrap;align-items:center;gap:5px;font-size:13px;display:flex}.LKs06q_modelDetails{flex-direction:column;align-items:stretch;gap:10px;min-width:0;padding:18px;display:flex;overflow-y:auto}.LKs06q_modelDetails h3{overflow-wrap:anywhere;margin:0;font-size:16px}.LKs06q_modelHeading{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.LKs06q_modelDetails .LKs06q_field{border:0;padding:8px 0}.LKs06q_modelDetails .LKs06q_input{box-sizing:border-box;width:100%;min-width:0}.LKs06q_modelDetails .LKs06q_reset{align-self:flex-start}.LKs06q_newBadge{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l4);border-radius:4px;padding:2px 4px;font-size:9px}.LKs06q_releaseDate{color:var(--dsw-alias-label-tertiary);margin-top:4px;font-size:10px;display:block}.LKs06q_modelChoice:focus-visible,.LKs06q_filter:focus-visible,.LKs06q_advancedTrigger:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}@container (width<=440px){.LKs06q_pageTop{flex-wrap:wrap}.LKs06q_modelLayout{grid-template-columns:minmax(0,1fr);height:auto}.LKs06q_modelList{border-right:0;border-bottom:.5px solid var(--dsw-alias-border-l2);max-height:240px}.LKs06q_modelDetails{max-height:460px}}.LKs06q_keySection{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px 0}.LKs06q_keySection summary{cursor:pointer;list-style:none}.LKs06q_keySection summary:after{content:"\u2304";color:var(--dsw-alias-label-tertiary)}.LKs06q_keySection summary::-webkit-details-marker{display:none}.LKs06q_keySection>.LKs06q_input{box-sizing:border-box;width:100%;margin:10px 0 6px}.LKs06q_keySection summary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}';
|
|
122
140
|
document.head.appendChild(style);
|
|
123
141
|
}
|
|
124
|
-
var Section_default = { "
|
|
142
|
+
var Section_default = { "field": "LKs06q_field", "head": "LKs06q_head", "chevron": "LKs06q_chevron", "label": "LKs06q_label", "inputInvalid": "LKs06q_inputInvalid LKs06q_input", "chevronOpen": "LKs06q_chevronOpen", "chips": "LKs06q_chips", "badges": "LKs06q_badges", "actions": "LKs06q_actions", "modelRow": "LKs06q_modelRow", "pageBody": "LKs06q_pageBody", "visuallyHidden": "LKs06q_visuallyHidden", "advancedTrigger": "LKs06q_advancedTrigger LKs06q_disclosure", "pageTop": "LKs06q_pageTop", "filters": "LKs06q_filters", "newBadge": "LKs06q_newBadge", "limitsModelId": "LKs06q_limitsModelId", "releaseDate": "LKs06q_releaseDate", "hint": "LKs06q_hint", "limitsToolbar": "LKs06q_limitsToolbar", "input": "LKs06q_input", "limitsId": "LKs06q_limitsId", "starLink": "LKs06q_starLink", "modelName": "LKs06q_modelName", "invalid": "LKs06q_invalid", "limitsFilterLabel": "LKs06q_limitsFilterLabel", "limitsSummary": "LKs06q_limitsSummary", "modelLayout": "LKs06q_modelLayout", "modelDetails": "LKs06q_modelDetails", "modelHeading": "LKs06q_modelHeading", "keySection": "LKs06q_keySection", "advanced": "LKs06q_advanced", "failedNote": "LKs06q_failedNote", "filter": "LKs06q_filter", "reset": "LKs06q_reset", "intro": "LKs06q_intro", "limitsEditor": "LKs06q_limitsEditor", "modelList": "LKs06q_modelList", "modelChoice": "LKs06q_modelChoice", "page": "LKs06q_page", "disclosure": "LKs06q_disclosure" };
|
|
125
143
|
|
|
126
144
|
// src/client/ModelEditor.tsx
|
|
127
145
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -146,6 +164,7 @@ function ModelEditor({ models, draft, modelVisibility, t, locale, disabled, visi
|
|
|
146
164
|
const entries = all.filter((model2) => `${model2.name ?? ""} ${model2.id}`.toLocaleLowerCase().includes(normalized) && (filter === "all" || filter === "new" && isNewModel(model2, now) || filter === "custom" && hasCapacityOverride(draft[model2.id]) || filter === "deprecated" && model2.deprecated));
|
|
147
165
|
const model = entries.find((entry) => entry.id === selected) ?? entries[0];
|
|
148
166
|
const visibilityDisabled = disabled || visibilitySaving;
|
|
167
|
+
const metadataUnavailable = models.status === "ready" && models.sources?.metadata.error !== void 0;
|
|
149
168
|
const customized = all.filter((entry) => hasCapacityOverride(draft[entry.id])).length;
|
|
150
169
|
const filters = [
|
|
151
170
|
["all", "filterAll", all.length],
|
|
@@ -158,7 +177,7 @@ function ModelEditor({ models, draft, modelVisibility, t, locale, disabled, visi
|
|
|
158
177
|
onEdit({ ...draft, [id3]: { ...current, [field]: value ?? null } });
|
|
159
178
|
};
|
|
160
179
|
const badges = (entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
161
|
-
entry.configurationMissing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "warning", children: t("configurationMissing") }) : null,
|
|
180
|
+
entry.configurationMissing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "warning", children: t(metadataUnavailable ? "configurationUnavailable" : "configurationMissing") }) : null,
|
|
162
181
|
isNewModel(entry, now) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Section_default.newBadge, title: t("newHint"), children: t("newBadge") }) : null,
|
|
163
182
|
entry.deprecated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "warning", children: t("deprecatedBadge") }) : null
|
|
164
183
|
] });
|
|
@@ -236,7 +255,7 @@ function ModelEditor({ models, draft, modelVisibility, t, locale, disabled, visi
|
|
|
236
255
|
badges(model)
|
|
237
256
|
] }),
|
|
238
257
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: Section_default.limitsModelId, translate: "no", children: model.id }),
|
|
239
|
-
model.configurationMissing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("configurationMissingHint") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
258
|
+
model.configurationMissing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t(metadataUnavailable ? "configurationUnavailableHint" : "configurationMissingHint") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
240
259
|
model.deprecated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: Section_default.hint, children: t("deprecatedHint") }) : null,
|
|
241
260
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Capacity, { model, field: "contextWindow", limit: draft[model.id], t, locale, disabled, onChange: write }),
|
|
242
261
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Capacity, { model, field: "maxTokens", limit: draft[model.id], t, locale, disabled, onChange: write }),
|
|
@@ -351,19 +370,37 @@ function ValueField(props) {
|
|
|
351
370
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { id: hintId, className: props.field.invalid ? Section_default.invalid : Section_default.hint, children: props.field.invalid ? props.invalidLabel : props.hint })
|
|
352
371
|
] });
|
|
353
372
|
}
|
|
354
|
-
function ModelsBody({ models, t }) {
|
|
373
|
+
function ModelsBody({ models, t, locale }) {
|
|
374
|
+
const sources = models.status === "ready" || models.status === "failed" ? models.sources : void 0;
|
|
375
|
+
const metadataOnlyFailure = sources?.metadata.error !== void 0 && sources.listing.error === void 0;
|
|
376
|
+
const sourceStatus = sources ? ["listing", "metadata"].map((source) => {
|
|
377
|
+
const status = sources[source];
|
|
378
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: Section_default.hint, children: [
|
|
379
|
+
t(source === "listing" ? "modelsListingSource" : "modelsMetadataSource"),
|
|
380
|
+
": ",
|
|
381
|
+
" ",
|
|
382
|
+
status.error ? t("modelsSourceFailed") : t("modelsSourceCurrent"),
|
|
383
|
+
". ",
|
|
384
|
+
" ",
|
|
385
|
+
status.updatedAt === void 0 ? t("modelsSourceNeverFetched") : t("modelsSourceUpdated", { time: new Date(status.updatedAt).toLocaleString(locale) })
|
|
386
|
+
] }, source);
|
|
387
|
+
}) : null;
|
|
355
388
|
if (models.status === "failed") {
|
|
356
389
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
357
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.failedNote, role: "alert", children: t("modelsFailed") }),
|
|
358
|
-
models.message ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: models.message }) : null
|
|
390
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.failedNote, role: "alert", children: t(metadataOnlyFailure ? "modelsMetadataFailed" : "modelsFailed") }),
|
|
391
|
+
models.message ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: models.message }) : null,
|
|
392
|
+
sourceStatus
|
|
359
393
|
] });
|
|
360
394
|
}
|
|
361
395
|
if (models.status !== "ready") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, role: "status", "aria-live": "polite", children: t("modelsLoading") });
|
|
362
|
-
|
|
396
|
+
const progress = models.refreshing ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, role: "status", children: t("modelsRefreshing") }) : null;
|
|
363
397
|
if (models.stale) return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
364
|
-
|
|
365
|
-
|
|
398
|
+
progress,
|
|
399
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.failedNote, role: "alert", children: t(metadataOnlyFailure ? "modelsMetadataFailed" : "modelsStale") }),
|
|
400
|
+
models.message ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: models.message }) : null,
|
|
401
|
+
sourceStatus
|
|
366
402
|
] });
|
|
403
|
+
if (models.refreshing) return progress;
|
|
367
404
|
if (models.count === 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.hint, children: t("modelsEmpty") });
|
|
368
405
|
return null;
|
|
369
406
|
}
|
|
@@ -601,7 +638,7 @@ function Loaded(props) {
|
|
|
601
638
|
)
|
|
602
639
|
] })
|
|
603
640
|
] }),
|
|
604
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ModelsBody, { models: state.models, t })
|
|
641
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ModelsBody, { models: state.models, t, locale: props.locale })
|
|
605
642
|
] }),
|
|
606
643
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.field, children: [
|
|
607
644
|
state.pickerFailed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.failedNote, role: "alert", children: t("pickerFailed") }) : null,
|
|
@@ -627,6 +664,20 @@ function Loaded(props) {
|
|
|
627
664
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: Section_default.actions, children: [
|
|
628
665
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", size: "md", disabled: disabled || !state.dirty || state.invalid || state.saving || state.pickerSaving, onClick: props.save, children: state.saving ? t("saving") : t("save") }),
|
|
629
666
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "outline", size: "md", disabled: disabled || !state.dirty || state.saving, onClick: props.discard, children: t("discard") }),
|
|
667
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
668
|
+
"a",
|
|
669
|
+
{
|
|
670
|
+
className: Section_default.starLink,
|
|
671
|
+
href: "https://github.com/Duskriver/dsh-opencode-go",
|
|
672
|
+
target: "_blank",
|
|
673
|
+
rel: "noopener noreferrer",
|
|
674
|
+
title: t("starProjectTitle"),
|
|
675
|
+
children: [
|
|
676
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 24 24", width: "18", height: "18", fill: "currentColor", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" }) }),
|
|
677
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("starProject") })
|
|
678
|
+
]
|
|
679
|
+
}
|
|
680
|
+
),
|
|
630
681
|
state.failed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: Section_default.failedNote, children: t("savedFailed") }) : null
|
|
631
682
|
] })
|
|
632
683
|
] });
|
|
@@ -1047,13 +1098,17 @@ var OpencodeGoSectionController = class {
|
|
|
1047
1098
|
this.models = previous ? { ...previous, refreshing: true } : { status: "loading" };
|
|
1048
1099
|
this.store.set(this.projection());
|
|
1049
1100
|
const failed = (message) => {
|
|
1050
|
-
this.models = previous ? { ...previous, refreshing: false, stale: true, message } : { status: "failed", message };
|
|
1101
|
+
this.models = previous ? { ...previous, refreshing: false, stale: true, message, sources: void 0 } : { status: "failed", message };
|
|
1051
1102
|
};
|
|
1052
1103
|
void this.readModels().then((response) => {
|
|
1053
1104
|
if (request !== this.modelsRequest) return;
|
|
1054
1105
|
if (!response.ok) failed(response.error.message);
|
|
1055
1106
|
else if (response.value.stale && response.value.models.length === 0) {
|
|
1056
|
-
this.models = {
|
|
1107
|
+
this.models = {
|
|
1108
|
+
status: "failed",
|
|
1109
|
+
...response.value.error ? { message: response.value.error } : {},
|
|
1110
|
+
...response.value.sources ? { sources: response.value.sources } : {}
|
|
1111
|
+
};
|
|
1057
1112
|
} else {
|
|
1058
1113
|
this.models = {
|
|
1059
1114
|
status: "ready",
|
|
@@ -1061,6 +1116,7 @@ var OpencodeGoSectionController = class {
|
|
|
1061
1116
|
preview: response.value.models.map((model) => model.name ?? model.id),
|
|
1062
1117
|
entries: response.value.models,
|
|
1063
1118
|
stale: response.value.stale,
|
|
1119
|
+
...response.value.sources ? { sources: response.value.sources } : {},
|
|
1064
1120
|
...response.value.error ? { message: response.value.error } : {}
|
|
1065
1121
|
};
|
|
1066
1122
|
}
|
|
@@ -1193,6 +1249,8 @@ var en = {
|
|
|
1193
1249
|
nav: "OpenCode Go",
|
|
1194
1250
|
title: "OpenCode Go",
|
|
1195
1251
|
intro: "Choose the OpenCode Go models shown in conversations and adjust their capacities.",
|
|
1252
|
+
starProject: "Enjoying the plugin? Star it on GitHub",
|
|
1253
|
+
starProjectTitle: "Open the GitHub project in a new tab",
|
|
1196
1254
|
enabledLabel: "Enable OpenCode Go",
|
|
1197
1255
|
enabledHint: "While this is on, OpenCode Go appears in every model picker and can serve requests. Turning it off withdraws the provider and its models immediately; this page stays reachable so you can turn it back on.",
|
|
1198
1256
|
enabledOff: "OpenCode Go is off. Its models are withdrawn from every picker and no request reaches the gateway. Turn the switch back on to use it again.",
|
|
@@ -1206,7 +1264,14 @@ var en = {
|
|
|
1206
1264
|
modelsRefresh: "Refresh",
|
|
1207
1265
|
modelsLoading: "Reading the gateway model list\u2026",
|
|
1208
1266
|
modelsFailed: "Could not read the gateway model list.",
|
|
1209
|
-
modelsStale: "
|
|
1267
|
+
modelsStale: "Some model data could not be refreshed. Available cached or built-in data is still in use, including in conversation pickers.",
|
|
1268
|
+
modelsMetadataFailed: "Could not load model configuration from models.dev. Models with usable cached or built-in configuration remain available. See the error details below, check network access from the machine running DSH, then refresh.",
|
|
1269
|
+
modelsListingSource: "Model availability",
|
|
1270
|
+
modelsMetadataSource: "Model configuration",
|
|
1271
|
+
modelsSourceFailed: "Refresh failed",
|
|
1272
|
+
modelsSourceCurrent: "Up to date",
|
|
1273
|
+
modelsSourceNeverFetched: "No successful fetch yet",
|
|
1274
|
+
modelsSourceUpdated: "Last successful check: {time}",
|
|
1210
1275
|
modelsRefreshing: "Refreshing\u2026 Keeping the last fetched models visible.",
|
|
1211
1276
|
modelsEmpty: "The gateway currently serves no model this adapter can route.",
|
|
1212
1277
|
modelsCount: "{count} models",
|
|
@@ -1223,7 +1288,9 @@ var en = {
|
|
|
1223
1288
|
visibilityHint: "Only enabled models appear in conversation pickers. Changes apply immediately. Deprecated models are off by default and can be enabled individually.",
|
|
1224
1289
|
modelVisibleLabel: "Show {name} in conversations",
|
|
1225
1290
|
configurationMissing: "Configuration missing",
|
|
1226
|
-
configurationMissingHint: "The gateway lists this model, but
|
|
1291
|
+
configurationMissingHint: "The gateway lists this model, but no usable OpenCode Go configuration is available. Once the upstream configuration is supplied or corrected, refresh the list to enable it.",
|
|
1292
|
+
configurationUnavailable: "Configuration unavailable",
|
|
1293
|
+
configurationUnavailableHint: "The model configuration could not be loaded, and no usable cached or built-in configuration is available for this model. Check network access to models.dev from the machine running DSH and the error details above, then refresh.",
|
|
1227
1294
|
pickerFailed: "Could not update model visibility. Please retry.",
|
|
1228
1295
|
deprecatedBadge: "Deprecated",
|
|
1229
1296
|
deprecatedHint: "Marked deprecated by models.dev, but still provided by this gateway.",
|
|
@@ -1290,6 +1357,8 @@ var zh = {
|
|
|
1290
1357
|
nav: "OpenCode Go",
|
|
1291
1358
|
title: "OpenCode Go",
|
|
1292
1359
|
intro: "\u9009\u62E9\u4F1A\u8BDD\u4E2D\u663E\u793A\u7684 OpenCode Go \u6A21\u578B\uFF0C\u5E76\u8C03\u6574\u5176\u5BB9\u91CF\u3002",
|
|
1360
|
+
starProject: "\u559C\u6B22\u8FD9\u4E2A\u63D2\u4EF6\uFF1F\u53BB GitHub \u70B9\u4E2A Star",
|
|
1361
|
+
starProjectTitle: "\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00 GitHub \u9879\u76EE",
|
|
1293
1362
|
enabledLabel: "\u542F\u7528 OpenCode Go",
|
|
1294
1363
|
enabledHint: "\u5F00\u542F\u65F6\uFF0COpenCode Go \u4F1A\u51FA\u73B0\u5728\u6240\u6709\u6A21\u578B\u9009\u62E9\u5668\u4E2D\u5E76\u53EF\u5904\u7406\u8BF7\u6C42\u3002\u5173\u95ED\u540E\u5C06\u7ACB\u5373\u64A4\u4E0B\u8BE5\u63D0\u4F9B\u65B9\u53CA\u5176\u6A21\u578B\uFF1B\u672C\u9875\u4ECD\u53EF\u8BBF\u95EE\uFF0C\u65B9\u4FBF\u4F60\u968F\u65F6\u91CD\u65B0\u5F00\u542F\u3002",
|
|
1295
1364
|
enabledOff: "OpenCode Go \u5DF2\u5173\u95ED\u3002\u5176\u6A21\u578B\u5DF2\u4ECE\u6240\u6709\u9009\u62E9\u5668\u4E2D\u64A4\u4E0B\uFF0C\u4E5F\u4E0D\u4F1A\u6709\u8BF7\u6C42\u53D1\u5F80\u7F51\u5173\u3002\u9700\u8981\u65F6\u628A\u5F00\u5173\u91CD\u65B0\u6253\u5F00\u5373\u53EF\u3002",
|
|
@@ -1303,7 +1372,14 @@ var zh = {
|
|
|
1303
1372
|
modelsRefresh: "\u5237\u65B0",
|
|
1304
1373
|
modelsLoading: "\u6B63\u5728\u8BFB\u53D6\u7F51\u5173\u6A21\u578B\u5217\u8868\u2026",
|
|
1305
1374
|
modelsFailed: "\u672A\u80FD\u8BFB\u53D6\u7F51\u5173\u6A21\u578B\u5217\u8868\u3002",
|
|
1306
|
-
modelsStale: "\u5237\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\
|
|
1375
|
+
modelsStale: "\u90E8\u5206\u6A21\u578B\u6570\u636E\u5237\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\u7EE7\u7EED\u4F7F\u7528\u53EF\u7528\u7684\u7F13\u5B58\u6216\u5185\u7F6E\u914D\u7F6E\uFF1B\u4F1A\u8BDD\u9009\u62E9\u5668\u4E5F\u4F7F\u7528\u8FD9\u4EFD\u6570\u636E\u3002",
|
|
1376
|
+
modelsMetadataFailed: "\u672A\u80FD\u4ECE models.dev \u83B7\u53D6\u6709\u6548\u7684\u6A21\u578B\u914D\u7F6E\u3002\u5DF2\u6709\u53EF\u7528\u7F13\u5B58\u6216\u5185\u7F6E\u914D\u7F6E\u7684\u6A21\u578B\u4ECD\u53EF\u4F7F\u7528\u3002\u8BF7\u67E5\u770B\u4E0B\u65B9\u9519\u8BEF\u8BE6\u60C5\uFF0C\u68C0\u67E5\u8FD0\u884C DSH \u7684\u8BBE\u5907\u7684\u7F51\u7EDC\u8FDE\u63A5\uFF0C\u7136\u540E\u5237\u65B0\u91CD\u8BD5\u3002",
|
|
1377
|
+
modelsListingSource: "\u6A21\u578B\u540D\u5355",
|
|
1378
|
+
modelsMetadataSource: "\u6A21\u578B\u914D\u7F6E",
|
|
1379
|
+
modelsSourceFailed: "\u5237\u65B0\u5931\u8D25",
|
|
1380
|
+
modelsSourceCurrent: "\u5DF2\u66F4\u65B0",
|
|
1381
|
+
modelsSourceNeverFetched: "\u5C1A\u672A\u6210\u529F\u83B7\u53D6",
|
|
1382
|
+
modelsSourceUpdated: "\u4E0A\u6B21\u6210\u529F\u68C0\u67E5\uFF1A{time}",
|
|
1307
1383
|
modelsRefreshing: "\u6B63\u5728\u5237\u65B0\uFF0C\u6682\u65F6\u4FDD\u7559\u4E0A\u6B21\u83B7\u53D6\u7684\u6A21\u578B\u3002",
|
|
1308
1384
|
modelsEmpty: "\u7F51\u5173\u5F53\u524D\u6CA1\u6709\u672C\u9002\u914D\u5668\u53EF\u8DEF\u7531\u7684\u6A21\u578B\u3002",
|
|
1309
1385
|
modelsCount: "{count} \u4E2A\u6A21\u578B",
|
|
@@ -1320,7 +1396,9 @@ var zh = {
|
|
|
1320
1396
|
visibilityHint: "\u5F00\u542F\u7684\u6A21\u578B\u624D\u4F1A\u663E\u793A\u5728\u4F1A\u8BDD\u9009\u62E9\u5668\u4E2D\uFF0C\u4FEE\u6539\u7ACB\u5373\u751F\u6548\u3002\u8FC7\u65F6\u6A21\u578B\u9ED8\u8BA4\u5173\u95ED\uFF0C\u53EF\u5355\u72EC\u5F00\u542F\u3002",
|
|
1321
1397
|
modelVisibleLabel: "\u5728\u4F1A\u8BDD\u4E2D\u663E\u793A {name}",
|
|
1322
1398
|
configurationMissing: "\u914D\u7F6E\u7F3A\u5931",
|
|
1323
|
-
configurationMissingHint: "\u7F51\u5173\u5DF2\u5217\u51FA\u6B64\u6A21\u578B\uFF0C\u4F46\
|
|
1399
|
+
configurationMissingHint: "\u7F51\u5173\u5DF2\u5217\u51FA\u6B64\u6A21\u578B\uFF0C\u4F46\u6682\u672A\u83B7\u5F97\u53EF\u7528\u7684 OpenCode Go \u914D\u7F6E\u3002\u4E0A\u6E38\u8865\u9F50\u6216\u4FEE\u6B63\u914D\u7F6E\u540E\uFF0C\u5237\u65B0\u5217\u8868\u5373\u53EF\u5F00\u542F\u3002",
|
|
1400
|
+
configurationUnavailable: "\u914D\u7F6E\u6682\u4E0D\u53EF\u7528",
|
|
1401
|
+
configurationUnavailableHint: "\u6682\u672A\u80FD\u83B7\u53D6\u6709\u6548\u7684\u6A21\u578B\u914D\u7F6E\uFF0C\u4E14\u6CA1\u6709\u6B64\u6A21\u578B\u7684\u53EF\u7528\u7F13\u5B58\u6216\u5185\u7F6E\u914D\u7F6E\u3002\u8BF7\u67E5\u770B\u4E0A\u65B9\u9519\u8BEF\u8BE6\u60C5\uFF0C\u68C0\u67E5\u8FD0\u884C DSH \u7684\u8BBE\u5907\u80FD\u5426\u8BBF\u95EE models.dev\uFF0C\u7136\u540E\u5237\u65B0\u91CD\u8BD5\u3002",
|
|
1324
1402
|
pickerFailed: "\u6A21\u578B\u663E\u793A\u8BBE\u7F6E\u672A\u66F4\u65B0\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
1325
1403
|
deprecatedBadge: "\u5DF2\u5F03\u7528",
|
|
1326
1404
|
deprecatedHint: "models.dev \u6807\u8BB0\u4E3A\u5DF2\u5F03\u7528\uFF0C\u5F53\u524D\u7F51\u5173\u4ECD\u63D0\u4F9B\u6B64\u6A21\u578B\u3002",
|
|
@@ -1415,16 +1493,16 @@ var goRemote = {
|
|
|
1415
1493
|
// src/client/UsagePill.tsx
|
|
1416
1494
|
var import_react3 = require("react");
|
|
1417
1495
|
|
|
1418
|
-
// plugin-css:/Users/tanglei/
|
|
1496
|
+
// plugin-css:/Users/tanglei/.codex/worktrees/release-0-1-14/dsh-opencode-go/src/client/UsagePill.module.css
|
|
1419
1497
|
var id2 = "dsh-opencode-go/UsagePill.module.css";
|
|
1420
1498
|
if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]")) {
|
|
1421
1499
|
const style = document.createElement("style");
|
|
1422
1500
|
style.dataset.plugin = "dsh-opencode-go";
|
|
1423
1501
|
style.dataset.pluginCss = id2;
|
|
1424
|
-
style.textContent = ".
|
|
1502
|
+
style.textContent = ".XD2TAq_root{min-width:0;display:inline-flex;position:relative}.XD2TAq_trigger{color:inherit;opacity:.75;font:inherit;cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:6px;padding:4px 6px;font-size:12px}.XD2TAq_trigger:hover,.XD2TAq_trigger:focus-visible{opacity:1;background:color-mix(in srgb, currentColor 8%, transparent)}.XD2TAq_panel{z-index:100;box-sizing:border-box;border:1px solid color-mix(in srgb, currentColor 18%, transparent);width:280px;max-width:calc(100vw - 32px);max-height:70vh;color:var(--dsw-alias-label-primary,CanvasText);background:var(--dsw-specific-menu,Canvas);border-radius:12px;padding:16px;font-size:12px;position:absolute;bottom:calc(100% + 12px);right:0;overflow-y:auto;box-shadow:0 8px 30px #0002}.XD2TAq_hint{opacity:.65;font-size:11px;line-height:1.6}.XD2TAq_warning{background:color-mix(in srgb, currentColor 6%, transparent);overflow-wrap:anywhere;border-radius:6px;margin:10px 0;padding:10px;line-height:1.5}.XD2TAq_warning p{margin:5px 0 0}.XD2TAq_retry{border:1px solid color-mix(in srgb, currentColor 25%, transparent);color:inherit;font:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:5px 9px}.XD2TAq_retry:disabled{opacity:.6;cursor:default}.XD2TAq_retry:focus-visible{outline-offset:2px;outline:2px solid}.XD2TAq_window{margin-top:14px}.XD2TAq_row{justify-content:space-between;margin-bottom:5px;display:flex}.XD2TAq_window progress{appearance:none;background:color-mix(in srgb, currentColor 15%, transparent);border:0;border-radius:4px;width:100%;height:5px;margin-bottom:5px;display:block}.XD2TAq_window progress::-webkit-progress-bar{background:0 0;border-radius:4px}.XD2TAq_window progress::-webkit-progress-value{background:#39a878;border-radius:4px}.XD2TAq_window progress::-moz-progress-bar{background:#39a878;border-radius:4px}";
|
|
1425
1503
|
document.head.appendChild(style);
|
|
1426
1504
|
}
|
|
1427
|
-
var UsagePill_default = { "
|
|
1505
|
+
var UsagePill_default = { "root": "XD2TAq_root", "hint": "XD2TAq_hint", "window": "XD2TAq_window", "trigger": "XD2TAq_trigger", "panel": "XD2TAq_panel", "warning": "XD2TAq_warning", "row": "XD2TAq_row", "retry": "XD2TAq_retry" };
|
|
1428
1506
|
|
|
1429
1507
|
// src/client/UsagePill.tsx
|
|
1430
1508
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
package/lib/index.js
CHANGED
|
@@ -312,8 +312,12 @@ function toolMessage(message) {
|
|
|
312
312
|
if (message.role !== "tool") return void 0;
|
|
313
313
|
return message;
|
|
314
314
|
}
|
|
315
|
-
function
|
|
315
|
+
function assertSupportedHistory(messages) {
|
|
316
316
|
for (const message of messages) {
|
|
317
|
+
if (message.role === "developer") throw new LlmError3("Developer messages are not supported yet", "UNSUPPORTED_CONTENT");
|
|
318
|
+
if (message.content.some((block) => block.type === "tool-addition" || block.type === "tool-removal")) {
|
|
319
|
+
throw new LlmError3("Tool-change blocks require developer role", "UNSUPPORTED_CONTENT");
|
|
320
|
+
}
|
|
317
321
|
if (message.role !== "user" && !toolMessage(message) && contentHasImage(message.content)) {
|
|
318
322
|
throw new LlmError3(
|
|
319
323
|
`pi-ai cannot represent an image in an in-history ${message.role} message`,
|
|
@@ -382,6 +386,9 @@ async function prepareRequestImages(messages, attachments, budget, signal) {
|
|
|
382
386
|
return versions;
|
|
383
387
|
}
|
|
384
388
|
function toolsOf(options) {
|
|
389
|
+
if (options.tools?.some((tool) => tool.deferLoading === true)) {
|
|
390
|
+
throw new LlmError3("Deferred tool loading is not supported yet", "UNSUPPORTED_CONTENT");
|
|
391
|
+
}
|
|
385
392
|
return options.tools?.map((tool) => ({
|
|
386
393
|
name: tool.name,
|
|
387
394
|
description: tool.description,
|
|
@@ -413,7 +420,7 @@ function appendAssistant(message, messages, toolNames, onReplayDegrade) {
|
|
|
413
420
|
messages.push(assistant);
|
|
414
421
|
}
|
|
415
422
|
function textOnlyContext(options, onReplayDegrade) {
|
|
416
|
-
|
|
423
|
+
assertSupportedHistory(options.messages);
|
|
417
424
|
const split = splitSystemPrompt(options);
|
|
418
425
|
const toolNames = /* @__PURE__ */ new Map();
|
|
419
426
|
const messages = [];
|
|
@@ -476,7 +483,7 @@ async function toPiContextWithImages(options, images, onReplayDegrade) {
|
|
|
476
483
|
maxPixels: DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET,
|
|
477
484
|
maxBytes: DEFAULT_REQUEST_IMAGE_MAX_BYTES
|
|
478
485
|
};
|
|
479
|
-
|
|
486
|
+
assertSupportedHistory(options.messages);
|
|
480
487
|
const split = splitSystemPrompt(options);
|
|
481
488
|
const projection = {
|
|
482
489
|
maxBytes: maxRequestImageBytes,
|
|
@@ -749,8 +756,26 @@ function parseGoModelCatalog(value) {
|
|
|
749
756
|
return {
|
|
750
757
|
models: parseGoModels(catalog.models),
|
|
751
758
|
stale: catalog.stale,
|
|
752
|
-
...catalog.error === void 0 ? {} : { error: catalog.error }
|
|
759
|
+
...catalog.error === void 0 ? {} : { error: catalog.error },
|
|
760
|
+
...catalog.sources === void 0 ? {} : { sources: parseSources(catalog.sources) }
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
function parseSources(value) {
|
|
764
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid OpenCode Go catalog sources");
|
|
765
|
+
const sources = value;
|
|
766
|
+
const parse = (value2) => {
|
|
767
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid OpenCode Go catalog source status");
|
|
768
|
+
const source = value2;
|
|
769
|
+
if (source.updatedAt !== void 0 && (typeof source.updatedAt !== "number" || !Number.isSafeInteger(source.updatedAt) || source.updatedAt < 0 || source.updatedAt > 864e13)) {
|
|
770
|
+
throw new Error("Invalid OpenCode Go catalog source timestamp");
|
|
771
|
+
}
|
|
772
|
+
if (source.error !== void 0 && typeof source.error !== "string") throw new Error("Invalid OpenCode Go catalog source error");
|
|
773
|
+
return {
|
|
774
|
+
...source.updatedAt === void 0 ? {} : { updatedAt: source.updatedAt },
|
|
775
|
+
...source.error === void 0 ? {} : { error: source.error }
|
|
776
|
+
};
|
|
753
777
|
};
|
|
778
|
+
return { listing: parse(sources.listing), metadata: parse(sources.metadata) };
|
|
754
779
|
}
|
|
755
780
|
var codec = {
|
|
756
781
|
mode: "strict",
|
|
@@ -973,8 +998,30 @@ var PROVIDER_ID = "opencode-go";
|
|
|
973
998
|
var DISPLAY_NAME = "OpenCode Go";
|
|
974
999
|
var DEFAULT_BASE_URL = "https://opencode.ai/zen/go/v1";
|
|
975
1000
|
var MODELS_FETCH_TIMEOUT_MS = 1e4;
|
|
1001
|
+
var METADATA_FETCH_TIMEOUT_MS = 3e4;
|
|
1002
|
+
var STALE_WHILE_REVALIDATE_MS = 5 * 6e4;
|
|
976
1003
|
var MODEL_LISTING_MAX_BYTES = 1024 * 1024;
|
|
977
1004
|
var MODEL_METADATA_MAX_BYTES = 16 * 1024 * 1024;
|
|
1005
|
+
var RETRY_MIN_MS = 5e3;
|
|
1006
|
+
var RETRY_MAX_MS = 6e4;
|
|
1007
|
+
function waitForSnapshot(pending, signal) {
|
|
1008
|
+
if (signal === void 0) return pending;
|
|
1009
|
+
return new Promise((resolve, reject) => {
|
|
1010
|
+
const abort = () => {
|
|
1011
|
+
signal.removeEventListener("abort", abort);
|
|
1012
|
+
reject(new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason }));
|
|
1013
|
+
};
|
|
1014
|
+
pending.then((value) => {
|
|
1015
|
+
signal.removeEventListener("abort", abort);
|
|
1016
|
+
resolve(value);
|
|
1017
|
+
}, (error) => {
|
|
1018
|
+
signal.removeEventListener("abort", abort);
|
|
1019
|
+
reject(error);
|
|
1020
|
+
});
|
|
1021
|
+
if (signal.aborted) abort();
|
|
1022
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
978
1025
|
function builtinModels(baseURL) {
|
|
979
1026
|
return new Map(getBuiltinModels("opencode-go").map((model) => [model.id, {
|
|
980
1027
|
...model,
|
|
@@ -1047,17 +1094,26 @@ var OpencodeGoCatalog = class {
|
|
|
1047
1094
|
pending;
|
|
1048
1095
|
metadata;
|
|
1049
1096
|
metadataETag;
|
|
1050
|
-
|
|
1051
|
-
|
|
1097
|
+
metadataUpdatedAtMs;
|
|
1098
|
+
failures = 0;
|
|
1099
|
+
refreshAtMs = 0;
|
|
1100
|
+
snapshot(force = false, signal) {
|
|
1101
|
+
if (signal?.aborted) {
|
|
1102
|
+
return Promise.reject(new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason }));
|
|
1103
|
+
}
|
|
1104
|
+
if (!force && this.served !== void 0 && Date.now() < this.refreshAtMs) {
|
|
1052
1105
|
return Promise.resolve(this.served);
|
|
1053
1106
|
}
|
|
1054
1107
|
this.pending ??= this.build().then((snapshot) => {
|
|
1055
1108
|
this.served = snapshot;
|
|
1109
|
+
this.failures = snapshot.live && snapshot.metadataLive ? 0 : Math.min(this.failures + 1, 5);
|
|
1110
|
+
const lifetime = this.failures === 0 ? this.refreshMs : Math.min(this.refreshMs, RETRY_MIN_MS * 2 ** (this.failures - 1), RETRY_MAX_MS);
|
|
1111
|
+
this.refreshAtMs = snapshot.fetchedAtMs + lifetime;
|
|
1056
1112
|
return snapshot;
|
|
1057
1113
|
}).finally(() => {
|
|
1058
1114
|
this.pending = void 0;
|
|
1059
1115
|
});
|
|
1060
|
-
return this.pending;
|
|
1116
|
+
return waitForSnapshot(this.pending, signal);
|
|
1061
1117
|
}
|
|
1062
1118
|
/** Conditional HTTP requests save bandwidth while still checking for updated metadata. */
|
|
1063
1119
|
async refreshMetadata(builtin) {
|
|
@@ -1068,23 +1124,37 @@ var OpencodeGoCatalog = class {
|
|
|
1068
1124
|
...this.metadataETag === void 0 ? {} : { "if-none-match": this.metadataETag }
|
|
1069
1125
|
},
|
|
1070
1126
|
cache: "no-cache",
|
|
1071
|
-
signal: AbortSignal.timeout(
|
|
1127
|
+
signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)
|
|
1072
1128
|
}, MODEL_METADATA_MAX_BYTES);
|
|
1073
|
-
if (response.status === 304 && this.metadata !== void 0)
|
|
1074
|
-
|
|
1075
|
-
|
|
1129
|
+
if (response.status === 304 && this.metadata !== void 0) {
|
|
1130
|
+
this.metadataUpdatedAtMs = Date.now();
|
|
1131
|
+
return this.metadata;
|
|
1132
|
+
}
|
|
1133
|
+
if (!response.ok) throw new LlmError5(`${MODEL_METADATA_URL} answered HTTP ${response.status}`, "DISCOVERY_FAILED");
|
|
1134
|
+
let metadata;
|
|
1135
|
+
try {
|
|
1136
|
+
metadata = readModelMetadata(body, this.baseURL, builtin);
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
throw new LlmError5(`${MODEL_METADATA_URL} returned invalid model configuration`, "DISCOVERY_FAILED", { cause: error });
|
|
1139
|
+
}
|
|
1076
1140
|
this.metadata = metadata;
|
|
1077
1141
|
this.metadataETag = response.headers.get("etag") ?? void 0;
|
|
1142
|
+
this.metadataUpdatedAtMs = Date.now();
|
|
1078
1143
|
return metadata;
|
|
1079
1144
|
}
|
|
1080
1145
|
/** Gateway ids decide membership; online metadata decides how to call each model. */
|
|
1081
1146
|
async build() {
|
|
1082
1147
|
const builtin = builtinModels(this.baseURL);
|
|
1083
1148
|
const [listing, metadataResult] = await Promise.allSettled([
|
|
1084
|
-
fetchLiveModelIds(this.baseURL),
|
|
1149
|
+
fetchLiveModelIds(this.baseURL).then((ids) => ({ ids, updatedAtMs: Date.now() })),
|
|
1085
1150
|
this.refreshMetadata(builtin)
|
|
1086
1151
|
]);
|
|
1087
1152
|
const metadata = metadataResult.status === "fulfilled" ? metadataResult.value : this.metadata;
|
|
1153
|
+
const metadataStatus = {
|
|
1154
|
+
metadataLive: metadataResult.status === "fulfilled",
|
|
1155
|
+
...this.metadataUpdatedAtMs === void 0 ? {} : { metadataUpdatedAtMs: this.metadataUpdatedAtMs },
|
|
1156
|
+
...metadataResult.status === "rejected" ? { metadataFailure: metadataResult.reason } : {}
|
|
1157
|
+
};
|
|
1088
1158
|
const known = new Map([...builtin, ...this.served?.models ?? [], ...metadata?.models ?? []]);
|
|
1089
1159
|
if (metadataResult.status === "rejected") {
|
|
1090
1160
|
this.onFallback({ url: MODEL_METADATA_URL, error: metadataResult.reason, kept: known.size });
|
|
@@ -1099,12 +1169,14 @@ var OpencodeGoCatalog = class {
|
|
|
1099
1169
|
provider: buildProvider(this.baseURL, [...models2.values()]),
|
|
1100
1170
|
live: false,
|
|
1101
1171
|
fetchedAtMs: Date.now(),
|
|
1172
|
+
...metadataStatus,
|
|
1173
|
+
...this.served?.listingUpdatedAtMs === void 0 ? {} : { listingUpdatedAtMs: this.served.listingUpdatedAtMs },
|
|
1102
1174
|
listingFailure: listing.reason
|
|
1103
1175
|
};
|
|
1104
1176
|
}
|
|
1105
1177
|
const models = /* @__PURE__ */ new Map();
|
|
1106
1178
|
const unavailable = /* @__PURE__ */ new Map();
|
|
1107
|
-
for (const id of listing.value) {
|
|
1179
|
+
for (const id of listing.value.ids) {
|
|
1108
1180
|
const error = metadata?.errors.get(id);
|
|
1109
1181
|
const model = known.get(id);
|
|
1110
1182
|
if (error !== void 0 || model === void 0) {
|
|
@@ -1115,19 +1187,28 @@ var OpencodeGoCatalog = class {
|
|
|
1115
1187
|
}
|
|
1116
1188
|
if (unavailable.size > 0) this.onOmitted([...unavailable.keys()]);
|
|
1117
1189
|
return {
|
|
1118
|
-
details: new Map(listing.value.map((id) => [id, metadata?.details.get(id) ?? {}])),
|
|
1190
|
+
details: new Map(listing.value.ids.map((id) => [id, metadata?.details.get(id) ?? {}])),
|
|
1119
1191
|
models,
|
|
1120
1192
|
unavailable,
|
|
1121
1193
|
provider: buildProvider(this.baseURL, [...models.values()]),
|
|
1122
1194
|
live: true,
|
|
1123
|
-
fetchedAtMs: Date.now()
|
|
1195
|
+
fetchedAtMs: Date.now(),
|
|
1196
|
+
listingUpdatedAtMs: listing.value.updatedAtMs,
|
|
1197
|
+
...metadataStatus
|
|
1124
1198
|
};
|
|
1125
1199
|
}
|
|
1126
1200
|
/** New or previously unconfigured ids get a fresh lookup even during the runtime TTL. */
|
|
1127
|
-
async forModel(id) {
|
|
1201
|
+
async forModel(id, signal) {
|
|
1128
1202
|
const cached = this.served;
|
|
1129
|
-
|
|
1130
|
-
if (
|
|
1203
|
+
if (signal?.aborted) throw new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason });
|
|
1204
|
+
if (cached?.models.has(id) && cached.listingUpdatedAtMs !== void 0 && cached.metadataUpdatedAtMs !== void 0 && Date.now() < Math.min(cached.listingUpdatedAtMs, cached.metadataUpdatedAtMs) + this.refreshMs + STALE_WHILE_REVALIDATE_MS) {
|
|
1205
|
+
void this.snapshot().catch(() => {
|
|
1206
|
+
});
|
|
1207
|
+
return cached;
|
|
1208
|
+
}
|
|
1209
|
+
let snapshot = await this.snapshot(false, signal);
|
|
1210
|
+
if (!snapshot.models.has(id) && snapshot === cached) snapshot = await this.snapshot(true, signal);
|
|
1211
|
+
if (signal?.aborted) throw new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason });
|
|
1131
1212
|
if (snapshot.unavailable.has(id)) {
|
|
1132
1213
|
throw new LlmError5(
|
|
1133
1214
|
`opencode-go model "${id}" is advertised but cannot be configured: ${snapshot.unavailable.get(id)}; refresh the model list to retry`,
|
|
@@ -1166,15 +1247,31 @@ function describeCatalog(snapshot) {
|
|
|
1166
1247
|
}
|
|
1167
1248
|
async function discoverSettingsModels(catalog) {
|
|
1168
1249
|
const snapshot = await catalog.snapshot(true);
|
|
1250
|
+
const metadataError = snapshot.metadataLive ? void 0 : metadataFailureMessage(snapshot.metadataFailure);
|
|
1251
|
+
const errors = [snapshot.live ? void 0 : listingError(snapshot).message, metadataError].filter((message) => message !== void 0);
|
|
1169
1252
|
return {
|
|
1170
1253
|
models: sortModels([
|
|
1171
1254
|
...describeConfiguredModels(snapshot),
|
|
1172
1255
|
...[...snapshot.unavailable.keys()].map((id) => ({ id, name: id, configurationMissing: true }))
|
|
1173
1256
|
].map((model) => ({ ...model, ...snapshot.details.get(model.id) }))),
|
|
1174
|
-
stale:
|
|
1175
|
-
...
|
|
1257
|
+
stale: errors.length > 0,
|
|
1258
|
+
...errors.length === 0 ? {} : { error: errors.join("; ") },
|
|
1259
|
+
sources: {
|
|
1260
|
+
listing: {
|
|
1261
|
+
...snapshot.listingUpdatedAtMs === void 0 ? {} : { updatedAt: snapshot.listingUpdatedAtMs },
|
|
1262
|
+
...snapshot.live ? {} : { error: listingError(snapshot).message }
|
|
1263
|
+
},
|
|
1264
|
+
metadata: {
|
|
1265
|
+
...snapshot.metadataUpdatedAtMs === void 0 ? {} : { updatedAt: snapshot.metadataUpdatedAtMs },
|
|
1266
|
+
...metadataError === void 0 ? {} : { error: metadataError }
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1176
1269
|
};
|
|
1177
1270
|
}
|
|
1271
|
+
function metadataFailureMessage(error) {
|
|
1272
|
+
const detail = error instanceof LlmError5 ? error.message : `${MODEL_METADATA_URL}: ${error instanceof SyntaxError ? "invalid JSON response" : error instanceof RangeError ? `response exceeds the ${MODEL_METADATA_MAX_BYTES} byte limit` : transportFailure(error)}`;
|
|
1273
|
+
return `could not refresh model configuration (${detail}); using previously fetched or built-in configuration where available`;
|
|
1274
|
+
}
|
|
1178
1275
|
|
|
1179
1276
|
// src/config.ts
|
|
1180
1277
|
import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
|
|
@@ -1288,9 +1385,9 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
1288
1385
|
inputModalities: [...model.input]
|
|
1289
1386
|
}));
|
|
1290
1387
|
}
|
|
1291
|
-
async resolveModel(_provider, model,
|
|
1388
|
+
async resolveModel(_provider, model, signal) {
|
|
1292
1389
|
const config = this.options.config();
|
|
1293
|
-
const snapshot = await this.catalogOf(config).forModel(model);
|
|
1390
|
+
const snapshot = await this.catalogOf(config).forModel(model, signal);
|
|
1294
1391
|
const resolved = snapshot.models.get(model);
|
|
1295
1392
|
if (resolved === void 0) {
|
|
1296
1393
|
throw new LlmError6(`opencode-go has no model "${model}"`, "UNKNOWN_MODEL");
|
|
@@ -1338,7 +1435,17 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
1338
1435
|
throw new LlmError6("llm-opencode-go does not support GenerateOptions.stop", "UNSUPPORTED_OPTION");
|
|
1339
1436
|
}
|
|
1340
1437
|
const config = this.options.config();
|
|
1341
|
-
|
|
1438
|
+
let snapshot;
|
|
1439
|
+
try {
|
|
1440
|
+
snapshot = await this.catalogOf(config).forModel(options.model, options.signal);
|
|
1441
|
+
} catch (error) {
|
|
1442
|
+
if (!options.signal?.aborted) throw error;
|
|
1443
|
+
yield { type: "finish", reason: {
|
|
1444
|
+
kind: "aborted",
|
|
1445
|
+
failure: { code: "ABORTED", message: "opencode-go request aborted by caller" }
|
|
1446
|
+
} };
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1342
1449
|
const advertised = snapshot.models.get(options.model);
|
|
1343
1450
|
if (advertised === void 0) {
|
|
1344
1451
|
throw new LlmError6(`opencode-go has no model "${options.model}"`, "UNKNOWN_MODEL");
|
package/lib/types/adapter.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* attribution User-Agent (`deepseek-harness/<version>`), which pi-ai's client
|
|
6
6
|
* lets request headers override, and `x-opencode-session`, which the gateway
|
|
7
7
|
* requires and uses to route a conversation and share its prompt cache. The
|
|
8
|
-
* header value is the request's session id
|
|
9
|
-
*
|
|
8
|
+
* header value is the request's session id, stable across continuation and
|
|
9
|
+
* restore. Actual cache reads remain an upstream usage fact. A request with no
|
|
10
10
|
* session id gets a fresh random value rather than a shared constant, because
|
|
11
11
|
* a constant would merge unrelated traffic into one cache bucket.
|
|
12
12
|
*
|
|
@@ -91,7 +91,7 @@ export declare class OpencodeGoAdapter extends LlmAdapter {
|
|
|
91
91
|
name: string;
|
|
92
92
|
};
|
|
93
93
|
listModels(_provider: string): Promise<readonly LlmModelInfo[]>;
|
|
94
|
-
resolveModel(_provider: string, model: string,
|
|
94
|
+
resolveModel(_provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
|
|
95
95
|
/** Describe one model: capacities plus the reasoning levels it actually offers. */
|
|
96
96
|
private modelInfo;
|
|
97
97
|
/** Validate an explicit effort against the model's own levels, without clamping. */
|
package/lib/types/catalog.d.ts
CHANGED
|
@@ -12,8 +12,13 @@ export interface CatalogSnapshot {
|
|
|
12
12
|
readonly unavailable: ReadonlyMap<string, string>;
|
|
13
13
|
readonly provider: Provider;
|
|
14
14
|
readonly live: boolean;
|
|
15
|
+
readonly metadataLive: boolean;
|
|
15
16
|
/** Retained for explicit discovery; runtime callers may still use the last catalog. */
|
|
16
17
|
readonly listingFailure?: unknown;
|
|
18
|
+
readonly metadataFailure?: unknown;
|
|
19
|
+
/** Last successful fetch or revalidation, not the time of a failed attempt. */
|
|
20
|
+
readonly listingUpdatedAtMs?: number;
|
|
21
|
+
readonly metadataUpdatedAtMs?: number;
|
|
17
22
|
readonly fetchedAtMs: number;
|
|
18
23
|
}
|
|
19
24
|
/** A valid empty listing means the gateway serves nothing; malformed replies are failures. */
|
|
@@ -29,6 +34,9 @@ export declare class OpencodeGoCatalog {
|
|
|
29
34
|
private pending;
|
|
30
35
|
private metadata;
|
|
31
36
|
private metadataETag;
|
|
37
|
+
private metadataUpdatedAtMs;
|
|
38
|
+
private failures;
|
|
39
|
+
private refreshAtMs;
|
|
32
40
|
constructor(baseURL: string, refreshMs: number, onFallback: (detail: {
|
|
33
41
|
url: string;
|
|
34
42
|
error: unknown;
|
|
@@ -36,13 +44,13 @@ export declare class OpencodeGoCatalog {
|
|
|
36
44
|
}) => void,
|
|
37
45
|
/** Kept for API compatibility; now reports unconfigured ids rather than hiding them. */
|
|
38
46
|
onOmitted: (ids: readonly string[]) => void);
|
|
39
|
-
snapshot(force?: boolean): Promise<CatalogSnapshot>;
|
|
47
|
+
snapshot(force?: boolean, signal?: AbortSignal): Promise<CatalogSnapshot>;
|
|
40
48
|
/** Conditional HTTP requests save bandwidth while still checking for updated metadata. */
|
|
41
49
|
private refreshMetadata;
|
|
42
50
|
/** Gateway ids decide membership; online metadata decides how to call each model. */
|
|
43
51
|
private build;
|
|
44
52
|
/** New or previously unconfigured ids get a fresh lookup even during the runtime TTL. */
|
|
45
|
-
forModel(id: string): Promise<CatalogSnapshot>;
|
|
53
|
+
forModel(id: string, signal?: AbortSignal): Promise<CatalogSnapshot>;
|
|
46
54
|
}
|
|
47
55
|
/** Explicit discovery always revalidates both sources, including during the runtime TTL. */
|
|
48
56
|
export declare function discoverCatalogModels(catalog: OpencodeGoCatalog): Promise<readonly LlmDiscoveredModel[]>;
|
|
@@ -20,6 +20,8 @@ export declare const en: {
|
|
|
20
20
|
nav: string;
|
|
21
21
|
title: string;
|
|
22
22
|
intro: string;
|
|
23
|
+
starProject: string;
|
|
24
|
+
starProjectTitle: string;
|
|
23
25
|
enabledLabel: string;
|
|
24
26
|
enabledHint: string;
|
|
25
27
|
enabledOff: string;
|
|
@@ -34,6 +36,13 @@ export declare const en: {
|
|
|
34
36
|
modelsLoading: string;
|
|
35
37
|
modelsFailed: string;
|
|
36
38
|
modelsStale: string;
|
|
39
|
+
modelsMetadataFailed: string;
|
|
40
|
+
modelsListingSource: string;
|
|
41
|
+
modelsMetadataSource: string;
|
|
42
|
+
modelsSourceFailed: string;
|
|
43
|
+
modelsSourceCurrent: string;
|
|
44
|
+
modelsSourceNeverFetched: string;
|
|
45
|
+
modelsSourceUpdated: string;
|
|
37
46
|
modelsRefreshing: string;
|
|
38
47
|
modelsEmpty: string;
|
|
39
48
|
modelsCount: string;
|
|
@@ -51,6 +60,8 @@ export declare const en: {
|
|
|
51
60
|
modelVisibleLabel: string;
|
|
52
61
|
configurationMissing: string;
|
|
53
62
|
configurationMissingHint: string;
|
|
63
|
+
configurationUnavailable: string;
|
|
64
|
+
configurationUnavailableHint: string;
|
|
54
65
|
pickerFailed: string;
|
|
55
66
|
deprecatedBadge: string;
|
|
56
67
|
deprecatedHint: string;
|
|
@@ -67,11 +67,13 @@ export type OpencodeGoModels =
|
|
|
67
67
|
readonly stale?: boolean;
|
|
68
68
|
readonly message?: string;
|
|
69
69
|
readonly refreshing?: boolean;
|
|
70
|
+
readonly sources?: GoModelCatalog['sources'];
|
|
70
71
|
}
|
|
71
72
|
/** The listing could not be read; `message` is the Host's own diagnostic. */
|
|
72
73
|
| {
|
|
73
74
|
readonly status: 'failed';
|
|
74
75
|
readonly message?: string;
|
|
76
|
+
readonly sources?: GoModelCatalog['sources'];
|
|
75
77
|
};
|
|
76
78
|
/** What the settings page renders. */
|
|
77
79
|
export interface OpencodeGoSectionState extends FormShell {
|
package/lib/types/config.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import z from '@deepseek-ai/schemastery';
|
|
12
12
|
/** Environment variable resolving the OpenCode API key. */
|
|
13
13
|
export declare const DEFAULT_API_KEY_ENV = "OPENCODE_API_KEY";
|
|
14
|
-
/**
|
|
14
|
+
/** Successful refresh lifetime; failed refreshes retry sooner and explicit discovery revalidates immediately. */
|
|
15
15
|
export declare const DEFAULT_REFRESH_MINUTES = 60;
|
|
16
16
|
/** Default maximum idle interval while a stream read is outstanding. */
|
|
17
17
|
export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
|
|
@@ -31,7 +31,7 @@ export interface PiImageRequestBudget {
|
|
|
31
31
|
* @param images - absent; selects the synchronous conversion.
|
|
32
32
|
* @param onReplayDegrade - forwarded to {@link toPiAssistant} for each assistant message.
|
|
33
33
|
* @returns the pi-ai context; `tools` is omitted when the request declares none.
|
|
34
|
-
* @throws {LlmError} `UNSUPPORTED_CONTENT` for
|
|
34
|
+
* @throws {LlmError} `UNSUPPORTED_CONTENT` for a `developer` history message, a tool-change block, a deferred tool, or an image in any history role, including a leading system message.
|
|
35
35
|
*/
|
|
36
36
|
export declare function toPiContext(options: GenerateOptions, images?: undefined, onReplayDegrade?: (reason: string) => void): PiContext;
|
|
37
37
|
/**
|
|
@@ -10,11 +10,21 @@ export interface GoModel {
|
|
|
10
10
|
/** Advertised by the gateway but lacking a usable protocol and capability configuration. */
|
|
11
11
|
configurationMissing?: boolean;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
13
|
+
/** Last successful check of one catalog source, plus any current refresh failure. */
|
|
14
|
+
export interface GoCatalogSourceStatus {
|
|
15
|
+
readonly updatedAt?: number;
|
|
16
|
+
readonly error?: string;
|
|
17
|
+
}
|
|
18
|
+
/** A failed refresh retains the Host's usable data with an explicit diagnostic. */
|
|
14
19
|
export interface GoModelCatalog {
|
|
15
20
|
readonly models: readonly GoModel[];
|
|
16
21
|
readonly stale: boolean;
|
|
17
22
|
readonly error?: string;
|
|
23
|
+
/** Optional for clients reading an older Host response. Timestamps are Unix milliseconds. */
|
|
24
|
+
readonly sources?: {
|
|
25
|
+
readonly listing: GoCatalogSourceStatus;
|
|
26
|
+
readonly metadata: GoCatalogSourceStatus;
|
|
27
|
+
};
|
|
18
28
|
}
|
|
19
29
|
/** Missing configuration cannot be enabled; configured models follow explicit switches or lifecycle defaults. */
|
|
20
30
|
export declare function isModelEnabled(model: Pick<GoModel, 'id' | 'deprecated' | 'configurationMissing'>, modelVisibility?: Readonly<Record<string, boolean>>): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-opencode-go",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "OpenCode Go model provider and settings UI for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": "^22.19.0 || >=24.0.0",
|
|
17
|
-
"dsh": ">=0.1.5-rc.1
|
|
17
|
+
"dsh": ">=0.1.5-rc.1"
|
|
18
18
|
},
|
|
19
19
|
"main": "./lib/index.js",
|
|
20
20
|
"types": "./lib/types/index.d.ts",
|
|
@@ -60,26 +60,26 @@
|
|
|
60
60
|
"verify:headless": "node scripts/headless-smoke.mjs"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@deepseek-ai/cordis": "4.0.2
|
|
64
|
-
"@deepseek-ai/dsh-api-gateway": ">=0.1.5-rc.1
|
|
65
|
-
"@deepseek-ai/dsh-api-remotes": ">=0.1.5-rc.1
|
|
66
|
-
"@deepseek-ai/dsh-api-settings-controller": ">=0.1.5-rc.1
|
|
67
|
-
"@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1
|
|
68
|
-
"@deepseek-ai/dsh-client-locale": ">=0.1.5-rc.1
|
|
69
|
-
"@deepseek-ai/dsh-client-store": ">=0.1.5-rc.1
|
|
70
|
-
"@deepseek-ai/dsh-client-ui-conversation": ">=0.1.5-rc.1
|
|
71
|
-
"@deepseek-ai/dsh-client-ui-model-selection": ">=0.1.5-rc.1
|
|
72
|
-
"@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.1
|
|
73
|
-
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.5-rc.1
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.1
|
|
75
|
-
"@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1
|
|
76
|
-
"@deepseek-ai/dsh-fs": ">=0.1.5-rc.1
|
|
77
|
-
"@deepseek-ai/dsh-launch-environment": ">=0.1.5-rc.1
|
|
78
|
-
"@deepseek-ai/dsh-llm": ">=0.1.5-rc.1
|
|
79
|
-
"@deepseek-ai/dsh-settings": ">=0.1.5-rc.1
|
|
80
|
-
"@deepseek-ai/dsh-timeout": ">=0.1.5-rc.1
|
|
81
|
-
"@deepseek-ai/dsh-typert-protocol": ">=0.1.5-rc.1
|
|
82
|
-
"@deepseek-ai/dsh-typert-registry": ">=0.1.5-rc.1
|
|
63
|
+
"@deepseek-ai/cordis": ">=4.0.2",
|
|
64
|
+
"@deepseek-ai/dsh-api-gateway": ">=0.1.5-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-api-remotes": ">=0.1.5-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-api-settings-controller": ">=0.1.5-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-client-locale": ">=0.1.5-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-client-store": ">=0.1.5-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-client-ui-conversation": ">=0.1.5-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-client-ui-model-selection": ">=0.1.5-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.5-rc.1",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1",
|
|
76
|
+
"@deepseek-ai/dsh-fs": ">=0.1.5-rc.1",
|
|
77
|
+
"@deepseek-ai/dsh-launch-environment": ">=0.1.5-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-llm": ">=0.1.5-rc.1",
|
|
79
|
+
"@deepseek-ai/dsh-settings": ">=0.1.5-rc.1",
|
|
80
|
+
"@deepseek-ai/dsh-timeout": ">=0.1.5-rc.1",
|
|
81
|
+
"@deepseek-ai/dsh-typert-protocol": ">=0.1.5-rc.1",
|
|
82
|
+
"@deepseek-ai/dsh-typert-registry": ">=0.1.5-rc.1"
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@deepseek-ai/dsh-brand": "0.1.6-alpha.1",
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
"@deepseek-ai/dsh-home-paths": "0.1.6-alpha.1",
|
|
111
111
|
"@deepseek-ai/dsh-launch-environment": "0.1.6-alpha.1",
|
|
112
112
|
"@deepseek-ai/dsh-llm": "0.1.6-alpha.1",
|
|
113
|
+
"@deepseek-ai/dsh-session": "0.1.6-alpha.1",
|
|
113
114
|
"@deepseek-ai/dsh-settings": "0.1.6-alpha.1",
|
|
114
115
|
"@deepseek-ai/dsh-settings-file": "0.1.6-alpha.1",
|
|
115
116
|
"@deepseek-ai/dsh-timeout": "0.1.6-alpha.1",
|