@mars-sea/dsh-commandcode-provider 0.1.6 → 0.1.9
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 +33 -0
- package/README.md +57 -17
- package/README.zh-CN.md +56 -17
- package/cordis.patch.yml +8 -1
- package/lib/client.js +57 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +39 -1
- package/lib/index.js +150 -20
- package/lib/index.js.map +1 -1
- package/package.json +19 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,39 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.1.9] - 2026-08-15
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Image input for Vision-capable models.** Models the official Command Code registry lists with Vision (see `KNOWN_IMAGE_MODELS` in `src/adapter.ts`, synced from the [official model registry](https://commandcode.ai/docs/reference/cli/models)) now accept attached images: bytes resolve through the dsh attachment service (`ctx.attachments`) and are sent in the official CLI wire shape `{ type: 'image', source: { type: 'base64', media_type, data } }`. Text-only models (e.g. `deepseek/deepseek-v4-flash`) refuse images loudly (`UNSUPPORTED_CONTENT`) rather than silently dropping them; a request carrying images also requires the attachment service. The `CommandCodeAdapterDeps` seam gains an optional `resolveAttachments` resolver (used lazily, only when a request actually has images).
|
|
12
|
+
- **The model picker now shows each Command Code model's image capability** (`listModels`/`resolveModel` return a `description`: *"Supports image input"* / *"Text only"*), so switching in an image-bearing session is informed instead of surprising.
|
|
13
|
+
- **A client half for the bundle** (`dsh.client` + `exports["./client"]` → `lib/client.js`): it wraps the shared `session.selectModel` face and rewrites the harness's image-session `model-unavailable` rejection into a clear, actionable message — `当前会话已包含图片,而模型 <model> 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。` — while passing the error code and details through unchanged. The rejection itself is a deliberate `dsh-host-apiproxy` guard that cannot be relaxed from the plugin side; this makes it friendlier. Both READMEs document the behavior.
|
|
14
|
+
|
|
15
|
+
## [0.1.8] - 2026-08-15
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **The 0.1.7 scoped-name fix crashed on boot with a YAML parse error.** 0.1.7 rewrote the patch row's `name` to `@mars-sea/dsh-commandcode-provider` but left it unquoted; a YAML scalar starting with `@` is an indicator and fails to parse (`YAMLException: bad indentation of a mapping entry`), so `dsh --dump-config` and every boot died. The value is now quoted: `name: "@mars-sea/dsh-commandcode-provider"` (verified against dsh's own js-yaml and a real profile boot).
|
|
20
|
+
- **Transport failures now surface the real root cause.** The `TRANSPORT` error from a failed `fetch` (DNS, refused/reset connection, TLS, proxy, timeout) previously reported only the generic wrapper — `Command Code API request to .../alpha/generate failed` — while the actionable detail sat unused on `error.cause`. The web UI renders only the error message (not the cause chain), so users hit a wall of retry rows ("重试延迟"/"Retry delay") with no way to diagnose. The message now appends `errorChain(cause)`, so the failure reason names e.g. `connect ECONNREFUSED`, `ENOTFOUND`, `CERT_HAS_EXPIRED`, or the timeout abort.
|
|
21
|
+
- **A stalled connection no longer hangs the turn.** `requestTimeoutMs` (default 60s) bounds the wait for the first response byte via `AbortSignal.timeout`, and `streamIdleTimeoutMs` (default 120s) treats a stream with no events as a dead connection — both fail as `TIMEOUT` with the duration instead of hanging until the OS socket timeout (which can be minutes). Configurable per profile in the `llm-commandcode` settings section.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- `CommandCodeConnectionOptions` gains `requestTimeoutMs` and `streamIdleTimeoutMs`; both are optional in the `Config` schema and default to 60s/120s. New `DEFAULT_REQUEST_TIMEOUT_MS` / `DEFAULT_STREAM_IDLE_TIMEOUT_MS` exports.
|
|
26
|
+
- Both READMEs document the new knobs and the transport-failure troubleshooting entry (notably: Node's fetch ignores `HTTP_PROXY`/`HTTPS_PROXY`, so proxy-dependent networks fail here while the browser works).
|
|
27
|
+
- Both READMEs gain an **Updating** section: since the bundle patch layer is read from the installed package at boot, updating the package fixes the patch row automatically; the section covers npm/git/local update commands and the ≤0.1.6 hand-copied-patch caveat.
|
|
28
|
+
- Both READMEs restructure the install docs: **npm is now the recommended install path** (one command, always the latest published release), GitHub moves below it, and the uninstall command is documented (use the scoped name `@mars-sea/dsh-commandcode-provider`, since pnpm records dependencies under the real package name).
|
|
29
|
+
|
|
30
|
+
## [0.1.7] - 2026-08-15
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **Boot-crashing patch row for every install path** (`cordis.patch.yml`): the layer's `name` was the bare `dsh-commandcode-provider`, but the loader imports it as a module from the profile's `node_modules`, where pnpm only links the true scoped name `@mars-sea/dsh-commandcode-provider`. Any install (npm, GitHub, local path — all of which link the scoped name) failed at load with `ERR_MODULE_NOT_FOUND` and took the web app into a `Restart=on-failure` crash loop. The row now reads `name: @mars-sea/dsh-commandcode-provider`. Existing profiles that copied the old row (or an old README example) into their own `cordis.patch.yml` must update it the same way (see the Troubleshooting entry).
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Both READMEs document the scoped `name` requirement, show the corrected `--dump-config` layer heading (`# == @mars-sea/dsh-commandcode-provider`), and use the scoped name in the `allowBuilds` example and the `remove` command.
|
|
39
|
+
|
|
7
40
|
## [0.1.6] - 2026-08-15
|
|
8
41
|
|
|
9
42
|
### Added
|
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ Unofficial [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harnes
|
|
|
17
17
|
- A **Models-page card** ("Command Code") with an API-key field — credentials are stored through the dsh credentials service, same as the DeepSeek card.
|
|
18
18
|
- **API key resolution** in this order: `config.apiKey` → credential reference `apiKeyEnv` (the web Models page writes it, default `COMMANDCODE_API_KEY`) → the launching environment → the official Command Code CLI auth file (`~/.commandcode/auth.json`, written by `command-code login`).
|
|
19
19
|
- **Reasoning-effort support** for the models Command Code's catalog marks as such (e.g. `claude-opus-5`, `gpt-5.5`, `deepseek/deepseek-v4-pro`, …) via `KNOWN_EFFORTS`, matching the official command-code@1.26.0 bundled catalog.
|
|
20
|
+
- **Image input for Vision-capable models**: models the official registry lists with Vision (e.g. `claude-sonnet-5`, `gpt-5.4`, `google/gemini-3.5-flash`, …) accept attached images, resolved through the dsh attachment service and sent in the official Command Code wire format. Text-only models (e.g. `deepseek/deepseek-v4-flash`, `zai-org/GLM-5.3`) refuse images loudly rather than silently dropping them.
|
|
20
21
|
|
|
21
22
|
## Getting an API key
|
|
22
23
|
|
|
@@ -31,11 +32,19 @@ cmd login # macOS/Linux; native Windows: cmdc login
|
|
|
31
32
|
|
|
32
33
|
## Install
|
|
33
34
|
|
|
34
|
-
### From
|
|
35
|
+
### From npm (recommended)
|
|
36
|
+
|
|
37
|
+
The plugin is published to the npm registry as **`@mars-sea/dsh-commandcode-provider`** (the bare name `dsh-commandcode-provider` is taken by an unrelated package):
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### From GitHub
|
|
35
44
|
|
|
36
45
|
```sh
|
|
37
46
|
# Pin a release tag (recommended — readable and immutable)
|
|
38
|
-
dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.
|
|
47
|
+
dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.8
|
|
39
48
|
# Or pin any exact commit by its SHA
|
|
40
49
|
dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#<full-commit-sha>
|
|
41
50
|
```
|
|
@@ -46,19 +55,11 @@ A git install fetches **sources**, so the package's `prepare` script builds `lib
|
|
|
46
55
|
|
|
47
56
|
```yaml
|
|
48
57
|
allowBuilds:
|
|
49
|
-
'dsh-commandcode-provider@github:Mars-Sea/dsh-commandcode-provider#<full-commit-sha>': true
|
|
58
|
+
'@mars-sea/dsh-commandcode-provider@github:Mars-Sea/dsh-commandcode-provider#<full-commit-sha>': true
|
|
50
59
|
```
|
|
51
60
|
|
|
52
61
|
and re-run the `add`. Only allow packages whose source you trust (and pin a commit).
|
|
53
62
|
|
|
54
|
-
### From npm
|
|
55
|
-
|
|
56
|
-
Published as **`@mars-sea/dsh-commandcode-provider`** (the bare name `dsh-commandcode-provider` is taken on the npm registry by an unrelated package):
|
|
57
|
-
|
|
58
|
-
```sh
|
|
59
|
-
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider
|
|
60
|
-
```
|
|
61
|
-
|
|
62
63
|
### From a local checkout
|
|
63
64
|
|
|
64
65
|
```sh
|
|
@@ -71,23 +72,52 @@ A local path install links the checkout as-is, so after changing `src/` re-run `
|
|
|
71
72
|
|
|
72
73
|
### What the install does
|
|
73
74
|
|
|
74
|
-
`dsh plugin add` links the package into the profile
|
|
75
|
+
`dsh plugin add` links the package into the profile (pnpm records dependencies and links `node_modules` by the **true package name**, i.e. `@mars-sea/dsh-commandcode-provider`), appends that same name to the profile's `dsh.profile.bundles`, and activates the `cordis.patch.yml` layer, which inserts:
|
|
75
76
|
|
|
76
77
|
```yaml
|
|
77
78
|
- insert:
|
|
78
79
|
- id: llm-commandcode
|
|
79
|
-
name: dsh-commandcode-provider
|
|
80
|
+
name: "@mars-sea/dsh-commandcode-provider"
|
|
80
81
|
config:
|
|
81
82
|
apiKeyEnv: COMMANDCODE_API_KEY
|
|
82
83
|
```
|
|
83
84
|
|
|
85
|
+
The `name` in the patch row must be the **full package specifier, quoted**: the loader imports it as a module and resolves it from the profile's `node_modules`, where pnpm only ever links the scoped name. A bare `dsh-commandcode-provider` fails with `ERR_MODULE_NOT_FOUND` and crashes the app on boot, and an unquoted `@mars-sea/...` fails YAML parsing (see [Troubleshooting](#troubleshooting)).
|
|
86
|
+
|
|
84
87
|
Verify the composed layer, then (re)start the web app:
|
|
85
88
|
|
|
86
89
|
```sh
|
|
87
|
-
dsh --profile web --dump-config # shows a "# == dsh-commandcode-provider" layer
|
|
90
|
+
dsh --profile web --dump-config # shows a "# == @mars-sea/dsh-commandcode-provider" layer
|
|
88
91
|
dsh web # or restart your running instance
|
|
89
92
|
```
|
|
90
93
|
|
|
94
|
+
## Updating
|
|
95
|
+
|
|
96
|
+
The bundle's patch layer is read from the **installed package** at every boot, so updating the package brings in the fixed patch row automatically — you do not need to hand-edit `cordis.patch.yml` unless you copied its contents into your own profile layer.
|
|
97
|
+
|
|
98
|
+
Update according to how you installed it:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
# From npm (recommended): always the latest published release
|
|
102
|
+
dsh plugin --profile web update @mars-sea/dsh-commandcode-provider
|
|
103
|
+
|
|
104
|
+
# From GitHub pinned to a tag: point at the new tag
|
|
105
|
+
# (no need to uninstall first — pnpm swaps the pinned revision in place,
|
|
106
|
+
# and the bundle layer is re-read from the installed package on next boot)
|
|
107
|
+
dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.8
|
|
108
|
+
|
|
109
|
+
# From a local checkout: pull the new code, rebuild, restart
|
|
110
|
+
git -C /path/to/dsh-commandcode-provider pull
|
|
111
|
+
npm run build --prefix /path/to/dsh-commandcode-provider
|
|
112
|
+
dsh web
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Then restart the web app (`dsh web`, or restart the service). Verify the running version with `dsh --profile web --dump-config` — the layer should show `name: '@mars-sea/dsh-commandcode-provider'`.
|
|
116
|
+
|
|
117
|
+
> **Upgrading from ≤0.1.6** (or a broken hand-edited profile): the installed package's patch layer now carries the corrected, quoted `name`. If you previously *copied* the old patch row into your profile's own `cordis.patch.yml`, that copy still wins over the bundle layer — fix it manually to `name: "@mars-sea/dsh-commandcode-provider"` (see [Troubleshooting](#troubleshooting)) or remove it and let the bundle layer apply.
|
|
118
|
+
|
|
119
|
+
> **To uninstall instead of upgrading** (e.g. you are on a broken pre-0.1.7 tag and want to start clean): `dsh plugin --profile web remove @mars-sea/dsh-commandcode-provider` (the scoped name — pnpm records the dependency under its real package name, so the bare `dsh-commandcode-provider` form does not match). This removes the dependency and its layer; your API key in the dsh credential store and `~/.commandcode/auth.json` are left untouched. Then install the current version with the npm or GitHub command above.
|
|
120
|
+
|
|
91
121
|
## Verify it works
|
|
92
122
|
|
|
93
123
|
After restart, in the web UI: **Settings → Models** shows a **Command Code** card; the model picker lists the live catalog under **commandcode** (54 models at the time of writing). Send a message with a model your plan includes — the default `deepseek/deepseek-v4-flash` works on entry-level plans; open-weight models (DeepSeek/Qwen/Kimi/MiniMax) generally do, while frontier models (Claude/GPT/Gemini/Grok) may require Pro/Max plans or on-demand usage (see FAQ).
|
|
@@ -133,21 +163,31 @@ llm-commandcode:
|
|
|
133
163
|
apiBase: https://api.commandcode.ai
|
|
134
164
|
workingDir: /path/to/project # reported to the API (project slug, config block)
|
|
135
165
|
modelsCachePath: ~/.commandcode/models-cache.json
|
|
166
|
+
requestTimeoutMs: 60000 # max wait for the first response byte (default 60s)
|
|
167
|
+
streamIdleTimeoutMs: 120000 # stream stall before treated as a dead connection (default 120s)
|
|
136
168
|
```
|
|
137
169
|
|
|
138
170
|
The composition-entry config (`cordis.patch.yml` / your profile `cordis.patch.yml`) accepts the same keys; a literal `apiKey` there takes precedence over the credential reference.
|
|
139
171
|
|
|
140
172
|
## Troubleshooting
|
|
141
173
|
|
|
174
|
+
- **`Command Code API request to .../alpha/generate failed` and the turn keeps retrying (`重试延迟` / "Retry delay")** — this is a **transport-layer failure**: `fetch()` never received an HTTP response (not a 401/403/429, which would say "API error"). Since dsh's retry policy retries `TRANSPORT` twice with backoff, you'll see retry rows in the UI before the turn finally fails. Since 0.1.8 the failure reason shows the **real root cause** (e.g. `fetch failed: connect ECONNREFUSED`, `ENOTFOUND`, `CERT_HAS_EXPIRED`, `The operation was aborted due to timeout`). Common causes:
|
|
175
|
+
- **A proxy is required in your network.** Node's `fetch` (undici) does **not** read `HTTP_PROXY`/`HTTPS_PROXY` environment variables, so a browser/curl that goes through a system proxy works while dsh fails. Run dsh with the proxy configured for undici (e.g. `NODE_OPTIONS=--import undici` with a dispatcher, or a network-level route), or whitelist `api.commandcode.ai`.
|
|
176
|
+
- **The connection is being reset/throttled mid-request** (firewall, GFW-style interference, unstable Wi-Fi). The error message will name it (`socket hang up`, `ECONNRESET`, `UND_ERR_SOCKET`).
|
|
177
|
+
- **TLS interception** (corporate MITM) — `CERT_HAS_EXPIRED`/`DEPTH_ZERO_SELF_SIGNED_CERT` in the chain.
|
|
178
|
+
- A transient blip that a retry recovers from; if it persists every turn, it's environmental, not the API (the models endpoint and generate endpoint respond normally from healthy networks).
|
|
179
|
+
- **A long generation stops mid-stream** — since 0.1.8 the adapter aborts a request that gets no response within `requestTimeoutMs` (60s default) and a stream that stalls past `streamIdleTimeoutMs` (120s default) instead of hanging forever. Both failures surface as `TIMEOUT` with the stall duration; tune the knobs in the `llm-commandcode` settings if your network is slow but stable.
|
|
180
|
+
- **The web app crashes on boot with `ERR_MODULE_NOT_FOUND: Cannot find package 'dsh-commandcode-provider'`** — the patch row's `name` is the bare package name, but the loader imports it as a module from the profile's `node_modules`, where pnpm only links the scoped name `@mars-sea/dsh-commandcode-provider`. Pre-0.1.7 bundles shipped this wrong row, and the bug also bites when an old `cordis.patch.yml` example (or a cached profile layer) is copied by hand. Fix the row in your profile's `cordis.patch.yml` (or re-add the plugin) so it reads `name: "@mars-sea/dsh-commandcode-provider"` — note the **quotes**: an unquoted `@`-prefixed scalar fails YAML parsing (0.1.7 shipped that regression; 0.1.8 quotes it) — then restart.
|
|
142
181
|
- **`MODEL_NOT_IN_PLAN` (403)** — the selected model is not in your Command Code plan. Pick an open-weight model (e.g. `deepseek/deepseek-v4-flash`) or upgrade. The error names the model and links the official docs.
|
|
143
182
|
- **`MISSING_CREDENTIAL`** — no key anywhere. Store one via the Models page card, export `COMMANDCODE_API_KEY`, set `config.apiKey`, or run `command-code login`. The route stays registered and the catalog stays browsable without a key.
|
|
144
183
|
- **The Models page card shows "not configured" but requests work** — the key came from `~/.commandcode/auth.json` (the `cmd login` fallback), not the dsh credential store. Paste it into the card once to make the card show as configured; both coexist fine.
|
|
145
184
|
- **A reasoning model returns no visible text on short requests** — reasoning models (e.g. `deepseek/deepseek-v4-*`) consume output tokens on reasoning first; a small `maxTokens` can be exhausted before any visible text. This is normal.
|
|
146
|
-
- **`allowBuilds` errors on `dsh plugin add` from git** — copy the exact package key pnpm printed (with the commit hash) into `pnpm-workspace.yaml` and re-run (see [Install](#from-github
|
|
185
|
+
- **`allowBuilds` errors on `dsh plugin add` from git** — copy the exact package key pnpm printed (with the commit hash) into `pnpm-workspace.yaml` and re-run (see [Install](#from-github)).
|
|
147
186
|
|
|
148
187
|
## Notes & limitations
|
|
149
188
|
|
|
150
|
-
- **
|
|
189
|
+
- **Image input is model-gated**: only models the official Command Code registry lists with Vision accept images (see the `KNOWN_IMAGE_MODELS` snapshot in `src/adapter.ts`, synced from the [official model registry](https://commandcode.ai/docs/reference/cli/models)). The model picker annotates each Command Code model with *"Supports image input"* / *"Text only"*, so the capability is visible before you switch. Sending an image to a text-only model throws `UNSUPPORTED_CONTENT`. Command Code's own CLI falls back to a client-side *VISION* side-call for text-only models; this adapter does **not** reproduce that interactive feature — switch to a Vision-capable model instead. Image input also requires the dsh **attachment service** (`ctx.attachments`); without it, requests carrying images throw `UNSUPPORTED_CONTENT`.
|
|
190
|
+
- **Switching to a text-only model in an image-bearing session is rejected by dsh itself** — a harness-level guard (`dsh-host-apiproxy`'s `selectModel` handler) refuses `model-unavailable` when the session history or the pending input already contains images and the target model does not declare `image` input. The rejection is intentional and cannot be relaxed from the plugin side (the picker rows this adapter provides are the input that makes the guard work — a text-only model correctly reports `inputModalities: ['text']`). What this bundle **does** do is make the message friendlier: its client half wraps `session.selectModel` and rewrites that rejection to `当前会话已包含图片,而模型 <model> 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。` (the error code and details pass through unchanged, so any caller switching on `error.code` keeps working). To keep using images, select a model the picker annotates *"Supports image input"*, or remove the images from the session first; alternatively an image-routing bundle (e.g. `@deepseek-ai/dsh-llm-image-routing`) can transparently route image turns to a vision fallback.
|
|
151
191
|
- **No `stop` sequences**: the wire format has no stop field; requests carrying one throw `UNSUPPORTED_OPTION`.
|
|
152
192
|
- Reasoning blocks are **not replayed** into later turns (matches the official CLI: prior private reasoning must not leak).
|
|
153
193
|
- Only tool calls with a paired tool result are replayed into the conversation.
|
|
@@ -173,7 +213,7 @@ This plugin operates entirely within your dsh profile and your Command Code acco
|
|
|
173
213
|
- **Uninstall** completely:
|
|
174
214
|
|
|
175
215
|
```sh
|
|
176
|
-
dsh plugin --profile web remove dsh-commandcode-provider
|
|
216
|
+
dsh plugin --profile web remove @mars-sea/dsh-commandcode-provider
|
|
177
217
|
```
|
|
178
218
|
|
|
179
219
|
This removes the bundle dependency and its layer. Your API key in the dsh credential store and `~/.commandcode/auth.json` are left untouched (you can remove them manually if you want to revoke access).
|
package/README.zh-CN.md
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
- **Models 页面卡片**("Command Code")带 API key 输入框——凭据通过 dsh 凭据服务存储,与 DeepSeek 卡片一致。
|
|
18
18
|
- **API key 解析顺序**:`config.apiKey` → 凭据引用 `apiKeyEnv`(Web Models 页面写入,默认 `COMMANDCODE_API_KEY`)→ 启动环境变量 → 官方 Command Code CLI 认证文件(`~/.commandcode/auth.json`,由 `command-code login` 写入)。
|
|
19
19
|
- **推理强度(reasoning-effort)支持**:针对 Command Code 目录中标为推理模型的模型(如 `claude-opus-5`、`gpt-5.5`、`deepseek/deepseek-v4-pro` 等),通过 `KNOWN_EFFORTS` 实现,与官方 command-code@1.26.0 内置目录一致。
|
|
20
|
+
- **支持视觉模型的图片输入**:官方注册表中带 Vision 能力的模型(如 `claude-sonnet-5`、`gpt-5.4`、`google/gemini-3.5-flash` 等)可接收附加图片——通过 dsh 附件服务解析字节,并以官方 Command Code wire 格式发送。纯文本模型(如 `deepseek/deepseek-v4-flash`、`zai-org/GLM-5.3`)会明确拒绝图片而非静默丢弃。
|
|
20
21
|
|
|
21
22
|
## 获取 API key
|
|
22
23
|
|
|
@@ -31,11 +32,19 @@ cmd login # macOS/Linux;Windows 原生版:cmdc login
|
|
|
31
32
|
|
|
32
33
|
## 安装
|
|
33
34
|
|
|
34
|
-
### 从
|
|
35
|
+
### 从 npm 安装(推荐)
|
|
36
|
+
|
|
37
|
+
插件发布在 npm 上,包名 **`@mars-sea/dsh-commandcode-provider`**(npm 上裸名 `dsh-commandcode-provider` 已被无关包占用):
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 从 GitHub 安装
|
|
35
44
|
|
|
36
45
|
```sh
|
|
37
46
|
# 推荐:锁定发布 tag(可读、不可变)
|
|
38
|
-
dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.
|
|
47
|
+
dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.8
|
|
39
48
|
# 或按完整 commit SHA 锁定任意提交
|
|
40
49
|
dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#<完整-commit-sha>
|
|
41
50
|
```
|
|
@@ -46,19 +55,11 @@ git 安装会拉取**源码**,因此包的 `prepare` 脚本会在安装后构
|
|
|
46
55
|
|
|
47
56
|
```yaml
|
|
48
57
|
allowBuilds:
|
|
49
|
-
'dsh-commandcode-provider@github:Mars-Sea/dsh-commandcode-provider#<完整-commit-sha>': true
|
|
58
|
+
'@mars-sea/dsh-commandcode-provider@github:Mars-Sea/dsh-commandcode-provider#<完整-commit-sha>': true
|
|
50
59
|
```
|
|
51
60
|
|
|
52
61
|
然后重新运行 `add`。只允许信任其源码的包(并固定 commit)。
|
|
53
62
|
|
|
54
|
-
### 从 npm 安装
|
|
55
|
-
|
|
56
|
-
发布为 **`@mars-sea/dsh-commandcode-provider`**(npm 上裸名 `dsh-commandcode-provider` 已被无关包占用):
|
|
57
|
-
|
|
58
|
-
```sh
|
|
59
|
-
dsh plugin --profile web add @mars-sea/dsh-commandcode-provider
|
|
60
|
-
```
|
|
61
|
-
|
|
62
63
|
### 从本地检出安装
|
|
63
64
|
|
|
64
65
|
```sh
|
|
@@ -71,23 +72,51 @@ dsh plugin --profile web add /path/to/dsh-commandcode-provider
|
|
|
71
72
|
|
|
72
73
|
### 安装做了什么
|
|
73
74
|
|
|
74
|
-
`dsh plugin add`
|
|
75
|
+
`dsh plugin add` 会将包链接到配置目录(pnpm 按**真实包名**记录依赖并链接 `node_modules`,即 `@mars-sea/dsh-commandcode-provider`),把同名包名追加到配置的 `dsh.profile.bundles`,并激活 `cordis.patch.yml` 层,其中插入:
|
|
75
76
|
|
|
76
77
|
```yaml
|
|
77
78
|
- insert:
|
|
78
79
|
- id: llm-commandcode
|
|
79
|
-
name: dsh-commandcode-provider
|
|
80
|
+
name: "@mars-sea/dsh-commandcode-provider"
|
|
80
81
|
config:
|
|
81
82
|
apiKeyEnv: COMMANDCODE_API_KEY
|
|
82
83
|
```
|
|
83
84
|
|
|
85
|
+
patch 行里的 `name` 必须是**带引号的完整包名**:loader 会把它当作模块从 profile 的 `node_modules` 导入,而 pnpm 只会链接带 scope 的名字。写成裸名 `dsh-commandcode-provider` 会报 `ERR_MODULE_NOT_FOUND` 并在启动时崩溃;不引号的 `@mars-sea/...` 也会导致 YAML 解析失败(见[故障排查](#故障排查))。
|
|
86
|
+
|
|
84
87
|
验证合成后的层,然后(重新)启动 Web 应用:
|
|
85
88
|
|
|
86
89
|
```sh
|
|
87
|
-
dsh --profile web --dump-config # 会显示 "# == dsh-commandcode-provider" 层
|
|
90
|
+
dsh --profile web --dump-config # 会显示 "# == @mars-sea/dsh-commandcode-provider" 层
|
|
88
91
|
dsh web # 或重启你正在运行的实例
|
|
89
92
|
```
|
|
90
93
|
|
|
94
|
+
## 更新
|
|
95
|
+
|
|
96
|
+
bundle 的 patch 层在每次启动时都从**已安装的包**读取,所以更新包本身就会带入修复后的 patch 行——**不需要**手工改 `cordis.patch.yml`(除非你把它的内容复制到了自己 profile 的层里)。
|
|
97
|
+
|
|
98
|
+
按安装方式选择更新命令:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
# 从 npm 安装(推荐):总是升到最新发布版本
|
|
102
|
+
dsh plugin --profile web update @mars-sea/dsh-commandcode-provider
|
|
103
|
+
|
|
104
|
+
# 从 GitHub 按 tag 安装:指向新 tag
|
|
105
|
+
# (无需先卸载——pnpm 会就地替换固定的版本,下次启动时 bundle 层会从新安装的包重新读取)
|
|
106
|
+
dsh plugin --profile web add github:Mars-Sea/dsh-commandcode-provider#v0.1.8
|
|
107
|
+
|
|
108
|
+
# 从本地检出安装:拉取新代码、重新构建、重启
|
|
109
|
+
git -C /path/to/dsh-commandcode-provider pull
|
|
110
|
+
npm run build --prefix /path/to/dsh-commandcode-provider
|
|
111
|
+
dsh web
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
然后重启 Web 应用(`dsh web`,或重启服务)。用 `dsh --profile web --dump-config` 验证运行的版本——层里应显示 `name: '@mars-sea/dsh-commandcode-provider'`。
|
|
115
|
+
|
|
116
|
+
> **从 ≤0.1.6 升级**(或手改坏的 profile):安装包自带的 patch 层现在已经带着修正后的带引号 `name`。如果你之前**手工复制**过旧的 patch 行到你 profile 自己的 `cordis.patch.yml`,那份拷贝会覆盖 bundle 层——请手动改成 `name: "@mars-sea/dsh-commandcode-provider"`(见[故障排查](#故障排查)),或删掉它让 bundle 层生效。
|
|
117
|
+
|
|
118
|
+
> **想卸载而不是升级**(例如正卡在 0.1.7 之前坏掉的 tag,想干净重来):`dsh plugin --profile web remove @mars-sea/dsh-commandcode-provider`(用 **scoped 名**——pnpm 按真实包名记录依赖,裸名 `dsh-commandcode-provider` 对不上)。这会移除依赖及其配置层;你在 dsh 凭据库和 `~/.commandcode/auth.json` 里的 API key 不受影响。然后用上面的 npm 或 GitHub 命令安装当前版本。
|
|
119
|
+
|
|
91
120
|
## 验证是否生效
|
|
92
121
|
|
|
93
122
|
重启后,在 Web UI 中:**设置 → Models** 会显示 **Command Code** 卡片;模型选择器会在 **commandcode** 下列出实时目录(撰写本文时有 54 个模型)。发送一条消息,选择你套餐中包含的模型——默认的 `deepseek/deepseek-v4-flash` 适用于入门级套餐;开放权重模型(DeepSeek/Qwen/Kimi/MiniMax)通常都可用,而前沿模型(Claude/GPT/Gemini/Grok)可能需要 Pro/Max 套餐或按需计费(见 FAQ)。
|
|
@@ -133,21 +162,31 @@ llm-commandcode:
|
|
|
133
162
|
apiBase: https://api.commandcode.ai
|
|
134
163
|
workingDir: /path/to/project # 上报给 API(项目 slug、配置块)
|
|
135
164
|
modelsCachePath: ~/.commandcode/models-cache.json
|
|
165
|
+
requestTimeoutMs: 60000 # 等待首个响应字节的最长时间(默认 60s)
|
|
166
|
+
streamIdleTimeoutMs: 120000 # 流停顿超过该时长即视为死连接(默认 120s)
|
|
136
167
|
```
|
|
137
168
|
|
|
138
169
|
组合入口配置(`cordis.patch.yml` / 你 profile 的 `cordis.patch.yml`)接受相同的键;那里的字面量 `apiKey` 优先于凭据引用。
|
|
139
170
|
|
|
140
171
|
## 故障排查
|
|
141
172
|
|
|
173
|
+
- **`Command Code API request to .../alpha/generate failed`,且每次会话都在重试(看到"重试延迟")** ——这是**传输层失败**:`fetch()` 根本没拿到 HTTP 响应(不是 401/403/429,那些会显示 "API error")。dsh 的重试策略会重试 `TRANSPORT` 两次(指数退避),所以 UI 里会先出现重试行,最终才失败。0.1.8 起失败原因会显示**真实根因**(例如 `fetch failed: connect ECONNREFUSED`、`ENOTFOUND`、`CERT_HAS_EXPIRED`、`The operation was aborted due to timeout`)。常见原因:
|
|
174
|
+
- **你的网络需要代理**。Node 的 `fetch`(undici)**不读取** `HTTP_PROXY`/`HTTPS_PROXY` 环境变量,所以浏览器/curl 走系统代理能通,而 dsh 进程直连失败。需要给 dsh 配置 undici 代理(例如 `NODE_OPTIONS=--import undici` + dispatcher,或网络层路由),或把 `api.commandcode.ai` 加入白名单。
|
|
175
|
+
- **连接被中途重置/限速**(防火墙、GFW 类干扰、Wi-Fi 不稳)。错误消息会点名(`socket hang up`、`ECONNRESET`、`UND_ERR_SOCKET`)。
|
|
176
|
+
- **TLS 被中间人替换**(企业 MITM)——错误链里出现 `CERT_HAS_EXPIRED`/`DEPTH_ZERO_SELF_SIGNED_CERT`。
|
|
177
|
+
- 也可能是瞬时抖动,重试能恢复;如果每轮都失败,那是环境问题而非 API 问题(健康网络下 models 端点和 generate 端点都能正常响应)。
|
|
178
|
+
- **长回答生成到一半中断** ——0.1.8 起,adapter 会在 `requestTimeoutMs`(默认 60s)内拿不到响应时中止请求,并在流停顿超过 `streamIdleTimeoutMs`(默认 120s)时判定为死连接,不再无限挂起。这两种失败都以 `TIMEOUT` 呈现并附带停顿时长;如果你的网络慢但稳定,可以在 `llm-commandcode` 设置里调大这两个值。
|
|
179
|
+
- **Web 应用启动即崩溃,报 `ERR_MODULE_NOT_FOUND: Cannot find package 'dsh-commandcode-provider'`** ——patch 行的 `name` 写成了裸包名,但 loader 会把它当作模块从 profile 的 `node_modules` 导入,而 pnpm 只会链接带 scope 的名字 `@mars-sea/dsh-commandcode-provider`。0.1.7 之前的 bundle 自带的就是这行错误配置;手工复制旧的 `cordis.patch.yml` 示例(或缓存层)也会踩中。修复方法:把你 profile 的 `cordis.patch.yml` 中该行改成 `name: "@mars-sea/dsh-commandcode-provider"` ——注意**必须加引号**:不引号的 `@` 开头标量会导致 YAML 解析失败(0.1.7 就带了这处回归,0.1.8 已加引号)——然后重启。
|
|
142
180
|
- **`MODEL_NOT_IN_PLAN` (403)** ——所选模型不在你的 Command Code 套餐内。选择一个开放权重模型(如 `deepseek/deepseek-v4-flash`)或升级套餐。错误信息会指明模型并附官方文档链接。
|
|
143
181
|
- **`MISSING_CREDENTIAL`** ——任何地方都没有 key。通过 Models 页面卡片存储一个、`export COMMANDCODE_API_KEY`、设置 `config.apiKey`,或运行 `command-code login`。没有 key 时路由保持注册、目录保持可浏览。
|
|
144
182
|
- **Models 页面卡片显示"未配置"但请求可用** ——key 来自 `~/.commandcode/auth.json`(`cmd login` 兜底),而不是 dsh 凭据存储。把它粘贴到卡片一次即可让卡片显示为已配置;两者可以共存。
|
|
145
183
|
- **推理模型在短请求下不返回可见文本** ——推理模型(如 `deepseek/deepseek-v4-*`)会先消耗输出 token 进行推理;`maxTokens` 较小时可能在出现可见文本前就用完。这属于正常现象。
|
|
146
|
-
- **git 安装时 `dsh plugin add` 报 `allowBuilds` 错误** ——把 pnpm 打印的确切包 key(含 commit hash)复制到 `pnpm-workspace.yaml` 并重新运行(见[从 GitHub
|
|
184
|
+
- **git 安装时 `dsh plugin add` 报 `allowBuilds` 错误** ——把 pnpm 打印的确切包 key(含 commit hash)复制到 `pnpm-workspace.yaml` 并重新运行(见[从 GitHub 安装](#从-github-安装))。
|
|
147
185
|
|
|
148
186
|
## 注意事项与限制
|
|
149
187
|
|
|
150
|
-
-
|
|
188
|
+
- **图片输入按模型能力限制**:只有官方 Command Code 注册表标记为 Vision 的模型接受图片(见 `src/adapter.ts` 中的 `KNOWN_IMAGE_MODELS` 快照,与[官方模型注册表](https://commandcode.ai/docs/reference/cli/models)同步)。模型选择器会为每个 Command Code 模型标注 *"Supports image input"* / *"Text only"*,切换前即可看出能力。向纯文本模型发送图片会抛出 `UNSUPPORTED_CONTENT`。官方 CLI 对纯文本模型会回退到客户端 *VISION* 副调用转文字;本适配器**不**复现该交互功能——请改用支持 Vision 的模型。图片输入还需要 dsh 的**附件服务**(`ctx.attachments`);缺失时携带图片的请求会抛出 `UNSUPPORTED_CONTENT`。
|
|
189
|
+
- **在含图片的会话里切换到纯文本模型会被 dsh 自身拒绝**——这是 harness 层的守卫(`dsh-host-apiproxy` 的 `selectModel` 处理器):当会话历史或待处理输入已包含图片、而目标模型未声明 `image` 输入时,会返回 `model-unavailable`。该拒绝是刻意设计,无法从插件侧放宽(适配器提供的模型行正是让守卫生效的输入——纯文本模型如实上报 `inputModalities: ['text']`)。本 bundle **能**做的是让提示更友好:它的客户端插件会包装 `session.selectModel`,把这条拒绝改写为「当前会话已包含图片,而模型 `<model>` 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。」(错误码与 details 原样透传,按 `error.code` 分支的调用方不受影响)。要继续使用图片,请选择选择器中标注 *"Supports image input"* 的模型,或先清空会话中的图片;也可安装图片路由 bundle(如 `@deepseek-ai/dsh-llm-image-routing`)把图片轮透明路由到视觉回退模型。
|
|
151
190
|
- **不支持 `stop` 序列**:线上格式没有 stop 字段;携带它的请求会抛出 `UNSUPPORTED_OPTION`。
|
|
152
191
|
- 推理块**不会**重放到后续轮次(与官方 CLI 一致:先前的私有推理不得泄漏)。
|
|
153
192
|
- 只有带配对工具结果的工具调用会被重放到对话中。
|
|
@@ -173,7 +212,7 @@ llm-commandcode:
|
|
|
173
212
|
- **完全卸载**:
|
|
174
213
|
|
|
175
214
|
```sh
|
|
176
|
-
dsh plugin --profile web remove dsh-commandcode-provider
|
|
215
|
+
dsh plugin --profile web remove @mars-sea/dsh-commandcode-provider
|
|
177
216
|
```
|
|
178
217
|
|
|
179
218
|
这会移除 bundle 依赖及其配置层。你在 dsh 凭据库和 `~/.commandcode/auth.json` 中的 API key 不会被改动(如需撤销访问权限,可手动删除)。
|
package/cordis.patch.yml
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# dsh-commandcode-provider bundle layer.
|
|
2
2
|
# One plugin row: registers the `commandcode` provider route and its
|
|
3
3
|
# Models-page card. All fields are optional — schema defaults apply.
|
|
4
|
+
#
|
|
5
|
+
# `name` must be the FULL package specifier as installed in the profile's
|
|
6
|
+
# node_modules: the loader imports it as a module and pnpm links packages by
|
|
7
|
+
# their true (scoped) name, so a bare `dsh-commandcode-provider` here fails to
|
|
8
|
+
# resolve (ERR_MODULE_NOT_FOUND) and crashes the app on boot. Quote the value:
|
|
9
|
+
# a YAML scalar starting with `@` is parsed as a directive/indicator and fails
|
|
10
|
+
# to parse.
|
|
4
11
|
- insert:
|
|
5
12
|
- id: llm-commandcode
|
|
6
|
-
name: dsh-commandcode-provider
|
|
13
|
+
name: "@mars-sea/dsh-commandcode-provider"
|
|
7
14
|
config:
|
|
8
15
|
apiKeyEnv: COMMANDCODE_API_KEY
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@mars-sea/dsh-commandcode-provider",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
//#region src/client/index.ts
|
|
8
|
+
/** Whether a selectModel rejection is the harness's image-session gate. */
|
|
9
|
+
function isImageSessionRejection(result) {
|
|
10
|
+
return !result.result.ok && result.result.error.code === "model-unavailable" && result.result.error.message.includes("does not accept image input");
|
|
11
|
+
}
|
|
12
|
+
/** Wrap the shared sessions API so selectModel failures read friendlier. */
|
|
13
|
+
function withFriendlyImageError(sessions) {
|
|
14
|
+
const selectModel = sessions.selectModel.bind(sessions);
|
|
15
|
+
return {
|
|
16
|
+
...sessions,
|
|
17
|
+
selectModel: async (payload, signal) => {
|
|
18
|
+
const result = await selectModel(payload, signal);
|
|
19
|
+
if (!isImageSessionRejection(result)) return result;
|
|
20
|
+
const model = result.result.error.details?.model ?? payload.model;
|
|
21
|
+
return {
|
|
22
|
+
...result,
|
|
23
|
+
result: {
|
|
24
|
+
...result.result,
|
|
25
|
+
error: {
|
|
26
|
+
...result.result.error,
|
|
27
|
+
message: `当前会话已包含图片,而模型 ${model} 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。`
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Client plugin body: install the selectModel wrapper on the connection's
|
|
36
|
+
* shared api. `inject: ['connection']` gates activation until the connection
|
|
37
|
+
* service is provided (the same pattern the harness's own client plugins
|
|
38
|
+
* use), and `connection.api.sessions` is a stable object the model-selection
|
|
39
|
+
* UI reads fresh on every call — so wrapping it once covers both the /model
|
|
40
|
+
* popup and the composer seat, across reconnects.
|
|
41
|
+
*/
|
|
42
|
+
function apply(ctx) {
|
|
43
|
+
const connection = ctx.get("connection");
|
|
44
|
+
if (connection === void 0) return;
|
|
45
|
+
connection.api.sessions = withFriendlyImageError(connection.api.sessions);
|
|
46
|
+
}
|
|
47
|
+
const inject = ["connection"];
|
|
48
|
+
//#endregion
|
|
49
|
+
exports.apply = apply;
|
|
50
|
+
exports.inject = inject;
|
|
51
|
+
exports.isImageSessionRejection = isImageSessionRejection;
|
|
52
|
+
exports.withFriendlyImageError = withFriendlyImageError;
|
|
53
|
+
return module.exports;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../src/client/index.ts"],"sourcesContent":["/**\n * Browser half of the dsh-commandcode-provider bundle.\n *\n * The host rejects switching to a text-only model while the session already\n * contains images with a harness-level `model-unavailable` error\n * (`dsh-host-apiproxy`'s `session.selectModel` handler). That rejection is\n * intentional and cannot be relaxed from the plugin side — the adapter's\n * `inputModalities` is exactly what makes the guard work. What we CAN do is\n * make the error message friendlier: this client plugin wraps the shared\n * `connection.api.sessions.selectModel` face so a `model-unavailable`\n * rejection shows a clear, actionable hint (with the requested model name)\n * instead of the raw English harness message.\n *\n * The wrapper is deliberately narrow: only the `model-unavailable` code is\n * rewritten, only when the message matches the image-session gate, and only\n * the message text changes — the error code and details pass through\n * untouched so any caller that switches on `error.code` keeps working.\n *\n * The wire types are spelled structurally here (not imported from\n * `@deepseek-ai/dsh-host-apiproxy`) so this client bundle does not drag an\n * extra peer dependency into the package; the shapes are stable and the\n * client build inlines them anyway.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\n\n/** The `model-unavailable` error details: provider + model id. */\ninterface ModelUnavailableDetails {\n provider: string\n model: string\n}\n\n/** The narrow slice of the RPC error we need to inspect and rewrite. */\ninterface RpcErrorLike {\n code: string\n message: string\n details?: ModelUnavailableDetails\n}\n\n/**\n * The narrow slice of a unary RPC result we need to inspect and rewrite.\n * The wire shape from `sessions.selectModel` (via `AbstractApiClient.callUnary`)\n * is the full envelope `{ rpcId, result: { ok, error? } }` — the error lives\n * under `result.result`, not at the top level. `RpcResultLike` models that.\n */\ninterface RpcResultLike {\n rpcId: string\n result:\n | { ok: true; value?: unknown }\n | { ok: false; error: RpcErrorLike }\n}\n\n/** One selectModel call: payload in, envelope out. */\ntype SelectModelCall = (\n payload: { sessionId: string; provider: string; model: string; reasoningEffort?: string },\n signal?: AbortSignal,\n) => Promise<RpcResultLike>\n\n/** The shared sessions wire face we wrap. */\ninterface SessionsLike {\n selectModel: SelectModelCall\n}\n\n/** The connection handle shape we read `api.sessions` from. */\ninterface ConnectionLike {\n api: { sessions: SessionsLike }\n}\n\n/** Whether a selectModel rejection is the harness's image-session gate. */\nexport function isImageSessionRejection(\n result: RpcResultLike,\n): result is RpcResultLike & { result: { ok: false; error: RpcErrorLike } } {\n return (\n !result.result.ok &&\n result.result.error.code === 'model-unavailable' &&\n result.result.error.message.includes('does not accept image input')\n )\n}\n\n/** Wrap the shared sessions API so selectModel failures read friendlier. */\nexport function withFriendlyImageError(sessions: SessionsLike): SessionsLike {\n const selectModel = sessions.selectModel.bind(sessions)\n return {\n ...sessions,\n selectModel: async (payload, signal) => {\n const result = await selectModel(payload, signal)\n if (!isImageSessionRejection(result)) return result\n const model = result.result.error.details?.model ?? payload.model\n return {\n ...result,\n result: {\n ...result.result,\n error: {\n ...result.result.error,\n message:\n `当前会话已包含图片,而模型 ${model} 不支持图片输入;`\n + '请选择支持图片的模型,或先移除会话中的图片。',\n },\n },\n }\n },\n }\n}\n\n/**\n * Client plugin body: install the selectModel wrapper on the connection's\n * shared api. `inject: ['connection']` gates activation until the connection\n * service is provided (the same pattern the harness's own client plugins\n * use), and `connection.api.sessions` is a stable object the model-selection\n * UI reads fresh on every call — so wrapping it once covers both the /model\n * popup and the composer seat, across reconnects.\n */\nexport function apply(ctx: Context): void {\n const connection = ctx.get('connection') as ConnectionLike | undefined\n if (connection === undefined) return\n connection.api.sessions = withFriendlyImageError(connection.api.sessions)\n}\n\nexport const inject: readonly string[] = ['connection']\n"],"mappings":";;;;;;;;EAqEA,SAAgB,wBACd,QAC0E;GAC1E,OACE,CAAC,OAAO,OAAO,MACf,OAAO,OAAO,MAAM,SAAS,uBAC7B,OAAO,OAAO,MAAM,QAAQ,SAAS,6BAA6B;EAEtE;;EAGA,SAAgB,uBAAuB,UAAsC;GAC3E,MAAM,cAAc,SAAS,YAAY,KAAK,QAAQ;GACtD,OAAO;IACL,GAAG;IACH,aAAa,OAAO,SAAS,WAAW;KACtC,MAAM,SAAS,MAAM,YAAY,SAAS,MAAM;KAChD,IAAI,CAAC,wBAAwB,MAAM,GAAG,OAAO;KAC7C,MAAM,QAAQ,OAAO,OAAO,MAAM,SAAS,SAAS,QAAQ;KAC5D,OAAO;MACL,GAAG;MACH,QAAQ;OACN,GAAG,OAAO;OACV,OAAO;QACL,GAAG,OAAO,OAAO;QACjB,SACE,iBAAiB,MAAM;OAE3B;MACF;KACF;IACF;GACF;EACF;;;;;;;;;EAUA,SAAgB,MAAM,KAAoB;GACxC,MAAM,aAAa,IAAI,IAAI,YAAY;GACvC,IAAI,eAAe,KAAA,GAAW;GAC9B,WAAW,IAAI,WAAW,uBAAuB,WAAW,IAAI,QAAQ;EAC1E;EAEA,MAAa,SAA4B,CAAC,YAAY"}
|
package/lib/index.d.ts
CHANGED
|
@@ -2,13 +2,34 @@ import z from "@deepseek-ai/schemastery";
|
|
|
2
2
|
import { GenerateOptions, LlmAdapter, LlmModelInfo, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk } from "@deepseek-ai/dsh-llm";
|
|
3
3
|
import { CredentialRef } from "@deepseek-ai/dsh-credentials";
|
|
4
4
|
import { Context } from "@deepseek-ai/cordis";
|
|
5
|
+
import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
|
|
5
6
|
import { CommandDefinition } from "@deepseek-ai/dsh-commands";
|
|
6
7
|
//#region src/adapter.d.ts
|
|
7
8
|
declare const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>>;
|
|
9
|
+
/**
|
|
10
|
+
* Models whose Capabilities include Vision, per the official Command Code
|
|
11
|
+
* model registry (`https://commandcode.ai/docs/reference/cli/models`, generated
|
|
12
|
+
* from the same registry as `cmd --list-models` / the `/model` picker).
|
|
13
|
+
*
|
|
14
|
+
* The Provider API does not expose modality metadata, so this snapshot is the
|
|
15
|
+
* source of truth for image-input gating. Command Code's own CLI falls back to
|
|
16
|
+
* a client-side VISION side-call for text-only models; this adapter does not
|
|
17
|
+
* reproduce that interactive feature, so images sent to a model outside this
|
|
18
|
+
* list are refused loudly (`UNSUPPORTED_CONTENT`) instead of being dropped or
|
|
19
|
+
* sent to a model that cannot read them.
|
|
20
|
+
*
|
|
21
|
+
* Keep in sync with the official registry when new models ship (see the
|
|
22
|
+
* dsh-commandcode-upstream skill).
|
|
23
|
+
*/
|
|
24
|
+
declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
|
|
8
25
|
declare const COMMAND_CODE_CLI_VERSION = "1.26.0";
|
|
9
26
|
declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
10
27
|
declare const DEFAULT_GENERATE_MAX_TOKENS = 64000;
|
|
11
28
|
declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
29
|
+
/** Head-of-request timeout: how long to wait for the first response byte. */
|
|
30
|
+
declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
|
|
31
|
+
/** Stream idle timeout: a generation that stalls this long is a dead connection. */
|
|
32
|
+
declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 120000;
|
|
12
33
|
declare function projectSlugFromPath(pathName: string): string;
|
|
13
34
|
/** Read a usable Command Code credential from the official CLI auth file. */
|
|
14
35
|
declare function resolveAuthFileApiKey(): string | undefined;
|
|
@@ -20,7 +41,17 @@ interface CommandCodeConnectionOptions {
|
|
|
20
41
|
workingDir: string;
|
|
21
42
|
/** Model catalog cache path. */
|
|
22
43
|
modelsCachePath: string;
|
|
44
|
+
/** Milliseconds to wait for the generate response's first byte (default 60s). */
|
|
45
|
+
requestTimeoutMs: number;
|
|
46
|
+
/** Milliseconds a stream may stall before it is treated as a dead connection (default 120s). */
|
|
47
|
+
streamIdleTimeoutMs: number;
|
|
23
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the durable attachment service, or undefined when the host does not
|
|
51
|
+
* provide one. Called lazily only when a request actually carries images, so a
|
|
52
|
+
* text-only request never depends on the attachment seam.
|
|
53
|
+
*/
|
|
54
|
+
type ResolveAttachments = () => AttachmentStore | undefined;
|
|
24
55
|
/** Everything the adapter needs beyond the request itself. */
|
|
25
56
|
interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
|
|
26
57
|
/** Resolve the current connection facts (fresh per request, settings-aware). */
|
|
@@ -29,6 +60,8 @@ interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = Comman
|
|
|
29
60
|
resolveApiKey: (connection: C) => Promise<string>;
|
|
30
61
|
/** HTTP transport override (tests); defaults to the global `fetch`. */
|
|
31
62
|
fetchImpl?: typeof fetch;
|
|
63
|
+
/** Resolve the optional durable attachment service for image input (tests); defaults to none. */
|
|
64
|
+
resolveAttachments?: ResolveAttachments;
|
|
32
65
|
}
|
|
33
66
|
/** Account identity from `/alpha/whoami`. */
|
|
34
67
|
interface CommandCodeAccount {
|
|
@@ -80,6 +113,7 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
|
|
|
80
113
|
private readonly deps;
|
|
81
114
|
private catalog;
|
|
82
115
|
private readonly fetchImpl;
|
|
116
|
+
private readonly resolveAttachments;
|
|
83
117
|
constructor(deps: CommandCodeAdapterDeps<C>);
|
|
84
118
|
/**
|
|
85
119
|
* Command Code is a metered subscription API: 429 (rate limit) and 5xx
|
|
@@ -141,6 +175,10 @@ interface Config {
|
|
|
141
175
|
workingDir?: string;
|
|
142
176
|
/** Model catalog cache path; defaults to `~/.commandcode/models-cache.json`. */
|
|
143
177
|
modelsCachePath?: string;
|
|
178
|
+
/** Milliseconds to wait for the generate response's first byte; defaults to 60s. */
|
|
179
|
+
requestTimeoutMs?: number;
|
|
180
|
+
/** Milliseconds a stream may stall before being treated as a dead connection; defaults to 120s. */
|
|
181
|
+
streamIdleTimeoutMs?: number;
|
|
144
182
|
}
|
|
145
183
|
declare const Config: z<Config>;
|
|
146
184
|
/** One resolution's complete request facts: connection plus credential reference. */
|
|
@@ -156,5 +194,5 @@ interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {
|
|
|
156
194
|
declare function resolveAdapterOptions(config: Config): ResolvedCommandCodeOptions;
|
|
157
195
|
declare function apply(ctx: Context, config: Config): void;
|
|
158
196
|
//#endregion
|
|
159
|
-
export { COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, type CommandCodeAdapterDeps, type CommandCodeCommandDeps, type CommandCodeConnectionOptions, type CommandCodeUsageReport, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, KNOWN_EFFORTS, PROVIDER, ResolvedCommandCodeOptions, apply, applyCommands, commandDefinition, inject, name, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey };
|
|
197
|
+
export { COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, type CommandCodeAdapterDeps, type CommandCodeCommandDeps, type CommandCodeConnectionOptions, type CommandCodeUsageReport, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, PROVIDER, type ResolveAttachments, ResolvedCommandCodeOptions, apply, applyCommands, commandDefinition, inject, name, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey };
|
|
160
198
|
//# sourceMappingURL=index.d.ts.map
|