@mars-sea/dsh-commandcode-provider 0.1.8 → 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 +10 -0
- package/README.md +41 -12
- package/README.zh-CN.md +40 -12
- package/lib/client.js +57 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +27 -1
- package/lib/index.js +107 -13
- package/lib/index.js.map +1 -1
- package/package.json +17 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ 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
|
+
|
|
7
15
|
## [0.1.8] - 2026-08-15
|
|
8
16
|
|
|
9
17
|
### Fixed
|
|
@@ -16,6 +24,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
16
24
|
|
|
17
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.
|
|
18
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).
|
|
19
29
|
|
|
20
30
|
## [0.1.7] - 2026-08-15
|
|
21
31
|
|
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
|
```
|
|
@@ -51,14 +60,6 @@ allowBuilds:
|
|
|
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
|
|
@@ -90,6 +91,33 @@ dsh --profile web --dump-config # shows a "# == @mars-sea/dsh-commandco
|
|
|
90
91
|
dsh web # or restart your running instance
|
|
91
92
|
```
|
|
92
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
|
+
|
|
93
121
|
## Verify it works
|
|
94
122
|
|
|
95
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).
|
|
@@ -154,11 +182,12 @@ The composition-entry config (`cordis.patch.yml` / your profile `cordis.patch.ym
|
|
|
154
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.
|
|
155
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.
|
|
156
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.
|
|
157
|
-
- **`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)).
|
|
158
186
|
|
|
159
187
|
## Notes & limitations
|
|
160
188
|
|
|
161
|
-
- **
|
|
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.
|
|
162
191
|
- **No `stop` sequences**: the wire format has no stop field; requests carrying one throw `UNSUPPORTED_OPTION`.
|
|
163
192
|
- Reasoning blocks are **not replayed** into later turns (matches the official CLI: prior private reasoning must not leak).
|
|
164
193
|
- Only tool calls with a paired tool result are replayed into the conversation.
|
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
|
```
|
|
@@ -51,14 +60,6 @@ allowBuilds:
|
|
|
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
|
|
@@ -90,6 +91,32 @@ dsh --profile web --dump-config # 会显示 "# == @mars-sea/dsh-command
|
|
|
90
91
|
dsh web # 或重启你正在运行的实例
|
|
91
92
|
```
|
|
92
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
|
+
|
|
93
120
|
## 验证是否生效
|
|
94
121
|
|
|
95
122
|
重启后,在 Web UI 中:**设置 → Models** 会显示 **Command Code** 卡片;模型选择器会在 **commandcode** 下列出实时目录(撰写本文时有 54 个模型)。发送一条消息,选择你套餐中包含的模型——默认的 `deepseek/deepseek-v4-flash` 适用于入门级套餐;开放权重模型(DeepSeek/Qwen/Kimi/MiniMax)通常都可用,而前沿模型(Claude/GPT/Gemini/Grok)可能需要 Pro/Max 套餐或按需计费(见 FAQ)。
|
|
@@ -154,11 +181,12 @@ llm-commandcode:
|
|
|
154
181
|
- **`MISSING_CREDENTIAL`** ——任何地方都没有 key。通过 Models 页面卡片存储一个、`export COMMANDCODE_API_KEY`、设置 `config.apiKey`,或运行 `command-code login`。没有 key 时路由保持注册、目录保持可浏览。
|
|
155
182
|
- **Models 页面卡片显示"未配置"但请求可用** ——key 来自 `~/.commandcode/auth.json`(`cmd login` 兜底),而不是 dsh 凭据存储。把它粘贴到卡片一次即可让卡片显示为已配置;两者可以共存。
|
|
156
183
|
- **推理模型在短请求下不返回可见文本** ——推理模型(如 `deepseek/deepseek-v4-*`)会先消耗输出 token 进行推理;`maxTokens` 较小时可能在出现可见文本前就用完。这属于正常现象。
|
|
157
|
-
- **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-安装))。
|
|
158
185
|
|
|
159
186
|
## 注意事项与限制
|
|
160
187
|
|
|
161
|
-
-
|
|
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`)把图片轮透明路由到视觉回退模型。
|
|
162
190
|
- **不支持 `stop` 序列**:线上格式没有 stop 字段;携带它的请求会抛出 `UNSUPPORTED_OPTION`。
|
|
163
191
|
- 推理块**不会**重放到后续轮次(与官方 CLI 一致:先前的私有推理不得泄漏)。
|
|
164
192
|
- 只有带配对工具结果的工具调用会被重放到对话中。
|
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,9 +2,26 @@ 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;
|
|
@@ -29,6 +46,12 @@ interface CommandCodeConnectionOptions {
|
|
|
29
46
|
/** Milliseconds a stream may stall before it is treated as a dead connection (default 120s). */
|
|
30
47
|
streamIdleTimeoutMs: number;
|
|
31
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;
|
|
32
55
|
/** Everything the adapter needs beyond the request itself. */
|
|
33
56
|
interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
|
|
34
57
|
/** Resolve the current connection facts (fresh per request, settings-aware). */
|
|
@@ -37,6 +60,8 @@ interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = Comman
|
|
|
37
60
|
resolveApiKey: (connection: C) => Promise<string>;
|
|
38
61
|
/** HTTP transport override (tests); defaults to the global `fetch`. */
|
|
39
62
|
fetchImpl?: typeof fetch;
|
|
63
|
+
/** Resolve the optional durable attachment service for image input (tests); defaults to none. */
|
|
64
|
+
resolveAttachments?: ResolveAttachments;
|
|
40
65
|
}
|
|
41
66
|
/** Account identity from `/alpha/whoami`. */
|
|
42
67
|
interface CommandCodeAccount {
|
|
@@ -88,6 +113,7 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
|
|
|
88
113
|
private readonly deps;
|
|
89
114
|
private catalog;
|
|
90
115
|
private readonly fetchImpl;
|
|
116
|
+
private readonly resolveAttachments;
|
|
91
117
|
constructor(deps: CommandCodeAdapterDeps<C>);
|
|
92
118
|
/**
|
|
93
119
|
* Command Code is a metered subscription API: 429 (rate limit) and 5xx
|
|
@@ -168,5 +194,5 @@ interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {
|
|
|
168
194
|
declare function resolveAdapterOptions(config: Config): ResolvedCommandCodeOptions;
|
|
169
195
|
declare function apply(ctx: Context, config: Config): void;
|
|
170
196
|
//#endregion
|
|
171
|
-
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, 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 };
|
|
172
198
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.js
CHANGED
|
@@ -167,6 +167,61 @@ const KNOWN_EFFORTS = {
|
|
|
167
167
|
"max"
|
|
168
168
|
]
|
|
169
169
|
};
|
|
170
|
+
/**
|
|
171
|
+
* Models whose Capabilities include Vision, per the official Command Code
|
|
172
|
+
* model registry (`https://commandcode.ai/docs/reference/cli/models`, generated
|
|
173
|
+
* from the same registry as `cmd --list-models` / the `/model` picker).
|
|
174
|
+
*
|
|
175
|
+
* The Provider API does not expose modality metadata, so this snapshot is the
|
|
176
|
+
* source of truth for image-input gating. Command Code's own CLI falls back to
|
|
177
|
+
* a client-side VISION side-call for text-only models; this adapter does not
|
|
178
|
+
* reproduce that interactive feature, so images sent to a model outside this
|
|
179
|
+
* list are refused loudly (`UNSUPPORTED_CONTENT`) instead of being dropped or
|
|
180
|
+
* sent to a model that cannot read them.
|
|
181
|
+
*
|
|
182
|
+
* Keep in sync with the official registry when new models ship (see the
|
|
183
|
+
* dsh-commandcode-upstream skill).
|
|
184
|
+
*/
|
|
185
|
+
const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
186
|
+
"MiniMaxAI/MiniMax-M3",
|
|
187
|
+
"Qwen/Qwen3.6-Plus",
|
|
188
|
+
"Qwen/Qwen3.7-Flash",
|
|
189
|
+
"Qwen/Qwen3.7-Plus",
|
|
190
|
+
"Qwen/Qwen3.8-Max",
|
|
191
|
+
"claude-fable-5",
|
|
192
|
+
"claude-haiku-4-5-20251001",
|
|
193
|
+
"claude-opus-4-7",
|
|
194
|
+
"claude-opus-4-8",
|
|
195
|
+
"claude-opus-5",
|
|
196
|
+
"claude-sonnet-4-6",
|
|
197
|
+
"claude-sonnet-5",
|
|
198
|
+
"google/gemini-3.1-flash-lite",
|
|
199
|
+
"google/gemini-3.5-flash",
|
|
200
|
+
"google/gemini-3.5-flash-lite",
|
|
201
|
+
"google/gemini-3.6-flash",
|
|
202
|
+
"google/gemini-3.7-flash",
|
|
203
|
+
"gpt-5.3-codex",
|
|
204
|
+
"gpt-5.4",
|
|
205
|
+
"gpt-5.4-mini",
|
|
206
|
+
"gpt-5.5",
|
|
207
|
+
"gpt-5.6-luna",
|
|
208
|
+
"gpt-5.6-sol",
|
|
209
|
+
"gpt-5.6-terra",
|
|
210
|
+
"meta/muse-spark-1.1",
|
|
211
|
+
"meta/muse-spark-1.2",
|
|
212
|
+
"meta/muse-spark-1.2-contributor",
|
|
213
|
+
"moonshotai/Kimi-K2.5",
|
|
214
|
+
"moonshotai/Kimi-K2.6",
|
|
215
|
+
"moonshotai/Kimi-K2.7-Code",
|
|
216
|
+
"moonshotai/Kimi-K2.7-Code-Highspeed",
|
|
217
|
+
"moonshotai/Kimi-K3",
|
|
218
|
+
"sakana/fugu-ultra",
|
|
219
|
+
"stepfun/Step-3.7-Flash",
|
|
220
|
+
"thinkingmachines/inkling",
|
|
221
|
+
"thinkingmachines/inkling-small",
|
|
222
|
+
"xai/grok-4.5",
|
|
223
|
+
"xiaomi/mimo-v2.5"
|
|
224
|
+
]);
|
|
170
225
|
const COMMAND_CODE_CLI_VERSION = "1.26.0";
|
|
171
226
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
172
227
|
const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
|
|
@@ -286,7 +341,24 @@ function hasImageContent(message) {
|
|
|
286
341
|
const check = (blocks) => blocks.some((b) => b.type === "image" || b.type === "tool-result" && check(b.content));
|
|
287
342
|
return check(message.content);
|
|
288
343
|
}
|
|
289
|
-
|
|
344
|
+
/**
|
|
345
|
+
* Convert one image reference to the Command Code wire format, as the official
|
|
346
|
+
* CLI does: `{ type: 'image', source: { type: 'base64', media_type, data } }`.
|
|
347
|
+
* Bytes come from the durable attachment service; the media type is the one
|
|
348
|
+
* verified at save time.
|
|
349
|
+
*/
|
|
350
|
+
async function imageToCommandCode(ref, readImage) {
|
|
351
|
+
const data = await readImage(ref);
|
|
352
|
+
return {
|
|
353
|
+
type: "image",
|
|
354
|
+
source: {
|
|
355
|
+
type: "base64",
|
|
356
|
+
media_type: ref.mediaType,
|
|
357
|
+
data: Buffer.from(data).toString("base64")
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
async function messagesToCC(messages, readImage) {
|
|
290
362
|
const out = [];
|
|
291
363
|
const paired = pairedToolCallIds(messages);
|
|
292
364
|
for (const message of messages) {
|
|
@@ -298,7 +370,10 @@ function messagesToCC(messages) {
|
|
|
298
370
|
type: "text",
|
|
299
371
|
text: block.text
|
|
300
372
|
});
|
|
301
|
-
if (block.type === "image")
|
|
373
|
+
if (block.type === "image") {
|
|
374
|
+
if (!readImage) throw new LlmError("Image input requires the durable attachment service", "UNSUPPORTED_CONTENT");
|
|
375
|
+
parts.push(await imageToCommandCode(block.attachment, readImage));
|
|
376
|
+
}
|
|
302
377
|
}
|
|
303
378
|
out.push({
|
|
304
379
|
role: "user",
|
|
@@ -350,10 +425,12 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
350
425
|
deps;
|
|
351
426
|
catalog = [];
|
|
352
427
|
fetchImpl;
|
|
428
|
+
resolveAttachments;
|
|
353
429
|
constructor(deps) {
|
|
354
430
|
super();
|
|
355
431
|
this.deps = deps;
|
|
356
432
|
this.fetchImpl = deps.fetchImpl ?? fetch;
|
|
433
|
+
this.resolveAttachments = deps.resolveAttachments;
|
|
357
434
|
}
|
|
358
435
|
/**
|
|
359
436
|
* Command Code is a metered subscription API: 429 (rate limit) and 5xx
|
|
@@ -387,21 +464,27 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
387
464
|
return this.catalog;
|
|
388
465
|
}
|
|
389
466
|
async listModels(provider) {
|
|
390
|
-
return (await this.loadCatalog()).map((model) =>
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
467
|
+
return (await this.loadCatalog()).map((model) => {
|
|
468
|
+
const vision = KNOWN_IMAGE_MODELS.has(model.id);
|
|
469
|
+
return {
|
|
470
|
+
provider,
|
|
471
|
+
id: model.id,
|
|
472
|
+
name: `${model.name} (CC)`,
|
|
473
|
+
description: vision ? "Supports image input" : "Text only",
|
|
474
|
+
inputModalities: vision ? ["text", "image"] : ["text"]
|
|
475
|
+
};
|
|
476
|
+
});
|
|
396
477
|
}
|
|
397
478
|
async resolveModel(provider, model, signal) {
|
|
398
479
|
const entry = this.catalog.find((m) => m.id === model) ?? (await this.loadCatalog(signal)).find((m) => m.id === model);
|
|
399
480
|
const efforts = KNOWN_EFFORTS[model];
|
|
481
|
+
const vision = KNOWN_IMAGE_MODELS.has(model);
|
|
400
482
|
return {
|
|
401
483
|
provider,
|
|
402
484
|
id: model,
|
|
403
485
|
name: entry ? `${entry.name} (CC)` : model,
|
|
404
|
-
|
|
486
|
+
description: vision ? "Supports image input" : "Text only",
|
|
487
|
+
inputModalities: vision ? ["text", "image"] : ["text"],
|
|
405
488
|
...entry ? {
|
|
406
489
|
context: { contextWindow: entry.contextWindow },
|
|
407
490
|
defaultMaxTokens: Math.min(entry.maxTokens, DEFAULT_GENERATE_MAX_TOKENS)
|
|
@@ -490,7 +573,14 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
490
573
|
}
|
|
491
574
|
async *stream(options) {
|
|
492
575
|
if (options.stop?.length) throw new LlmError("Command Code adapter does not support stop sequences", "UNSUPPORTED_OPTION");
|
|
493
|
-
|
|
576
|
+
const hasImages = options.messages.some(hasImageContent);
|
|
577
|
+
let readImage;
|
|
578
|
+
if (hasImages) {
|
|
579
|
+
if (!KNOWN_IMAGE_MODELS.has(options.model)) throw new LlmError(`Command Code model "${options.model}" does not support image input; switch to a Vision-capable model (see the model registry)`, "UNSUPPORTED_CONTENT");
|
|
580
|
+
const attachments = this.resolveAttachments?.();
|
|
581
|
+
if (attachments === void 0) throw new LlmError("Command Code image input requires the durable attachment service", "UNSUPPORTED_CONTENT");
|
|
582
|
+
readImage = (ref) => attachments.readImage(ref).then((stored) => stored.data);
|
|
583
|
+
}
|
|
494
584
|
const connection = this.deps.options();
|
|
495
585
|
const apiKey = await this.deps.resolveApiKey(connection);
|
|
496
586
|
const modelMax = this.catalog.find((m) => m.id === options.model)?.maxTokens ?? 65536;
|
|
@@ -516,7 +606,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
516
606
|
skills: null,
|
|
517
607
|
params: {
|
|
518
608
|
model: options.model,
|
|
519
|
-
messages: messagesToCC(options.messages),
|
|
609
|
+
messages: await messagesToCC(options.messages, readImage),
|
|
520
610
|
tools: (options.tools ?? []).map((tool) => ({
|
|
521
611
|
type: "function",
|
|
522
612
|
name: tool.name,
|
|
@@ -947,7 +1037,11 @@ function apply(ctx, config) {
|
|
|
947
1037
|
};
|
|
948
1038
|
const adapter = new CommandCodeAdapter({
|
|
949
1039
|
options,
|
|
950
|
-
resolveApiKey
|
|
1040
|
+
resolveApiKey,
|
|
1041
|
+
resolveAttachments: () => {
|
|
1042
|
+
const attachments = ctx.get("attachments");
|
|
1043
|
+
return attachments === void 0 ? void 0 : attachments;
|
|
1044
|
+
}
|
|
951
1045
|
});
|
|
952
1046
|
ctx.llm.registerConfigurableProviders([{
|
|
953
1047
|
provider: PROVIDER,
|
|
@@ -967,6 +1061,6 @@ function apply(ctx, config) {
|
|
|
967
1061
|
});
|
|
968
1062
|
}
|
|
969
1063
|
//#endregion
|
|
970
|
-
export { COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, 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, PROVIDER, apply, applyCommands, commandDefinition, inject, name, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey };
|
|
1064
|
+
export { COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, 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, apply, applyCommands, commandDefinition, inject, name, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey };
|
|
971
1065
|
|
|
972
1066
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/adapter.ts","../src/commands.ts","../src/index.ts"],"sourcesContent":["/**\n * DeepSeek Harness LLM adapter for the Command Code Provider API.\n *\n * Ported from pi-commandcode-provider@0.5.1 (MIT). This is an unofficial,\n * community-maintained integration; you need your own Command Code account\n * and API key or subscription, and Command Code's terms apply.\n *\n * Wire protocol (reverse-engineered by the pi plugin, command-code@1.26.0):\n * POST {apiBase}/alpha/generate\n * body: { config, memory, taste, skills, params: { model, messages, tools,\n * system, max_tokens, temperature, stream, reasoning_effort? }, threadId }\n * SSE-ish JSONL events: text-delta | reasoning-start/delta/end | tool-call\n * | tool-result | finish | error\n * Model catalog: GET {apiBase}/provider/v1/models -> { object: 'list', data: [...] }\n *\n * The adapter is deliberately free of cordis/schemastery: it receives a\n * per-request options thunk and an API-key resolver from the plugin entry\n * (src/index.ts), so a settings change reaches the very next request.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\nimport { randomUUID } from 'node:crypto'\n\nimport {\n attributionHeaders,\n CallId,\n LlmAdapter,\n LlmError,\n ReasoningEffortId,\n errorChain,\n resolveRetryPolicy,\n type ResolvedRetryPolicy,\n type ContentBlock,\n type FinishReason,\n type GenerateOptions,\n type LlmModelInfo,\n type LlmResolvedModelInfo,\n type Message,\n type StreamChunk,\n type TokenUsage,\n} from '@deepseek-ai/dsh-llm'\n\n// ---------------------------------------------------------------------------\n// Static capability snapshot (from the official command-code@1.26.0 bundled\n// model catalog, dist/cli.mjs). The Provider API does not expose reasoning\n// metadata; models omitted here let Command Code choose their reasoning\n// depth, matching the official CLI.\n// ---------------------------------------------------------------------------\n\nexport const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>> = {\n 'Qwen/Qwen3.8-Max': ['low', 'medium', 'xhigh'],\n 'claude-fable-5': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-opus-4-7': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-opus-4-8': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-opus-5': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-sonnet-4-6': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-sonnet-5': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'deepseek/deepseek-v4-flash': ['high', 'max'],\n 'deepseek/deepseek-v4-pro': ['high', 'max'],\n 'google/gemini-3.1-flash-lite': ['low', 'medium', 'high'],\n 'google/gemini-3.5-flash': ['low', 'medium', 'high'],\n 'google/gemini-3.5-flash-lite': ['low', 'medium', 'high'],\n 'google/gemini-3.6-flash': ['low', 'medium', 'high'],\n 'google/gemini-3.7-flash': ['low', 'medium', 'high'],\n 'gpt-5.3-codex': ['low', 'medium', 'high', 'xhigh'],\n 'gpt-5.4': ['low', 'medium', 'high', 'xhigh'],\n 'gpt-5.4-mini': ['low', 'medium', 'high'],\n 'gpt-5.5': ['low', 'medium', 'high', 'xhigh'],\n 'gpt-5.6-luna': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'gpt-5.6-sol': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'gpt-5.6-terra': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'sakana/fugu-ultra': ['high', 'xhigh'],\n 'xai/grok-4.5': ['low', 'medium', 'high'],\n 'xai/grok-4.6': ['low', 'medium', 'high', 'xhigh'],\n 'zai-org/GLM-5.2': ['high', 'max'],\n 'zai-org/GLM-5.3': ['low', 'high', 'max'],\n}\n\nexport const COMMAND_CODE_CLI_VERSION = '1.26.0'\nexport const DEFAULT_API_BASE = 'https://api.commandcode.ai'\nexport const DEFAULT_GENERATE_MAX_TOKENS = 64_000\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 65_536\nexport const MODELS_TIMEOUT_MS = 10_000\n/** Head-of-request timeout: how long to wait for the first response byte. */\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 60_000\n/** Stream idle timeout: a generation that stalls this long is a dead connection. */\nexport const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 120_000\nconst MODEL_CACHE_VERSION = 1\n\n// ---------------------------------------------------------------------------\n// Small helpers (ported from converters.ts / models.ts)\n// ---------------------------------------------------------------------------\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined\n}\n\nfunction numberValue(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\nfunction recordOrEmpty(value: unknown): Record<string, unknown> {\n if (isRecord(value)) return value\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value)\n if (isRecord(parsed)) return parsed\n } catch {\n // Some providers stream incomplete JSON argument fragments.\n }\n }\n return {}\n}\n\nexport function projectSlugFromPath(pathName: string): string {\n const slug = pathName\n .toLowerCase()\n .replace(/^[a-z]:/i, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n return slug || 'project'\n}\n\nfunction parseStreamEventLine(line: string): unknown | undefined {\n let trimmed = line.trim()\n if (!trimmed || trimmed.startsWith(':') || trimmed.startsWith('event:')) return undefined\n if (trimmed.startsWith('data:')) trimmed = trimmed.slice(5).trim()\n if (!trimmed || trimmed === '[DONE]') return undefined\n try {\n return JSON.parse(trimmed) as unknown\n } catch {\n return undefined\n }\n}\n\n// ---------------------------------------------------------------------------\n// Credential fallback from the official Command Code CLI auth file. Used as\n// the last fallback by the plugin entry, so a user who already logged in with\n// `command-code login` can reuse that credential. Only the official CLI's own\n// file is read — pi/OMP auth files are intentionally not scanned, so their\n// credentials and formats cannot surprise this adapter.\n// ---------------------------------------------------------------------------\n\n/** Extract the key from the CLI's nested credential records (`command-code`). */\nfunction apiKeyFromCredentialRecord(value: unknown): string | undefined {\n if (!isRecord(value)) return undefined\n const type = stringValue(value.type)\n if (type === 'api') return stringValue(value.key)\n if (type === 'oauth') return stringValue(value.access)\n return stringValue(value.key) ?? stringValue(value.access)\n}\n\n/** Read a usable Command Code credential from the official CLI auth file. */\nexport function resolveAuthFileApiKey(): string | undefined {\n const authPath = join(homedir(), '.commandcode', 'auth.json')\n try {\n if (!existsSync(authPath)) return undefined\n const parsed: unknown = JSON.parse(readFileSync(authPath, 'utf-8'))\n if (!isRecord(parsed)) return undefined\n const direct = stringValue(parsed.apiKey) ?? stringValue(parsed.commandcode)\n if (direct) return direct\n const nested =\n apiKeyFromCredentialRecord(parsed.commandcode) ??\n apiKeyFromCredentialRecord(parsed['command-code'])\n return nested\n } catch {\n // Ignore malformed or unreadable auth file.\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Model catalog discovery with on-disk cache fallback (ported from models.ts)\n// ---------------------------------------------------------------------------\n\ninterface CommandCodeModel {\n id: string\n name: string\n contextWindow: number\n maxTokens: number\n}\n\nfunction parseCatalogResponse(value: unknown): CommandCodeModel[] {\n if (!isRecord(value) || value.object !== 'list' || !Array.isArray(value.data)) {\n throw new LlmError('Unexpected Command Code models response shape', 'PROVIDER_PROTOCOL_ERROR')\n }\n const models: CommandCodeModel[] = []\n for (const entry of value.data) {\n if (!isRecord(entry)) continue\n const id = stringValue(entry.id)\n const name = stringValue(entry.name)\n const contextLength = numberValue(entry.context_length)\n if (!id || !name || !contextLength || contextLength <= 0) continue\n models.push({\n id,\n name,\n contextWindow: contextLength,\n maxTokens: Math.min(contextLength, DEFAULT_MAX_OUTPUT_TOKENS),\n })\n }\n if (models.length === 0) {\n throw new LlmError('Command Code returned an empty model catalog', 'PROVIDER_PROTOCOL_ERROR')\n }\n return models\n}\n\nasync function readModelsCache(cachePath: string): Promise<CommandCodeModel[]> {\n const parsed: unknown = JSON.parse(await readFile(cachePath, 'utf-8'))\n if (!isRecord(parsed) || parsed.version !== MODEL_CACHE_VERSION || !Array.isArray(parsed.models)) {\n throw new Error(`Invalid model cache at ${cachePath}`)\n }\n return parsed.models as CommandCodeModel[]\n}\n\nasync function writeModelsCache(cachePath: string, models: CommandCodeModel[]): Promise<void> {\n await mkdir(dirname(cachePath), { recursive: true })\n const tmp = `${cachePath}.${process.pid}.tmp`\n try {\n await writeFile(tmp, `${JSON.stringify({ version: MODEL_CACHE_VERSION, models }, null, 2)}\\n`, {\n encoding: 'utf-8',\n mode: 0o600,\n })\n await rename(tmp, cachePath)\n } finally {\n await rm(tmp, { force: true }).catch(() => undefined)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message conversion: harness Message[] -> Command Code wire messages.\n// Reasoning blocks are intentionally NOT replayed (matches the pi plugin and\n// the official CLI: prior private reasoning must not leak into later turns).\n// Only tool calls with a paired tool result are replayed.\n// ---------------------------------------------------------------------------\n\nfunction pairedToolCallIds(messages: readonly Message[]): Set<string> {\n const callIds = new Set<string>()\n const resultIds = new Set<string>()\n for (const message of messages) {\n for (const block of message.content) {\n if (message.role === 'assistant' && block.type === 'tool-call') callIds.add(block.id)\n if (block.type === 'tool-result') resultIds.add(block.toolCallId)\n }\n }\n return new Set([...callIds].filter((id) => resultIds.has(id)))\n}\n\nfunction blockText(block: ContentBlock): string {\n return block.type === 'text' || block.type === 'reasoning' ? block.text : ''\n}\n\nfunction toolResultText(block: Extract<ContentBlock, { type: 'tool-result' }>): string {\n return block.content.map(blockText).filter(Boolean).join('\\n')\n}\n\nfunction hasImageContent(message: Message): boolean {\n const check = (blocks: readonly ContentBlock[]): boolean =>\n blocks.some(\n (b) => b.type === 'image' || (b.type === 'tool-result' && check(b.content)),\n )\n return check(message.content)\n}\n\nfunction messagesToCC(messages: readonly Message[]): unknown[] {\n const out: unknown[] = []\n const paired = pairedToolCallIds(messages)\n\n for (const message of messages) {\n if (message.role === 'system') continue // folded into params.system by the caller\n\n if (message.role === 'user' && message.source.kind !== 'tool') {\n const parts: unknown[] = []\n for (const block of message.content) {\n if (block.type === 'text') parts.push({ type: 'text', text: block.text })\n if (block.type === 'image') {\n // ImageBlock carries an attachment ref owned by the attachment\n // service; resolving it to bytes requires that service. Fail loudly\n // instead of silently dropping (adapter contract).\n throw new LlmError(\n 'Image input is not wired to the attachment service in this adapter yet',\n 'UNSUPPORTED_CONTENT',\n )\n }\n }\n out.push({ role: 'user', content: parts })\n continue\n }\n\n if (message.role === 'assistant') {\n const parts: unknown[] = []\n for (const block of message.content) {\n if (block.type === 'text') {\n parts.push({ type: 'text', text: block.text })\n } else if (block.type === 'tool-call' && paired.has(block.id)) {\n parts.push({\n type: 'tool-call',\n toolCallId: block.id,\n toolName: block.name,\n input: recordOrEmpty(block.arguments),\n })\n }\n // reasoning blocks: skipped by design (see header comment)\n }\n if (parts.length > 0) out.push({ role: 'assistant', content: parts })\n continue\n }\n\n // tool-result message (user role, single tool-result block)\n if (message.role === 'user' && message.source.kind === 'tool') {\n const block = message.content[0]\n if (!block || block.type !== 'tool-result' || !paired.has(block.toolCallId)) continue\n out.push({\n role: 'tool',\n content: [\n {\n type: 'tool-result',\n toolCallId: block.toolCallId,\n toolName: '',\n output: block.isError\n ? { type: 'error-text', value: toolResultText(block) }\n : { type: 'text', value: toolResultText(block) },\n },\n ],\n })\n }\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\n/** Connection facts resolved fresh per request by the plugin entry. */\nexport interface CommandCodeConnectionOptions {\n /** API base; the Provider API lives under it (`/alpha/generate`, `/provider/v1/models`). */\n apiBase: string\n /** Working directory reported to the API (project slug, config block). */\n workingDir: string\n /** Model catalog cache path. */\n modelsCachePath: string\n /** Milliseconds to wait for the generate response's first byte (default 60s). */\n requestTimeoutMs: number\n /** Milliseconds a stream may stall before it is treated as a dead connection (default 120s). */\n streamIdleTimeoutMs: number\n}\n\n/** Everything the adapter needs beyond the request itself. */\nexport interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {\n /** Resolve the current connection facts (fresh per request, settings-aware). */\n options: () => C\n /** Resolve a usable API key for the given connection facts, or throw `MISSING_CREDENTIAL`. */\n resolveApiKey: (connection: C) => Promise<string>\n /** HTTP transport override (tests); defaults to the global `fetch`. */\n fetchImpl?: typeof fetch\n}\n\n/** Account identity from `/alpha/whoami`. */\nexport interface CommandCodeAccount {\n id: string\n name: string\n userName: string\n}\n\n/** Usage summary from `/alpha/usage/summary`. */\nexport interface CommandCodeUsage {\n totalCount: number\n totalCost: number\n successRate: number\n completedCount: number\n failedCount: number\n totalTokensIn: number\n totalTokensOut: number\n totalCredits: number\n periodBasis: string\n}\n\n/** Credit/limit state from `/alpha/billing/credits`. */\nexport interface CommandCodeCredits {\n monthlyCredits: number\n purchasedCredits: number\n freeCredits: number\n /** Five-hour rolling window limits. */\n fiveHour: { used: number; cap: number; exceeded: boolean; resetAt: number }\n /** Weekly window limits. */\n weekly: { used: number; cap: number; exceeded: boolean; resetAt: number }\n}\n\n/** Everything the usage endpoints report, fetched together. */\nexport interface CommandCodeUsageReport {\n account?: CommandCodeAccount\n usage?: CommandCodeUsage\n credits?: CommandCodeCredits\n /** Endpoint failures degrade the report instead of failing it. */\n failures: string[]\n}\n\nexport class CommandCodeAdapter<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends LlmAdapter {\n private catalog: CommandCodeModel[] = []\n private readonly fetchImpl: typeof fetch\n\n constructor(private readonly deps: CommandCodeAdapterDeps<C>) {\n super()\n this.fetchImpl = deps.fetchImpl ?? fetch\n }\n\n /**\n * Command Code is a metered subscription API: 429 (rate limit) and 5xx\n * (transient server errors) are worth retrying at the agent-step boundary,\n * which is where dsh-llm-retry executes the policy returned here. The\n * default policy already retries `RATE_LIMIT` and `SERVER`; declaring it\n * explicitly documents the intent and gives the plugin entry a stable hook\n * to override (e.g. a stricter cap for a metered plan).\n */\n override providerRetryPolicy(_provider: string): ResolvedRetryPolicy {\n return resolveRetryPolicy(undefined, 'llm-commandcode: retryPolicy')\n }\n\n /** Refresh the catalog (live fetch, cache fallback) and return it. */\n private async loadCatalog(signal?: AbortSignal): Promise<CommandCodeModel[]> {\n const { apiBase, modelsCachePath } = this.deps.options()\n try {\n const response = await this.fetchImpl(`${apiBase}/provider/v1/models`, {\n headers: { accept: 'application/json', ...attributionHeaders() },\n signal: signal ?? AbortSignal.timeout(MODELS_TIMEOUT_MS),\n })\n if (!response.ok) {\n throw new Error(`models endpoint returned ${response.status}`)\n }\n this.catalog = parseCatalogResponse(await response.json())\n await writeModelsCache(modelsCachePath, this.catalog).catch(() => undefined)\n } catch (error) {\n if (signal?.aborted) throw error\n // A catalog refresh failure is a degradation, not a request failure:\n // fall back to the last successful catalog on disk (or the in-memory\n // one from an earlier successful load). The adapter still serves any\n // model the user names; only the advisory selector loses entries.\n this.catalog = await readModelsCache(modelsCachePath).catch(() => this.catalog)\n }\n return this.catalog\n }\n\n override async listModels(provider: string): Promise<readonly LlmModelInfo[]> {\n const catalog = await this.loadCatalog()\n return catalog.map((model) => ({\n provider,\n id: model.id,\n name: `${model.name} (CC)`,\n inputModalities: ['text' as const],\n }))\n }\n\n override async resolveModel(\n provider: string,\n model: string,\n signal?: AbortSignal,\n ): Promise<LlmResolvedModelInfo> {\n const entry =\n this.catalog.find((m) => m.id === model) ??\n (await this.loadCatalog(signal)).find((m) => m.id === model)\n\n const efforts = KNOWN_EFFORTS[model]\n return {\n provider,\n id: model,\n name: entry ? `${entry.name} (CC)` : model,\n inputModalities: ['text' as const],\n ...(entry\n ? {\n context: { contextWindow: entry.contextWindow },\n defaultMaxTokens: Math.min(entry.maxTokens, DEFAULT_GENERATE_MAX_TOKENS),\n }\n : {}),\n // Omit `reasoning` entirely for models without known effort support:\n // the harness then treats the model as having no selectable efforts.\n ...(efforts\n ? {\n reasoning: {\n efforts: efforts.map((effort) => ({\n id: ReasoningEffortId(effort),\n name: effort,\n })),\n },\n }\n : {}),\n }\n }\n\n /**\n * Fetch account, usage, and credit state from the Command Code account\n * endpoints (`/alpha/whoami`, `/alpha/usage/summary`, `/alpha/billing/credits`).\n * Each endpoint degrades independently: a failed one lands in `failures`\n * while the rest still report, so a transient outage never blanks the whole\n * view. Requires a usable API key (throws `MISSING_CREDENTIAL` otherwise).\n */\n async getUsage(): Promise<CommandCodeUsageReport> {\n const connection = this.deps.options()\n const apiKey = await this.deps.resolveApiKey(connection)\n const base = connection.apiBase\n const headers = {\n Authorization: `Bearer ${apiKey}`,\n 'x-command-code-version': COMMAND_CODE_CLI_VERSION,\n 'x-cli-environment': 'production',\n ...attributionHeaders(),\n }\n const failures: string[] = []\n\n const getJson = async (path: string): Promise<Record<string, unknown> | undefined> => {\n try {\n const response = await this.fetchImpl(`${base}${path}`, { headers })\n if (!response.ok) {\n failures.push(`${path}: HTTP ${response.status}`)\n return undefined\n }\n const parsed: unknown = await response.json()\n return isRecord(parsed) ? parsed : undefined\n } catch (error: unknown) {\n failures.push(`${path}: ${error instanceof Error ? error.message : String(error)}`)\n return undefined\n }\n }\n\n const report: CommandCodeUsageReport = { failures }\n\n // whoami -> account identity.\n const whoami = await getJson('/alpha/whoami')\n const whoamiData = whoami && isRecord(whoami.user) ? whoami.user : undefined\n if (whoamiData) {\n report.account = {\n id: stringValue(whoamiData.id) ?? '',\n name: stringValue(whoamiData.name) ?? '',\n userName: stringValue(whoamiData.userName) ?? '',\n }\n }\n\n // usage/summary -> totals.\n const usage = await getJson('/alpha/usage/summary')\n if (usage) {\n report.usage = {\n totalCount: numberValue(usage.totalCount) ?? 0,\n totalCost: numberValue(usage.totalCost) ?? 0,\n successRate: numberValue(usage.successRate) ?? 0,\n completedCount: numberValue(usage.completedCount) ?? 0,\n failedCount: numberValue(usage.failedCount) ?? 0,\n totalTokensIn: numberValue(usage.totalTokensIn) ?? 0,\n totalTokensOut: numberValue(usage.totalTokensOut) ?? 0,\n totalCredits: numberValue(usage.totalCredits) ?? 0,\n periodBasis: stringValue(usage.periodBasis) ?? 'billing-period',\n }\n }\n\n // billing/credits -> credit + window limits.\n const credits = await getJson('/alpha/billing/credits')\n const creditsData = credits && isRecord(credits.credits) ? credits.credits : undefined\n const windowLimits = credits && isRecord(credits.windowLimits) ? credits.windowLimits : undefined\n const fiveHour = windowLimits && isRecord(windowLimits.fiveHour) ? windowLimits.fiveHour : undefined\n const weekly = windowLimits && isRecord(windowLimits.weekly) ? windowLimits.weekly : undefined\n if (creditsData || fiveHour || weekly) {\n report.credits = {\n monthlyCredits: numberValue(creditsData?.monthlyCredits) ?? 0,\n purchasedCredits: numberValue(creditsData?.purchasedCredits) ?? 0,\n freeCredits: numberValue(creditsData?.freeCredits) ?? 0,\n fiveHour: {\n used: numberValue(fiveHour?.used) ?? 0,\n cap: numberValue(fiveHour?.cap) ?? 0,\n exceeded: fiveHour?.exceeded === true,\n resetAt: numberValue(fiveHour?.resetAt) ?? 0,\n },\n weekly: {\n used: numberValue(weekly?.used) ?? 0,\n cap: numberValue(weekly?.cap) ?? 0,\n exceeded: weekly?.exceeded === true,\n resetAt: numberValue(weekly?.resetAt) ?? 0,\n },\n }\n }\n\n return report\n }\n\n async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {\n if (options.stop?.length) {\n // The Command Code wire format has no documented stop field; refuse\n // loudly instead of silently dropping a request field.\n throw new LlmError('Command Code adapter does not support stop sequences', 'UNSUPPORTED_OPTION')\n }\n if (options.messages.some(hasImageContent)) {\n throw new LlmError(\n 'Image input is not wired to the attachment service in this adapter yet',\n 'UNSUPPORTED_CONTENT',\n )\n }\n\n const connection = this.deps.options()\n const apiKey = await this.deps.resolveApiKey(connection)\n const entry = this.catalog.find((m) => m.id === options.model)\n const modelMax = entry?.maxTokens ?? DEFAULT_MAX_OUTPUT_TOKENS\n const maxTokens = Math.min(\n options.maxTokens ?? modelMax,\n modelMax,\n DEFAULT_GENERATE_MAX_TOKENS,\n )\n\n const effort = options.reasoningEffort as string | undefined\n const supported = KNOWN_EFFORTS[options.model]\n const reasoningEffort =\n effort && effort !== 'off' && supported?.includes(effort) ? effort : undefined\n\n const systemText = [\n options.system ?? '',\n ...options.messages\n .filter((m) => m.role === 'system')\n .map((m) => m.content.map(blockText).filter(Boolean).join('\\n')),\n ]\n .filter(Boolean)\n .join('\\n\\n')\n\n const body = {\n config: {\n workingDir: connection.workingDir,\n date: new Date().toISOString().split('T')[0],\n environment: `${process.platform}-${process.arch}, Node.js ${process.version}`,\n structure: [],\n isGitRepo: false,\n currentBranch: '',\n mainBranch: '',\n gitStatus: '',\n recentCommits: [],\n },\n memory: null,\n taste: null,\n skills: null,\n params: {\n model: options.model,\n messages: messagesToCC(options.messages),\n tools: (options.tools ?? []).map((tool) => ({\n type: 'function',\n name: tool.name,\n description: tool.description,\n input_schema: tool.parameters,\n })),\n system: systemText,\n max_tokens: maxTokens,\n temperature: options.temperature ?? 0.3,\n stream: true,\n ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),\n },\n threadId: randomUUID(),\n }\n\n let response: Response\n try {\n response = await this.fetchImpl(`${connection.apiBase}/alpha/generate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n 'x-command-code-version': COMMAND_CODE_CLI_VERSION,\n 'x-cli-environment': 'production',\n 'x-project-slug': projectSlugFromPath(connection.workingDir),\n 'x-taste-learning': 'true',\n 'x-co-flag': 'false',\n ...attributionHeaders(),\n },\n body: JSON.stringify(body),\n // The generation can legitimately run long, but the connection phase\n // must not hang forever: bound the wait for the first response byte.\n signal: options.signal\n ? AbortSignal.any([options.signal, AbortSignal.timeout(connection.requestTimeoutMs)])\n : AbortSignal.timeout(connection.requestTimeoutMs),\n }) } catch (error: unknown) {\n // Caller cancellation must propagate as-is, not be relabeled.\n if (options.signal?.aborted) throw error\n // The timeout signal above aborts with a TimeoutError. Because the\n // caller's own signal was already ruled out, any TimeoutError here came\n // from our request deadline — classify it precisely.\n if (error instanceof DOMException && error.name === 'TimeoutError') {\n throw new LlmError(\n `Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms`\n + `: ${errorChain(error)}`,\n 'TIMEOUT',\n { cause: error },\n )\n }\n // fetch wraps every transport failure (DNS, refused connection, TLS,\n // proxy, reset) in a bare `TypeError: fetch failed` whose actionable\n // detail lives on `cause`. Include the full chain so the failure reason\n // shown in the web UI (which renders only the message, not `cause`)\n // names the real root cause instead of a generic wrapper.\n throw new LlmError(\n `Command Code API request to ${connection.apiBase}/alpha/generate failed: ${errorChain(error)}`,\n 'TRANSPORT',\n { cause: error },\n )\n }\n\n if (!response.ok) {\n const errText = await response.text().catch(() => '')\n // Command Code folds several business rejections into 403 (plan limits,\n // CLI version, model access). Prefer the machine-readable `error.code`\n // when present; the status alone cannot distinguish them.\n let providerCode: string | undefined\n try {\n const parsed: unknown = JSON.parse(errText)\n if (isRecord(parsed) && isRecord(parsed.error)) {\n providerCode = stringValue(parsed.error.code)\n }\n } catch {\n // Plain-text bodies: rely on the status mapping below.\n }\n const detail = providerCode ?? `HTTP ${response.status}`\n if (response.status === 401) {\n // An invalid or missing credential is a config problem, not a\n // transport failure: retrying it identically cannot succeed.\n throw new LlmError(\n `Command Code API error 401 (${detail}): the API key is missing or invalid — check the`\n + ' key stored for COMMANDCODE_API_KEY (Models page) or the auth file',\n 'INVALID_CREDENTIAL',\n { status: 401 },\n )\n }\n throw new LlmError(\n `Command Code API error ${response.status}${detail === `HTTP ${response.status}` ? '' : ` (${detail})`}: ${errText.slice(0, 500)}`,\n response.status === 429 ? 'RATE_LIMIT' : 'PROVIDER_HTTP_ERROR',\n { status: response.status },\n )\n }\n if (!response.body) {\n throw new LlmError('Command Code API returned no response body', 'PROVIDER_PROTOCOL_ERROR')\n }\n\n // --- SSE/JSONL event stream -> harness StreamChunk protocol ---\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n\n // Stream idle watchdog: a generation that stalls this long has a dead\n // connection (the API keeps the socket open between reasoning/text\n // bursts). reader.cancel() unblocks a pending read(), which the loop then\n // turns into a TIMEOUT failure instead of hanging forever.\n let idleTimer: ReturnType<typeof setTimeout> | undefined\n let idleFired = false\n const armIdle = () => {\n if (idleTimer !== undefined) clearTimeout(idleTimer)\n idleTimer = setTimeout(() => {\n idleFired = true\n void reader.cancel().catch(() => undefined)\n }, connection.streamIdleTimeoutMs)\n }\n const clearIdle = () => {\n if (idleTimer !== undefined) {\n clearTimeout(idleTimer)\n idleTimer = undefined\n }\n }\n\n // Block assembly state: at most one text block and one reasoning block\n // are open at a time (same assumption as the pi plugin).\n let nextIndex = 0\n let textIndex = -1\n let textContent = ''\n let reasoningIndex = -1\n let reasoningContent = ''\n let sawContent = false\n\n const closeText = function* (): Generator<StreamChunk> {\n if (textIndex < 0) return\n yield {\n type: 'block-end',\n index: textIndex,\n block: { type: 'text', text: textContent },\n }\n textIndex = -1\n textContent = ''\n }\n const closeReasoning = function* (): Generator<StreamChunk> {\n if (reasoningIndex < 0) return\n yield {\n type: 'block-end',\n index: reasoningIndex,\n block: { type: 'reasoning', text: reasoningContent },\n }\n reasoningIndex = -1\n reasoningContent = ''\n }\n\n const handleEvent = (event: unknown): StreamChunk[] => {\n const chunks: StreamChunk[] = []\n if (!isRecord(event)) return chunks\n\n switch (event.type) {\n case 'text-delta': {\n chunks.push(...closeReasoning())\n if (textIndex < 0) {\n textIndex = nextIndex++\n chunks.push({ type: 'block-start', index: textIndex, blockType: 'text' })\n }\n const delta = stringValue(event.text) ?? ''\n textContent += delta\n sawContent = true\n chunks.push({ type: 'text-delta', index: textIndex, text: delta })\n break\n }\n case 'reasoning-delta': {\n chunks.push(...closeText())\n if (reasoningIndex < 0) {\n reasoningIndex = nextIndex++\n chunks.push({ type: 'block-start', index: reasoningIndex, blockType: 'reasoning' })\n }\n const delta = stringValue(event.text) ?? ''\n reasoningContent += delta\n chunks.push({ type: 'reasoning-delta', index: reasoningIndex, text: delta })\n break\n }\n case 'reasoning-start':\n chunks.push(...closeText())\n break\n case 'reasoning-end':\n chunks.push(...closeReasoning())\n break\n case 'tool-call': {\n chunks.push(...closeText(), ...closeReasoning())\n const id = stringValue(event.toolCallId) ?? randomUUID()\n const name = stringValue(event.toolName) ?? ''\n const args = JSON.stringify(recordOrEmpty(event.input ?? event.args ?? event.arguments))\n const index = nextIndex++\n sawContent = true\n chunks.push(\n { type: 'block-start', index, blockType: 'tool-call' },\n { type: 'tool-call-delta', index, id: CallId(id), name, argumentsDelta: args },\n {\n type: 'block-end',\n index,\n block: { type: 'tool-call', id: CallId(id), name, arguments: args },\n },\n )\n break\n }\n case 'finish': {\n chunks.push(...closeText(), ...closeReasoning())\n const usage = isRecord(event.totalUsage) ? event.totalUsage : undefined\n if (usage) {\n const details = isRecord(usage.inputTokenDetails) ? usage.inputTokenDetails : undefined\n const totalInput = numberValue(usage.inputTokens) ?? 0\n const cacheRead = numberValue(details?.cacheReadTokens) ?? 0\n const cacheWrite = numberValue(details?.cacheWriteTokens) ?? 0\n // Harness TokenUsage counts are disjoint: uncached input only.\n const tokenUsage: TokenUsage = {\n inputTokens:\n numberValue(details?.noCacheTokens) ?? Math.max(0, totalInput - cacheRead - cacheWrite),\n outputTokens: numberValue(usage.outputTokens) ?? 0,\n cacheReadTokens: cacheRead,\n cacheWriteTokens: cacheWrite,\n }\n chunks.push({ type: 'usage', usage: tokenUsage })\n }\n chunks.push({ type: 'finish', reason: mapFinishReason(event.finishReason) })\n break\n }\n case 'error': {\n const detail = isRecord(event.error)\n ? (stringValue(event.error.message) ?? JSON.stringify(event.error))\n : (stringValue(event.error) ?? stringValue(event.message) ?? 'Stream error')\n throw new LlmError(`Command Code stream error: ${detail}`, 'PROVIDER_STREAM_ERROR')\n }\n }\n return chunks\n }\n\n try {\n let finished = false\n for (;;) {\n let read: ReadableStreamReadResult<Uint8Array>\n armIdle()\n try {\n read = await reader.read()\n } catch (error: unknown) {\n // A mid-stream transport failure (connection reset, TLS teardown)\n // surfaces here. Caller cancellation propagates as-is.\n if (options.signal?.aborted) throw error\n throw new LlmError(\n `Command Code API stream from ${connection.apiBase} failed while reading: ${errorChain(error)}`,\n 'TRANSPORT',\n { cause: error },\n )\n } finally {\n clearIdle()\n }\n const { done, value } = read\n if (done) {\n // The idle watchdog cancels the reader to unblock a stalled read;\n // cancel() resolves a pending read() as done, so a done here after\n // the watchdog fired is a timeout, not a normal stream end.\n if (idleFired) {\n throw new LlmError(\n `Command Code API stream from ${connection.apiBase} was idle for ${connection.streamIdleTimeoutMs}ms`\n + ' (no events) and was treated as a dead connection',\n 'TIMEOUT',\n )\n }\n if (buffer.trim()) for (const chunk of handleEvent(parseStreamEventLine(buffer))) yield chunk\n break\n }\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n const chunks = handleEvent(parseStreamEventLine(line))\n for (const chunk of chunks) {\n yield chunk\n if (chunk.type === 'finish') finished = true\n }\n }\n if (finished) break\n }\n if (!finished) {\n // Stream ended without a finish event: close open blocks and\n // terminate according to the adapter contract (usage, then finish).\n yield* closeText()\n yield* closeReasoning()\n if (!sawContent) {\n throw new LlmError('Command Code returned an empty response', 'EMPTY_RESPONSE')\n }\n yield { type: 'finish', reason: { kind: 'stop' } }\n }\n } finally {\n clearIdle()\n await reader.cancel().catch(() => undefined)\n reader.releaseLock()\n }\n }\n}\n\nfunction mapFinishReason(reason: unknown): FinishReason {\n if (reason === 'tool-calls') return { kind: 'tool-calls' }\n if (\n reason === 'length' ||\n reason === 'max_tokens' ||\n reason === 'max-tokens' ||\n reason === 'max_output_tokens'\n ) {\n return { kind: 'max-tokens' }\n }\n return { kind: 'stop' }\n}\n","/**\n * `/commandcode` slash command — account usage dashboard.\n *\n * /commandcode show account, usage, and credit state\n * /commandcode status same as bare `/commandcode`\n *\n * Backed by the Command Code account endpoints the official CLI uses\n * (`/alpha/whoami`, `/alpha/usage/summary`, `/alpha/billing/credits`),\n * exposed through `CommandCodeAdapter.getUsage()`.\n *\n * @module dsh-commandcode-provider/commands\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only import that loads the module augmentation (`ctx.commands`).\nimport type { CommandDefinition } from '@deepseek-ai/dsh-commands'\nimport { CommandCodeAdapter } from './adapter.ts'\nimport type { CommandCodeConnectionOptions, CommandCodeUsageReport } from './adapter.ts'\n\n/** Everything the command needs beyond the adapter itself. */\nexport interface CommandCodeCommandDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {\n /** The registered adapter (for getUsage / listModels). */\n adapter: CommandCodeAdapter<C>\n}\n\n/** Format a dollar amount. */\nfunction money(value: number): string {\n return `$${value.toFixed(4)}`\n}\n\n/** Format a dollar amount compactly (2 decimals). */\nfunction moneyShort(value: number): string {\n return `$${value.toFixed(2)}`\n}\n\n/** Format a token count with thousands separators. */\n/** Format a large token count compactly (1.9亿 style). */\nfunction tokensCompact(value: number): string {\n if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`\n if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`\n if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`\n return String(value)\n}\n\n/** Format a millis timestamp as a local date. */\nfunction resetLabel(ms: number): string {\n if (ms <= 0) return 'n/a'\n return new Date(ms).toLocaleString()\n}\n\n/**\n * A 10-cell horizontal bar: `██████████` for 100%, `███░░░░░░░` for ~33%.\n * Handles caps of 0 (no limit) and out-of-range values.\n */\nfunction bar(used: number, cap: number): string {\n if (cap <= 0) return '—'\n const ratio = Math.max(0, Math.min(1, used / cap))\n const filled = Math.round(ratio * 10)\n return '█'.repeat(filled) + '░'.repeat(10 - filled)\n}\n\n/** Render the usage report as a structured, aligned, bar-chart text view. */\nfunction renderReport(report: CommandCodeUsageReport): string {\n const lines: string[] = []\n const account = report.account ? ` (${report.account.userName || report.account.name})` : ''\n\n lines.push(`📊 Command Code 用量${account}`, '')\n\n if (report.usage) {\n const u = report.usage\n lines.push(\n '── 请求 ──────────────────────────────',\n ` 💬 请求 ${u.completedCount} 次 / 失败 ${u.failedCount} 成功率 ${u.successRate}%`,\n ` 💰 花费 ${money(u.totalCost)} (${moneyShort(u.totalCredits)} credits)`,\n ` 🔤 Token ${tokensCompact(u.totalTokensIn)} 入 / ${tokensCompact(u.totalTokensOut)} 出`,\n '',\n )\n }\n\n if (report.credits) {\n const c = report.credits\n const monthlyPct = c.monthlyCredits > 0\n ? `${((c.monthlyCredits / (c.monthlyCredits + c.purchasedCredits)) * 100).toFixed(0)}%`\n : '—'\n lines.push(\n '── 信用 ──────────────────────────────',\n ` 💳 月额度 ${moneyShort(c.monthlyCredits)} (已购 ${moneyShort(c.purchasedCredits)} / 赠送 ${moneyShort(c.freeCredits)})`,\n ` └ ${bar(c.monthlyCredits, c.monthlyCredits + c.purchasedCredits)} ${monthlyPct}`,\n '',\n '── 窗口用量 ──────────────────────────',\n ` ⏱ 5 小时 ${moneyShort(c.fiveHour.used)} / ${moneyShort(c.fiveHour.cap)}${c.fiveHour.exceeded ? ' ⚠️ 超限!' : ''}`,\n ` └ ${bar(c.fiveHour.used, c.fiveHour.cap)} 重置 ${resetLabel(c.fiveHour.resetAt)}`,\n ` 📅 每周 ${moneyShort(c.weekly.used)} / ${moneyShort(c.weekly.cap)}${c.weekly.exceeded ? ' ⚠️ 超限!' : ''}`,\n ` └ ${bar(c.weekly.used, c.weekly.cap)} 重置 ${resetLabel(c.weekly.resetAt)}`,\n '',\n )\n }\n\n if (report.failures.length > 0) {\n lines.push(`⚠️ 部分端点失败: ${report.failures.join('; ')}`, '')\n }\n if (!report.account && !report.usage && !report.credits) {\n lines.push('(no data — check your API key)', '')\n }\n\n return lines.join('\\n').trimEnd()\n}\n\n/** The one registered `/commandcode` command. */\nexport function commandDefinition<C extends CommandCodeConnectionOptions>(\n deps: CommandCodeCommandDeps<C>,\n): CommandDefinition {\n const { adapter } = deps\n return {\n name: 'commandcode',\n description: 'Command Code account usage dashboard',\n input: { hint: '[status]' },\n handler: async () => {\n try {\n const report = await adapter.getUsage()\n return { kind: 'success', text: renderReport(report) }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n kind: 'error',\n text: `Could not fetch Command Code usage: ${message}`,\n }\n }\n },\n }\n}\n\n/** Register the command on `ctx.commands` (called from the plugin entry). */\nexport function applyCommands<C extends CommandCodeConnectionOptions>(\n ctx: Context,\n deps: CommandCodeCommandDeps<C>,\n): void {\n ctx.commands.register(commandDefinition(deps))\n}\n","/**\n * dsh-commandcode-provider — DeepSeek Harness LLM provider plugin for Command\n * Code (unofficial; ported from pi-commandcode-provider@0.5.1).\n *\n * Registers the `commandcode` provider route on `ctx.llm` and declares it in\n * the configurable-provider directory, so the web Models page shows a\n * \"Command Code\" card with an API-key field and the model picker lists the\n * live Command Code model catalog. Connection facts resolve per request over\n * the optional `llm-commandcode` user-settings section and the credential\n * seam, so a changed key, endpoint, or cache path reaches the next request\n * without a restart.\n *\n * ```yaml\n * - id: llm-commandcode\n * name: \"@mars-sea/dsh-commandcode-provider\"\n * config:\n * apiKeyEnv: COMMANDCODE_API_KEY\n * ```\n *\n * The `name` is the full package specifier as installed in the profile's\n * node_modules: the loader imports it as a module, and pnpm links packages by\n * their true (scoped) name — a bare `dsh-commandcode-provider` fails to\n * resolve (ERR_MODULE_NOT_FOUND) and crashes the app on boot. The value must\n * be quoted in YAML: an unquoted scalar starting with `@` fails to parse.\n *\n * @module dsh-commandcode-provider\n */\n\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport z from '@deepseek-ai/schemastery'\nimport { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'\nimport { assertUsableApiKey, LlmError } from '@deepseek-ai/dsh-llm'\nimport { credentialRef, type CredentialRef } from '@deepseek-ai/dsh-credentials'\nimport { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment'\nimport { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'\nimport { CommandCodeAdapter, DEFAULT_API_BASE, resolveAuthFileApiKey } from './adapter.ts'\nimport { DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS } from './adapter.ts'\nimport type { CommandCodeConnectionOptions } from './adapter.ts'\nimport { applyCommands } from './commands.ts'\n\nexport {\n COMMAND_CODE_CLI_VERSION,\n DEFAULT_API_BASE,\n DEFAULT_GENERATE_MAX_TOKENS,\n DEFAULT_MAX_OUTPUT_TOKENS,\n DEFAULT_REQUEST_TIMEOUT_MS,\n DEFAULT_STREAM_IDLE_TIMEOUT_MS,\n CommandCodeAdapter,\n KNOWN_EFFORTS,\n projectSlugFromPath,\n resolveAuthFileApiKey,\n} from './adapter.ts'\nexport type { CommandCodeAdapterDeps, CommandCodeConnectionOptions, CommandCodeUsageReport } from './adapter.ts'\nexport { applyCommands, commandDefinition } from './commands.ts'\nexport type { CommandCodeCommandDeps } from './commands.ts'\n\nexport const name = 'llm-commandcode'\nexport const inject = ['llm']\n\nconst NS = settingsNamespace('llm-commandcode')\nconst DEFAULT_API_KEY_ENV = 'COMMANDCODE_API_KEY'\n\n/** The single provider route this plugin owns. */\nexport const PROVIDER = 'commandcode'\n/** Default models cache path (mirrors the pi plugin's on-disk cache). */\nexport const DEFAULT_MODELS_CACHE_PATH = join(homedir(), '.commandcode', 'models-cache.json')\n\n/**\n * Plugin config, validated by the same-named schemastery schema and doubling\n * as the `llm-commandcode` settings-section shape. Every field is optional:\n * a missing API key resolves through {@link Config.apiKeyEnv} at each request\n * (the web Models page writes it), with the official Command Code CLI auth\n * file (`~/.commandcode/auth.json`) as the last fallback.\n */\nexport interface Config {\n /** Credential reference (environment-variable name) resolved per request; defaults to `COMMANDCODE_API_KEY`. */\n apiKeyEnv?: string\n /** Literal API key override (composition config only); takes precedence over `apiKeyEnv`. */\n apiKey?: string\n /** API base; defaults to the public Command Code Provider API. */\n apiBase?: string\n /** Working directory reported to the API; defaults to the process cwd. */\n workingDir?: string\n /** Model catalog cache path; defaults to `~/.commandcode/models-cache.json`. */\n modelsCachePath?: string\n /** Milliseconds to wait for the generate response's first byte; defaults to 60s. */\n requestTimeoutMs?: number\n /** Milliseconds a stream may stall before being treated as a dead connection; defaults to 120s. */\n streamIdleTimeoutMs?: number\n}\n\nexport const Config: z<Config> = z.object({\n apiKeyEnv: z.string().role('credential-ref').default(DEFAULT_API_KEY_ENV),\n apiKey: z.string(),\n apiBase: z.string(),\n workingDir: z.string(),\n modelsCachePath: z.string(),\n requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),\n streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),\n})\n\n/** One resolution's complete request facts: connection plus credential reference. */\nexport interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {\n apiKeyEnv: CredentialRef\n}\n\n/**\n * The one explicit resolve step from raw config to validated connection\n * facts. Programmatic construction may bypass Schemastery normalization, so\n * every default is re-judged here — for the composition entry at load and for\n * each settings snapshot at its first use.\n */\nexport function resolveAdapterOptions(config: Config): ResolvedCommandCodeOptions {\n return {\n apiKeyEnv: credentialRef(config.apiKeyEnv ?? DEFAULT_API_KEY_ENV),\n apiBase: config.apiBase ?? DEFAULT_API_BASE,\n workingDir: config.workingDir ?? process.cwd(),\n modelsCachePath: config.modelsCachePath ?? DEFAULT_MODELS_CACHE_PATH,\n requestTimeoutMs: config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,\n streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS,\n }\n}\n\nexport function apply(ctx: Context, config: Config): void {\n let current: () => Config = () => config\n let lastRaw: Config | undefined\n let lastGood: ResolvedCommandCodeOptions | undefined\n const options = (): ResolvedCommandCodeOptions => {\n const raw = current()\n if (raw === lastRaw && lastGood !== undefined) return lastGood\n const next = resolveAdapterOptions(raw)\n lastRaw = raw\n lastGood = next\n return next\n }\n options()\n\n const resolveApiKey = async (connection: ResolvedCommandCodeOptions): Promise<string> => {\n // 1. A literal key in composition config wins outright.\n const literal = current().apiKey\n if (literal) return assertUsableApiKey(literal, 'llm-commandcode', 'config.apiKey')\n // 2. The credential seam (web Models page) or the trusted environment.\n const ref = connection.apiKeyEnv\n const credentials = ctx.get('credentials')\n if (credentials !== undefined) {\n const hit = await credentials.resolve(ref)\n if (hit !== undefined) return assertUsableApiKey(hit.value, 'llm-commandcode', ref)\n } else {\n const ambient = launchEnvironmentOf(ctx).get(ref)\n if (ambient !== undefined && ambient.value.length > 0) {\n return assertUsableApiKey(ambient.value, 'llm-commandcode', ref)\n }\n }\n // 3. Last resort: reuse the official Command Code CLI login (~/.commandcode/auth.json).\n const authFileKey = resolveAuthFileApiKey()\n if (authFileKey) return assertUsableApiKey(authFileKey, 'llm-commandcode', '~/.commandcode/auth.json')\n throw new LlmError(\n `llm-commandcode: no API key for provider route \"${PROVIDER}\"; store ${ref} through the`\n + ' credentials service (the web Models page writes it), export it in the launching'\n + ' environment, set config.apiKey, or run `command-code login` to write'\n + ' ~/.commandcode/auth.json',\n 'MISSING_CREDENTIAL',\n )\n }\n\n const adapter = new CommandCodeAdapter({ options, resolveApiKey })\n // The Models page card: a configurable provider with a settings address.\n // settingsPath [] means the whole `llm-commandcode` section configures it.\n ctx.llm.registerConfigurableProviders([\n { provider: PROVIDER, displayName: 'Command Code', settingsNs: NS, settingsPath: [] },\n ])\n // The live route: this is what makes models requestable under `commandcode`.\n ctx.llm.registerAdapter([PROVIDER], adapter)\n\n // The /commandcode usage command rides the optional `commands` service: a\n // child fiber injects it, so it registers whenever the profile mounts\n // dsh-commands and the fiber simply never activates when it does not.\n ctx.inject(['commands'], (commandCtx) => {\n applyCommands(commandCtx, { adapter })\n })\n\n installSettingsSection(ctx, NS, Config, config, {\n setSource: (source) => {\n current = source\n },\n // Everything the adapter reads is resolved per request, so a settings\n // change needs no registration-level action.\n onChange: () => {},\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/adapter.ts","../src/commands.ts","../src/index.ts"],"sourcesContent":["/**\n * DeepSeek Harness LLM adapter for the Command Code Provider API.\n *\n * Ported from pi-commandcode-provider@0.5.1 (MIT). This is an unofficial,\n * community-maintained integration; you need your own Command Code account\n * and API key or subscription, and Command Code's terms apply.\n *\n * Wire protocol (reverse-engineered by the pi plugin, command-code@1.26.0):\n * POST {apiBase}/alpha/generate\n * body: { config, memory, taste, skills, params: { model, messages, tools,\n * system, max_tokens, temperature, stream, reasoning_effort? }, threadId }\n * SSE-ish JSONL events: text-delta | reasoning-start/delta/end | tool-call\n * | tool-result | finish | error\n * Model catalog: GET {apiBase}/provider/v1/models -> { object: 'list', data: [...] }\n *\n * The adapter is deliberately free of cordis/schemastery: it receives a\n * per-request options thunk and an API-key resolver from the plugin entry\n * (src/index.ts), so a settings change reaches the very next request.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\nimport { randomUUID } from 'node:crypto'\n\nimport type { AttachmentStore, ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'\n\nimport {\n attributionHeaders,\n CallId,\n LlmAdapter,\n LlmError,\n ReasoningEffortId,\n errorChain,\n resolveRetryPolicy,\n type ResolvedRetryPolicy,\n type ContentBlock,\n type FinishReason,\n type GenerateOptions,\n type LlmModelInfo,\n type LlmResolvedModelInfo,\n type Message,\n type StreamChunk,\n type TokenUsage,\n} from '@deepseek-ai/dsh-llm'\n\n// ---------------------------------------------------------------------------\n// Static capability snapshot (from the official command-code@1.26.0 bundled\n// model catalog, dist/cli.mjs). The Provider API does not expose reasoning\n// metadata; models omitted here let Command Code choose their reasoning\n// depth, matching the official CLI.\n// ---------------------------------------------------------------------------\n\nexport const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>> = {\n 'Qwen/Qwen3.8-Max': ['low', 'medium', 'xhigh'],\n 'claude-fable-5': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-opus-4-7': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-opus-4-8': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-opus-5': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-sonnet-4-6': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'claude-sonnet-5': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'deepseek/deepseek-v4-flash': ['high', 'max'],\n 'deepseek/deepseek-v4-pro': ['high', 'max'],\n 'google/gemini-3.1-flash-lite': ['low', 'medium', 'high'],\n 'google/gemini-3.5-flash': ['low', 'medium', 'high'],\n 'google/gemini-3.5-flash-lite': ['low', 'medium', 'high'],\n 'google/gemini-3.6-flash': ['low', 'medium', 'high'],\n 'google/gemini-3.7-flash': ['low', 'medium', 'high'],\n 'gpt-5.3-codex': ['low', 'medium', 'high', 'xhigh'],\n 'gpt-5.4': ['low', 'medium', 'high', 'xhigh'],\n 'gpt-5.4-mini': ['low', 'medium', 'high'],\n 'gpt-5.5': ['low', 'medium', 'high', 'xhigh'],\n 'gpt-5.6-luna': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'gpt-5.6-sol': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'gpt-5.6-terra': ['low', 'medium', 'high', 'xhigh', 'max'],\n 'sakana/fugu-ultra': ['high', 'xhigh'],\n 'xai/grok-4.5': ['low', 'medium', 'high'],\n 'xai/grok-4.6': ['low', 'medium', 'high', 'xhigh'],\n 'zai-org/GLM-5.2': ['high', 'max'],\n 'zai-org/GLM-5.3': ['low', 'high', 'max'],\n}\n\n/**\n * Models whose Capabilities include Vision, per the official Command Code\n * model registry (`https://commandcode.ai/docs/reference/cli/models`, generated\n * from the same registry as `cmd --list-models` / the `/model` picker).\n *\n * The Provider API does not expose modality metadata, so this snapshot is the\n * source of truth for image-input gating. Command Code's own CLI falls back to\n * a client-side VISION side-call for text-only models; this adapter does not\n * reproduce that interactive feature, so images sent to a model outside this\n * list are refused loudly (`UNSUPPORTED_CONTENT`) instead of being dropped or\n * sent to a model that cannot read them.\n *\n * Keep in sync with the official registry when new models ship (see the\n * dsh-commandcode-upstream skill).\n */\nexport const KNOWN_IMAGE_MODELS: ReadonlySet<string> = new Set([\n 'MiniMaxAI/MiniMax-M3',\n 'Qwen/Qwen3.6-Plus',\n 'Qwen/Qwen3.7-Flash',\n 'Qwen/Qwen3.7-Plus',\n 'Qwen/Qwen3.8-Max',\n 'claude-fable-5',\n 'claude-haiku-4-5-20251001',\n 'claude-opus-4-7',\n 'claude-opus-4-8',\n 'claude-opus-5',\n 'claude-sonnet-4-6',\n 'claude-sonnet-5',\n 'google/gemini-3.1-flash-lite',\n 'google/gemini-3.5-flash',\n 'google/gemini-3.5-flash-lite',\n 'google/gemini-3.6-flash',\n 'google/gemini-3.7-flash',\n 'gpt-5.3-codex',\n 'gpt-5.4',\n 'gpt-5.4-mini',\n 'gpt-5.5',\n 'gpt-5.6-luna',\n 'gpt-5.6-sol',\n 'gpt-5.6-terra',\n 'meta/muse-spark-1.1',\n 'meta/muse-spark-1.2',\n 'meta/muse-spark-1.2-contributor',\n 'moonshotai/Kimi-K2.5',\n 'moonshotai/Kimi-K2.6',\n 'moonshotai/Kimi-K2.7-Code',\n 'moonshotai/Kimi-K2.7-Code-Highspeed',\n 'moonshotai/Kimi-K3',\n 'sakana/fugu-ultra',\n 'stepfun/Step-3.7-Flash',\n 'thinkingmachines/inkling',\n 'thinkingmachines/inkling-small',\n 'xai/grok-4.5',\n 'xiaomi/mimo-v2.5',\n])\n\nexport const COMMAND_CODE_CLI_VERSION = '1.26.0'\nexport const DEFAULT_API_BASE = 'https://api.commandcode.ai'\nexport const DEFAULT_GENERATE_MAX_TOKENS = 64_000\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 65_536\nexport const MODELS_TIMEOUT_MS = 10_000\n/** Head-of-request timeout: how long to wait for the first response byte. */\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 60_000\n/** Stream idle timeout: a generation that stalls this long is a dead connection. */\nexport const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 120_000\nconst MODEL_CACHE_VERSION = 1\n\n// ---------------------------------------------------------------------------\n// Small helpers (ported from converters.ts / models.ts)\n// ---------------------------------------------------------------------------\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined\n}\n\nfunction numberValue(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\nfunction recordOrEmpty(value: unknown): Record<string, unknown> {\n if (isRecord(value)) return value\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value)\n if (isRecord(parsed)) return parsed\n } catch {\n // Some providers stream incomplete JSON argument fragments.\n }\n }\n return {}\n}\n\nexport function projectSlugFromPath(pathName: string): string {\n const slug = pathName\n .toLowerCase()\n .replace(/^[a-z]:/i, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n return slug || 'project'\n}\n\nfunction parseStreamEventLine(line: string): unknown | undefined {\n let trimmed = line.trim()\n if (!trimmed || trimmed.startsWith(':') || trimmed.startsWith('event:')) return undefined\n if (trimmed.startsWith('data:')) trimmed = trimmed.slice(5).trim()\n if (!trimmed || trimmed === '[DONE]') return undefined\n try {\n return JSON.parse(trimmed) as unknown\n } catch {\n return undefined\n }\n}\n\n// ---------------------------------------------------------------------------\n// Credential fallback from the official Command Code CLI auth file. Used as\n// the last fallback by the plugin entry, so a user who already logged in with\n// `command-code login` can reuse that credential. Only the official CLI's own\n// file is read — pi/OMP auth files are intentionally not scanned, so their\n// credentials and formats cannot surprise this adapter.\n// ---------------------------------------------------------------------------\n\n/** Extract the key from the CLI's nested credential records (`command-code`). */\nfunction apiKeyFromCredentialRecord(value: unknown): string | undefined {\n if (!isRecord(value)) return undefined\n const type = stringValue(value.type)\n if (type === 'api') return stringValue(value.key)\n if (type === 'oauth') return stringValue(value.access)\n return stringValue(value.key) ?? stringValue(value.access)\n}\n\n/** Read a usable Command Code credential from the official CLI auth file. */\nexport function resolveAuthFileApiKey(): string | undefined {\n const authPath = join(homedir(), '.commandcode', 'auth.json')\n try {\n if (!existsSync(authPath)) return undefined\n const parsed: unknown = JSON.parse(readFileSync(authPath, 'utf-8'))\n if (!isRecord(parsed)) return undefined\n const direct = stringValue(parsed.apiKey) ?? stringValue(parsed.commandcode)\n if (direct) return direct\n const nested =\n apiKeyFromCredentialRecord(parsed.commandcode) ??\n apiKeyFromCredentialRecord(parsed['command-code'])\n return nested\n } catch {\n // Ignore malformed or unreadable auth file.\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Model catalog discovery with on-disk cache fallback (ported from models.ts)\n// ---------------------------------------------------------------------------\n\ninterface CommandCodeModel {\n id: string\n name: string\n contextWindow: number\n maxTokens: number\n}\n\nfunction parseCatalogResponse(value: unknown): CommandCodeModel[] {\n if (!isRecord(value) || value.object !== 'list' || !Array.isArray(value.data)) {\n throw new LlmError('Unexpected Command Code models response shape', 'PROVIDER_PROTOCOL_ERROR')\n }\n const models: CommandCodeModel[] = []\n for (const entry of value.data) {\n if (!isRecord(entry)) continue\n const id = stringValue(entry.id)\n const name = stringValue(entry.name)\n const contextLength = numberValue(entry.context_length)\n if (!id || !name || !contextLength || contextLength <= 0) continue\n models.push({\n id,\n name,\n contextWindow: contextLength,\n maxTokens: Math.min(contextLength, DEFAULT_MAX_OUTPUT_TOKENS),\n })\n }\n if (models.length === 0) {\n throw new LlmError('Command Code returned an empty model catalog', 'PROVIDER_PROTOCOL_ERROR')\n }\n return models\n}\n\nasync function readModelsCache(cachePath: string): Promise<CommandCodeModel[]> {\n const parsed: unknown = JSON.parse(await readFile(cachePath, 'utf-8'))\n if (!isRecord(parsed) || parsed.version !== MODEL_CACHE_VERSION || !Array.isArray(parsed.models)) {\n throw new Error(`Invalid model cache at ${cachePath}`)\n }\n return parsed.models as CommandCodeModel[]\n}\n\nasync function writeModelsCache(cachePath: string, models: CommandCodeModel[]): Promise<void> {\n await mkdir(dirname(cachePath), { recursive: true })\n const tmp = `${cachePath}.${process.pid}.tmp`\n try {\n await writeFile(tmp, `${JSON.stringify({ version: MODEL_CACHE_VERSION, models }, null, 2)}\\n`, {\n encoding: 'utf-8',\n mode: 0o600,\n })\n await rename(tmp, cachePath)\n } finally {\n await rm(tmp, { force: true }).catch(() => undefined)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message conversion: harness Message[] -> Command Code wire messages.\n// Reasoning blocks are intentionally NOT replayed (matches the pi plugin and\n// the official CLI: prior private reasoning must not leak into later turns).\n// Only tool calls with a paired tool result are replayed.\n// ---------------------------------------------------------------------------\n\nfunction pairedToolCallIds(messages: readonly Message[]): Set<string> {\n const callIds = new Set<string>()\n const resultIds = new Set<string>()\n for (const message of messages) {\n for (const block of message.content) {\n if (message.role === 'assistant' && block.type === 'tool-call') callIds.add(block.id)\n if (block.type === 'tool-result') resultIds.add(block.toolCallId)\n }\n }\n return new Set([...callIds].filter((id) => resultIds.has(id)))\n}\n\nfunction blockText(block: ContentBlock): string {\n return block.type === 'text' || block.type === 'reasoning' ? block.text : ''\n}\n\nfunction toolResultText(block: Extract<ContentBlock, { type: 'tool-result' }>): string {\n return block.content.map(blockText).filter(Boolean).join('\\n')\n}\n\nfunction hasImageContent(message: Message): boolean {\n const check = (blocks: readonly ContentBlock[]): boolean =>\n blocks.some(\n (b) => b.type === 'image' || (b.type === 'tool-result' && check(b.content)),\n )\n return check(message.content)\n}\n\n/**\n * Convert one image reference to the Command Code wire format, as the official\n * CLI does: `{ type: 'image', source: { type: 'base64', media_type, data } }`.\n * Bytes come from the durable attachment service; the media type is the one\n * verified at save time.\n */\nasync function imageToCommandCode(\n ref: ImageAttachmentRef,\n readImage: (ref: ImageAttachmentRef) => Promise<Uint8Array>,\n): Promise<{ type: 'image'; source: { type: 'base64'; media_type: string; data: string } }> {\n const data = await readImage(ref)\n return {\n type: 'image',\n source: {\n type: 'base64',\n media_type: ref.mediaType,\n data: Buffer.from(data).toString('base64'),\n },\n }\n}\n\nasync function messagesToCC(\n messages: readonly Message[],\n readImage?: (ref: ImageAttachmentRef) => Promise<Uint8Array>,\n): Promise<unknown[]> {\n const out: unknown[] = []\n const paired = pairedToolCallIds(messages)\n\n for (const message of messages) {\n if (message.role === 'system') continue // folded into params.system by the caller\n\n if (message.role === 'user' && message.source.kind !== 'tool') {\n const parts: unknown[] = []\n for (const block of message.content) {\n if (block.type === 'text') parts.push({ type: 'text', text: block.text })\n if (block.type === 'image') {\n // The caller (stream) has already gated image input on model\n // capability and attachment-service availability, so reaching this\n // branch with no resolver is an internal contract violation.\n if (!readImage) {\n throw new LlmError(\n 'Image input requires the durable attachment service',\n 'UNSUPPORTED_CONTENT',\n )\n }\n parts.push(await imageToCommandCode(block.attachment, readImage))\n }\n }\n out.push({ role: 'user', content: parts })\n continue\n }\n\n if (message.role === 'assistant') {\n const parts: unknown[] = []\n for (const block of message.content) {\n if (block.type === 'text') {\n parts.push({ type: 'text', text: block.text })\n } else if (block.type === 'tool-call' && paired.has(block.id)) {\n parts.push({\n type: 'tool-call',\n toolCallId: block.id,\n toolName: block.name,\n input: recordOrEmpty(block.arguments),\n })\n }\n // reasoning blocks: skipped by design (see header comment)\n }\n if (parts.length > 0) out.push({ role: 'assistant', content: parts })\n continue\n }\n\n // tool-result message (user role, single tool-result block)\n if (message.role === 'user' && message.source.kind === 'tool') {\n const block = message.content[0]\n if (!block || block.type !== 'tool-result' || !paired.has(block.toolCallId)) continue\n out.push({\n role: 'tool',\n content: [\n {\n type: 'tool-result',\n toolCallId: block.toolCallId,\n toolName: '',\n output: block.isError\n ? { type: 'error-text', value: toolResultText(block) }\n : { type: 'text', value: toolResultText(block) },\n },\n ],\n })\n }\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n/** Connection facts resolved fresh per request by the plugin entry. */\nexport interface CommandCodeConnectionOptions {\n /** API base; the Provider API lives under it (`/alpha/generate`, `/provider/v1/models`). */\n apiBase: string\n /** Working directory reported to the API (project slug, config block). */\n workingDir: string\n /** Model catalog cache path. */\n modelsCachePath: string\n /** Milliseconds to wait for the generate response's first byte (default 60s). */\n requestTimeoutMs: number\n /** Milliseconds a stream may stall before it is treated as a dead connection (default 120s). */\n streamIdleTimeoutMs: number\n}\n\n/**\n * Resolve the durable attachment service, or undefined when the host does not\n * provide one. Called lazily only when a request actually carries images, so a\n * text-only request never depends on the attachment seam.\n */\nexport type ResolveAttachments = () => AttachmentStore | undefined\n\n/** Everything the adapter needs beyond the request itself. */\nexport interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {\n /** Resolve the current connection facts (fresh per request, settings-aware). */\n options: () => C\n /** Resolve a usable API key for the given connection facts, or throw `MISSING_CREDENTIAL`. */\n resolveApiKey: (connection: C) => Promise<string>\n /** HTTP transport override (tests); defaults to the global `fetch`. */\n fetchImpl?: typeof fetch\n /** Resolve the optional durable attachment service for image input (tests); defaults to none. */\n resolveAttachments?: ResolveAttachments\n}\n\n/** Account identity from `/alpha/whoami`. */\nexport interface CommandCodeAccount {\n id: string\n name: string\n userName: string\n}\n\n/** Usage summary from `/alpha/usage/summary`. */\nexport interface CommandCodeUsage {\n totalCount: number\n totalCost: number\n successRate: number\n completedCount: number\n failedCount: number\n totalTokensIn: number\n totalTokensOut: number\n totalCredits: number\n periodBasis: string\n}\n\n/** Credit/limit state from `/alpha/billing/credits`. */\nexport interface CommandCodeCredits {\n monthlyCredits: number\n purchasedCredits: number\n freeCredits: number\n /** Five-hour rolling window limits. */\n fiveHour: { used: number; cap: number; exceeded: boolean; resetAt: number }\n /** Weekly window limits. */\n weekly: { used: number; cap: number; exceeded: boolean; resetAt: number }\n}\n\n/** Everything the usage endpoints report, fetched together. */\nexport interface CommandCodeUsageReport {\n account?: CommandCodeAccount\n usage?: CommandCodeUsage\n credits?: CommandCodeCredits\n /** Endpoint failures degrade the report instead of failing it. */\n failures: string[]\n}\n\nexport class CommandCodeAdapter<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends LlmAdapter {\n private catalog: CommandCodeModel[] = []\n private readonly fetchImpl: typeof fetch\n private readonly resolveAttachments: ResolveAttachments | undefined\n\n constructor(private readonly deps: CommandCodeAdapterDeps<C>) {\n super()\n this.fetchImpl = deps.fetchImpl ?? fetch\n this.resolveAttachments = deps.resolveAttachments\n }\n\n /**\n * Command Code is a metered subscription API: 429 (rate limit) and 5xx\n * (transient server errors) are worth retrying at the agent-step boundary,\n * which is where dsh-llm-retry executes the policy returned here. The\n * default policy already retries `RATE_LIMIT` and `SERVER`; declaring it\n * explicitly documents the intent and gives the plugin entry a stable hook\n * to override (e.g. a stricter cap for a metered plan).\n */\n override providerRetryPolicy(_provider: string): ResolvedRetryPolicy {\n return resolveRetryPolicy(undefined, 'llm-commandcode: retryPolicy')\n }\n\n /** Refresh the catalog (live fetch, cache fallback) and return it. */\n private async loadCatalog(signal?: AbortSignal): Promise<CommandCodeModel[]> {\n const { apiBase, modelsCachePath } = this.deps.options()\n try {\n const response = await this.fetchImpl(`${apiBase}/provider/v1/models`, {\n headers: { accept: 'application/json', ...attributionHeaders() },\n signal: signal ?? AbortSignal.timeout(MODELS_TIMEOUT_MS),\n })\n if (!response.ok) {\n throw new Error(`models endpoint returned ${response.status}`)\n }\n this.catalog = parseCatalogResponse(await response.json())\n await writeModelsCache(modelsCachePath, this.catalog).catch(() => undefined)\n } catch (error) {\n if (signal?.aborted) throw error\n // A catalog refresh failure is a degradation, not a request failure:\n // fall back to the last successful catalog on disk (or the in-memory\n // one from an earlier successful load). The adapter still serves any\n // model the user names; only the advisory selector loses entries.\n this.catalog = await readModelsCache(modelsCachePath).catch(() => this.catalog)\n }\n return this.catalog\n }\n\n override async listModels(provider: string): Promise<readonly LlmModelInfo[]> {\n const catalog = await this.loadCatalog()\n return catalog.map((model) => {\n const vision = KNOWN_IMAGE_MODELS.has(model.id)\n return {\n provider,\n id: model.id,\n name: `${model.name} (CC)`,\n // The picker renders `description` under the model name; make the\n // image capability visible up front so a switch in an image-bearing\n // session does not have to be rejected by the host's image gate.\n description: vision ? 'Supports image input' : 'Text only',\n inputModalities: vision ? (['text', 'image'] as const) : (['text'] as const),\n }\n })\n }\n\n override async resolveModel(\n provider: string,\n model: string,\n signal?: AbortSignal,\n ): Promise<LlmResolvedModelInfo> {\n const entry =\n this.catalog.find((m) => m.id === model) ??\n (await this.loadCatalog(signal)).find((m) => m.id === model)\n\n const efforts = KNOWN_EFFORTS[model]\n const vision = KNOWN_IMAGE_MODELS.has(model)\n return {\n provider,\n id: model,\n name: entry ? `${entry.name} (CC)` : model,\n description: vision ? 'Supports image input' : 'Text only',\n inputModalities: vision ? (['text', 'image'] as const) : (['text'] as const),\n ...(entry\n ? {\n context: { contextWindow: entry.contextWindow },\n defaultMaxTokens: Math.min(entry.maxTokens, DEFAULT_GENERATE_MAX_TOKENS),\n }\n : {}),\n // Omit `reasoning` entirely for models without known effort support:\n // the harness then treats the model as having no selectable efforts.\n ...(efforts\n ? {\n reasoning: {\n efforts: efforts.map((effort) => ({\n id: ReasoningEffortId(effort),\n name: effort,\n })),\n },\n }\n : {}),\n }\n }\n\n /**\n * Fetch account, usage, and credit state from the Command Code account\n * endpoints (`/alpha/whoami`, `/alpha/usage/summary`, `/alpha/billing/credits`).\n * Each endpoint degrades independently: a failed one lands in `failures`\n * while the rest still report, so a transient outage never blanks the whole\n * view. Requires a usable API key (throws `MISSING_CREDENTIAL` otherwise).\n */\n async getUsage(): Promise<CommandCodeUsageReport> {\n const connection = this.deps.options()\n const apiKey = await this.deps.resolveApiKey(connection)\n const base = connection.apiBase\n const headers = {\n Authorization: `Bearer ${apiKey}`,\n 'x-command-code-version': COMMAND_CODE_CLI_VERSION,\n 'x-cli-environment': 'production',\n ...attributionHeaders(),\n }\n const failures: string[] = []\n\n const getJson = async (path: string): Promise<Record<string, unknown> | undefined> => {\n try {\n const response = await this.fetchImpl(`${base}${path}`, { headers })\n if (!response.ok) {\n failures.push(`${path}: HTTP ${response.status}`)\n return undefined\n }\n const parsed: unknown = await response.json()\n return isRecord(parsed) ? parsed : undefined\n } catch (error: unknown) {\n failures.push(`${path}: ${error instanceof Error ? error.message : String(error)}`)\n return undefined\n }\n }\n\n const report: CommandCodeUsageReport = { failures }\n\n // whoami -> account identity.\n const whoami = await getJson('/alpha/whoami')\n const whoamiData = whoami && isRecord(whoami.user) ? whoami.user : undefined\n if (whoamiData) {\n report.account = {\n id: stringValue(whoamiData.id) ?? '',\n name: stringValue(whoamiData.name) ?? '',\n userName: stringValue(whoamiData.userName) ?? '',\n }\n }\n\n // usage/summary -> totals.\n const usage = await getJson('/alpha/usage/summary')\n if (usage) {\n report.usage = {\n totalCount: numberValue(usage.totalCount) ?? 0,\n totalCost: numberValue(usage.totalCost) ?? 0,\n successRate: numberValue(usage.successRate) ?? 0,\n completedCount: numberValue(usage.completedCount) ?? 0,\n failedCount: numberValue(usage.failedCount) ?? 0,\n totalTokensIn: numberValue(usage.totalTokensIn) ?? 0,\n totalTokensOut: numberValue(usage.totalTokensOut) ?? 0,\n totalCredits: numberValue(usage.totalCredits) ?? 0,\n periodBasis: stringValue(usage.periodBasis) ?? 'billing-period',\n }\n }\n\n // billing/credits -> credit + window limits.\n const credits = await getJson('/alpha/billing/credits')\n const creditsData = credits && isRecord(credits.credits) ? credits.credits : undefined\n const windowLimits = credits && isRecord(credits.windowLimits) ? credits.windowLimits : undefined\n const fiveHour = windowLimits && isRecord(windowLimits.fiveHour) ? windowLimits.fiveHour : undefined\n const weekly = windowLimits && isRecord(windowLimits.weekly) ? windowLimits.weekly : undefined\n if (creditsData || fiveHour || weekly) {\n report.credits = {\n monthlyCredits: numberValue(creditsData?.monthlyCredits) ?? 0,\n purchasedCredits: numberValue(creditsData?.purchasedCredits) ?? 0,\n freeCredits: numberValue(creditsData?.freeCredits) ?? 0,\n fiveHour: {\n used: numberValue(fiveHour?.used) ?? 0,\n cap: numberValue(fiveHour?.cap) ?? 0,\n exceeded: fiveHour?.exceeded === true,\n resetAt: numberValue(fiveHour?.resetAt) ?? 0,\n },\n weekly: {\n used: numberValue(weekly?.used) ?? 0,\n cap: numberValue(weekly?.cap) ?? 0,\n exceeded: weekly?.exceeded === true,\n resetAt: numberValue(weekly?.resetAt) ?? 0,\n },\n }\n }\n\n return report\n }\n\n async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {\n if (options.stop?.length) {\n // The Command Code wire format has no documented stop field; refuse\n // loudly instead of silently dropping a request field.\n throw new LlmError('Command Code adapter does not support stop sequences', 'UNSUPPORTED_OPTION')\n }\n const hasImages = options.messages.some(hasImageContent)\n // Per-call image byte resolver, set only when this request carries images.\n // Local, not an instance field: concurrent streams must never read each\n // other's resolver.\n let readImage: ((ref: ImageAttachmentRef) => Promise<Uint8Array>) | undefined\n if (hasImages) {\n // Model-capability gate: only models the official registry lists with\n // Vision accept images natively. Command Code's own CLI falls back to a\n // client-side VISION side-call for text-only models; this adapter does\n // not reproduce that interactive feature, so it refuses loudly instead\n // of sending bytes to a model that cannot read them.\n if (!KNOWN_IMAGE_MODELS.has(options.model)) {\n throw new LlmError(\n `Command Code model \"${options.model}\" does not support image input;`\n + ' switch to a Vision-capable model (see the model registry)',\n 'UNSUPPORTED_CONTENT',\n )\n }\n // Attachment seam: images arrive as durable references; resolving them\n // requires the host's attachment service.\n const attachments = this.resolveAttachments?.()\n if (attachments === undefined) {\n throw new LlmError(\n 'Command Code image input requires the durable attachment service',\n 'UNSUPPORTED_CONTENT',\n )\n }\n readImage = (ref) => attachments.readImage(ref).then((stored) => stored.data)\n }\n\n const connection = this.deps.options()\n const apiKey = await this.deps.resolveApiKey(connection)\n const entry = this.catalog.find((m) => m.id === options.model)\n const modelMax = entry?.maxTokens ?? DEFAULT_MAX_OUTPUT_TOKENS\n const maxTokens = Math.min(\n options.maxTokens ?? modelMax,\n modelMax,\n DEFAULT_GENERATE_MAX_TOKENS,\n )\n\n const effort = options.reasoningEffort as string | undefined\n const supported = KNOWN_EFFORTS[options.model]\n const reasoningEffort =\n effort && effort !== 'off' && supported?.includes(effort) ? effort : undefined\n\n const systemText = [\n options.system ?? '',\n ...options.messages\n .filter((m) => m.role === 'system')\n .map((m) => m.content.map(blockText).filter(Boolean).join('\\n')),\n ]\n .filter(Boolean)\n .join('\\n\\n')\n\n const body = {\n config: {\n workingDir: connection.workingDir,\n date: new Date().toISOString().split('T')[0],\n environment: `${process.platform}-${process.arch}, Node.js ${process.version}`,\n structure: [],\n isGitRepo: false,\n currentBranch: '',\n mainBranch: '',\n gitStatus: '',\n recentCommits: [],\n },\n memory: null,\n taste: null,\n skills: null,\n params: {\n model: options.model,\n messages: await messagesToCC(options.messages, readImage),\n tools: (options.tools ?? []).map((tool) => ({\n type: 'function',\n name: tool.name,\n description: tool.description,\n input_schema: tool.parameters,\n })),\n system: systemText,\n max_tokens: maxTokens,\n temperature: options.temperature ?? 0.3,\n stream: true,\n ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),\n },\n threadId: randomUUID(),\n }\n\n let response: Response\n try {\n response = await this.fetchImpl(`${connection.apiBase}/alpha/generate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n 'x-command-code-version': COMMAND_CODE_CLI_VERSION,\n 'x-cli-environment': 'production',\n 'x-project-slug': projectSlugFromPath(connection.workingDir),\n 'x-taste-learning': 'true',\n 'x-co-flag': 'false',\n ...attributionHeaders(),\n },\n body: JSON.stringify(body),\n // The generation can legitimately run long, but the connection phase\n // must not hang forever: bound the wait for the first response byte.\n signal: options.signal\n ? AbortSignal.any([options.signal, AbortSignal.timeout(connection.requestTimeoutMs)])\n : AbortSignal.timeout(connection.requestTimeoutMs),\n }) } catch (error: unknown) {\n // Caller cancellation must propagate as-is, not be relabeled.\n if (options.signal?.aborted) throw error\n // The timeout signal above aborts with a TimeoutError. Because the\n // caller's own signal was already ruled out, any TimeoutError here came\n // from our request deadline — classify it precisely.\n if (error instanceof DOMException && error.name === 'TimeoutError') {\n throw new LlmError(\n `Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms`\n + `: ${errorChain(error)}`,\n 'TIMEOUT',\n { cause: error },\n )\n }\n // fetch wraps every transport failure (DNS, refused connection, TLS,\n // proxy, reset) in a bare `TypeError: fetch failed` whose actionable\n // detail lives on `cause`. Include the full chain so the failure reason\n // shown in the web UI (which renders only the message, not `cause`)\n // names the real root cause instead of a generic wrapper.\n throw new LlmError(\n `Command Code API request to ${connection.apiBase}/alpha/generate failed: ${errorChain(error)}`,\n 'TRANSPORT',\n { cause: error },\n )\n }\n\n if (!response.ok) {\n const errText = await response.text().catch(() => '')\n // Command Code folds several business rejections into 403 (plan limits,\n // CLI version, model access). Prefer the machine-readable `error.code`\n // when present; the status alone cannot distinguish them.\n let providerCode: string | undefined\n try {\n const parsed: unknown = JSON.parse(errText)\n if (isRecord(parsed) && isRecord(parsed.error)) {\n providerCode = stringValue(parsed.error.code)\n }\n } catch {\n // Plain-text bodies: rely on the status mapping below.\n }\n const detail = providerCode ?? `HTTP ${response.status}`\n if (response.status === 401) {\n // An invalid or missing credential is a config problem, not a\n // transport failure: retrying it identically cannot succeed.\n throw new LlmError(\n `Command Code API error 401 (${detail}): the API key is missing or invalid — check the`\n + ' key stored for COMMANDCODE_API_KEY (Models page) or the auth file',\n 'INVALID_CREDENTIAL',\n { status: 401 },\n )\n }\n throw new LlmError(\n `Command Code API error ${response.status}${detail === `HTTP ${response.status}` ? '' : ` (${detail})`}: ${errText.slice(0, 500)}`,\n response.status === 429 ? 'RATE_LIMIT' : 'PROVIDER_HTTP_ERROR',\n { status: response.status },\n )\n }\n if (!response.body) {\n throw new LlmError('Command Code API returned no response body', 'PROVIDER_PROTOCOL_ERROR')\n }\n\n // --- SSE/JSONL event stream -> harness StreamChunk protocol ---\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n\n // Stream idle watchdog: a generation that stalls this long has a dead\n // connection (the API keeps the socket open between reasoning/text\n // bursts). reader.cancel() unblocks a pending read(), which the loop then\n // turns into a TIMEOUT failure instead of hanging forever.\n let idleTimer: ReturnType<typeof setTimeout> | undefined\n let idleFired = false\n const armIdle = () => {\n if (idleTimer !== undefined) clearTimeout(idleTimer)\n idleTimer = setTimeout(() => {\n idleFired = true\n void reader.cancel().catch(() => undefined)\n }, connection.streamIdleTimeoutMs)\n }\n const clearIdle = () => {\n if (idleTimer !== undefined) {\n clearTimeout(idleTimer)\n idleTimer = undefined\n }\n }\n\n // Block assembly state: at most one text block and one reasoning block\n // are open at a time (same assumption as the pi plugin).\n let nextIndex = 0\n let textIndex = -1\n let textContent = ''\n let reasoningIndex = -1\n let reasoningContent = ''\n let sawContent = false\n\n const closeText = function* (): Generator<StreamChunk> {\n if (textIndex < 0) return\n yield {\n type: 'block-end',\n index: textIndex,\n block: { type: 'text', text: textContent },\n }\n textIndex = -1\n textContent = ''\n }\n const closeReasoning = function* (): Generator<StreamChunk> {\n if (reasoningIndex < 0) return\n yield {\n type: 'block-end',\n index: reasoningIndex,\n block: { type: 'reasoning', text: reasoningContent },\n }\n reasoningIndex = -1\n reasoningContent = ''\n }\n\n const handleEvent = (event: unknown): StreamChunk[] => {\n const chunks: StreamChunk[] = []\n if (!isRecord(event)) return chunks\n\n switch (event.type) {\n case 'text-delta': {\n chunks.push(...closeReasoning())\n if (textIndex < 0) {\n textIndex = nextIndex++\n chunks.push({ type: 'block-start', index: textIndex, blockType: 'text' })\n }\n const delta = stringValue(event.text) ?? ''\n textContent += delta\n sawContent = true\n chunks.push({ type: 'text-delta', index: textIndex, text: delta })\n break\n }\n case 'reasoning-delta': {\n chunks.push(...closeText())\n if (reasoningIndex < 0) {\n reasoningIndex = nextIndex++\n chunks.push({ type: 'block-start', index: reasoningIndex, blockType: 'reasoning' })\n }\n const delta = stringValue(event.text) ?? ''\n reasoningContent += delta\n chunks.push({ type: 'reasoning-delta', index: reasoningIndex, text: delta })\n break\n }\n case 'reasoning-start':\n chunks.push(...closeText())\n break\n case 'reasoning-end':\n chunks.push(...closeReasoning())\n break\n case 'tool-call': {\n chunks.push(...closeText(), ...closeReasoning())\n const id = stringValue(event.toolCallId) ?? randomUUID()\n const name = stringValue(event.toolName) ?? ''\n const args = JSON.stringify(recordOrEmpty(event.input ?? event.args ?? event.arguments))\n const index = nextIndex++\n sawContent = true\n chunks.push(\n { type: 'block-start', index, blockType: 'tool-call' },\n { type: 'tool-call-delta', index, id: CallId(id), name, argumentsDelta: args },\n {\n type: 'block-end',\n index,\n block: { type: 'tool-call', id: CallId(id), name, arguments: args },\n },\n )\n break\n }\n case 'finish': {\n chunks.push(...closeText(), ...closeReasoning())\n const usage = isRecord(event.totalUsage) ? event.totalUsage : undefined\n if (usage) {\n const details = isRecord(usage.inputTokenDetails) ? usage.inputTokenDetails : undefined\n const totalInput = numberValue(usage.inputTokens) ?? 0\n const cacheRead = numberValue(details?.cacheReadTokens) ?? 0\n const cacheWrite = numberValue(details?.cacheWriteTokens) ?? 0\n // Harness TokenUsage counts are disjoint: uncached input only.\n const tokenUsage: TokenUsage = {\n inputTokens:\n numberValue(details?.noCacheTokens) ?? Math.max(0, totalInput - cacheRead - cacheWrite),\n outputTokens: numberValue(usage.outputTokens) ?? 0,\n cacheReadTokens: cacheRead,\n cacheWriteTokens: cacheWrite,\n }\n chunks.push({ type: 'usage', usage: tokenUsage })\n }\n chunks.push({ type: 'finish', reason: mapFinishReason(event.finishReason) })\n break\n }\n case 'error': {\n const detail = isRecord(event.error)\n ? (stringValue(event.error.message) ?? JSON.stringify(event.error))\n : (stringValue(event.error) ?? stringValue(event.message) ?? 'Stream error')\n throw new LlmError(`Command Code stream error: ${detail}`, 'PROVIDER_STREAM_ERROR')\n }\n }\n return chunks\n }\n\n try {\n let finished = false\n for (;;) {\n let read: ReadableStreamReadResult<Uint8Array>\n armIdle()\n try {\n read = await reader.read()\n } catch (error: unknown) {\n // A mid-stream transport failure (connection reset, TLS teardown)\n // surfaces here. Caller cancellation propagates as-is.\n if (options.signal?.aborted) throw error\n throw new LlmError(\n `Command Code API stream from ${connection.apiBase} failed while reading: ${errorChain(error)}`,\n 'TRANSPORT',\n { cause: error },\n )\n } finally {\n clearIdle()\n }\n const { done, value } = read\n if (done) {\n // The idle watchdog cancels the reader to unblock a stalled read;\n // cancel() resolves a pending read() as done, so a done here after\n // the watchdog fired is a timeout, not a normal stream end.\n if (idleFired) {\n throw new LlmError(\n `Command Code API stream from ${connection.apiBase} was idle for ${connection.streamIdleTimeoutMs}ms`\n + ' (no events) and was treated as a dead connection',\n 'TIMEOUT',\n )\n }\n if (buffer.trim()) for (const chunk of handleEvent(parseStreamEventLine(buffer))) yield chunk\n break\n }\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n const chunks = handleEvent(parseStreamEventLine(line))\n for (const chunk of chunks) {\n yield chunk\n if (chunk.type === 'finish') finished = true\n }\n }\n if (finished) break\n }\n if (!finished) {\n // Stream ended without a finish event: close open blocks and\n // terminate according to the adapter contract (usage, then finish).\n yield* closeText()\n yield* closeReasoning()\n if (!sawContent) {\n throw new LlmError('Command Code returned an empty response', 'EMPTY_RESPONSE')\n }\n yield { type: 'finish', reason: { kind: 'stop' } }\n }\n } finally {\n clearIdle()\n await reader.cancel().catch(() => undefined)\n reader.releaseLock()\n }\n }\n}\n\nfunction mapFinishReason(reason: unknown): FinishReason {\n if (reason === 'tool-calls') return { kind: 'tool-calls' }\n if (\n reason === 'length' ||\n reason === 'max_tokens' ||\n reason === 'max-tokens' ||\n reason === 'max_output_tokens'\n ) {\n return { kind: 'max-tokens' }\n }\n return { kind: 'stop' }\n}\n","/**\n * `/commandcode` slash command — account usage dashboard.\n *\n * /commandcode show account, usage, and credit state\n * /commandcode status same as bare `/commandcode`\n *\n * Backed by the Command Code account endpoints the official CLI uses\n * (`/alpha/whoami`, `/alpha/usage/summary`, `/alpha/billing/credits`),\n * exposed through `CommandCodeAdapter.getUsage()`.\n *\n * @module dsh-commandcode-provider/commands\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only import that loads the module augmentation (`ctx.commands`).\nimport type { CommandDefinition } from '@deepseek-ai/dsh-commands'\nimport { CommandCodeAdapter } from './adapter.ts'\nimport type { CommandCodeConnectionOptions, CommandCodeUsageReport } from './adapter.ts'\n\n/** Everything the command needs beyond the adapter itself. */\nexport interface CommandCodeCommandDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {\n /** The registered adapter (for getUsage / listModels). */\n adapter: CommandCodeAdapter<C>\n}\n\n/** Format a dollar amount. */\nfunction money(value: number): string {\n return `$${value.toFixed(4)}`\n}\n\n/** Format a dollar amount compactly (2 decimals). */\nfunction moneyShort(value: number): string {\n return `$${value.toFixed(2)}`\n}\n\n/** Format a token count with thousands separators. */\n/** Format a large token count compactly (1.9亿 style). */\nfunction tokensCompact(value: number): string {\n if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`\n if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`\n if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`\n return String(value)\n}\n\n/** Format a millis timestamp as a local date. */\nfunction resetLabel(ms: number): string {\n if (ms <= 0) return 'n/a'\n return new Date(ms).toLocaleString()\n}\n\n/**\n * A 10-cell horizontal bar: `██████████` for 100%, `███░░░░░░░` for ~33%.\n * Handles caps of 0 (no limit) and out-of-range values.\n */\nfunction bar(used: number, cap: number): string {\n if (cap <= 0) return '—'\n const ratio = Math.max(0, Math.min(1, used / cap))\n const filled = Math.round(ratio * 10)\n return '█'.repeat(filled) + '░'.repeat(10 - filled)\n}\n\n/** Render the usage report as a structured, aligned, bar-chart text view. */\nfunction renderReport(report: CommandCodeUsageReport): string {\n const lines: string[] = []\n const account = report.account ? ` (${report.account.userName || report.account.name})` : ''\n\n lines.push(`📊 Command Code 用量${account}`, '')\n\n if (report.usage) {\n const u = report.usage\n lines.push(\n '── 请求 ──────────────────────────────',\n ` 💬 请求 ${u.completedCount} 次 / 失败 ${u.failedCount} 成功率 ${u.successRate}%`,\n ` 💰 花费 ${money(u.totalCost)} (${moneyShort(u.totalCredits)} credits)`,\n ` 🔤 Token ${tokensCompact(u.totalTokensIn)} 入 / ${tokensCompact(u.totalTokensOut)} 出`,\n '',\n )\n }\n\n if (report.credits) {\n const c = report.credits\n const monthlyPct = c.monthlyCredits > 0\n ? `${((c.monthlyCredits / (c.monthlyCredits + c.purchasedCredits)) * 100).toFixed(0)}%`\n : '—'\n lines.push(\n '── 信用 ──────────────────────────────',\n ` 💳 月额度 ${moneyShort(c.monthlyCredits)} (已购 ${moneyShort(c.purchasedCredits)} / 赠送 ${moneyShort(c.freeCredits)})`,\n ` └ ${bar(c.monthlyCredits, c.monthlyCredits + c.purchasedCredits)} ${monthlyPct}`,\n '',\n '── 窗口用量 ──────────────────────────',\n ` ⏱ 5 小时 ${moneyShort(c.fiveHour.used)} / ${moneyShort(c.fiveHour.cap)}${c.fiveHour.exceeded ? ' ⚠️ 超限!' : ''}`,\n ` └ ${bar(c.fiveHour.used, c.fiveHour.cap)} 重置 ${resetLabel(c.fiveHour.resetAt)}`,\n ` 📅 每周 ${moneyShort(c.weekly.used)} / ${moneyShort(c.weekly.cap)}${c.weekly.exceeded ? ' ⚠️ 超限!' : ''}`,\n ` └ ${bar(c.weekly.used, c.weekly.cap)} 重置 ${resetLabel(c.weekly.resetAt)}`,\n '',\n )\n }\n\n if (report.failures.length > 0) {\n lines.push(`⚠️ 部分端点失败: ${report.failures.join('; ')}`, '')\n }\n if (!report.account && !report.usage && !report.credits) {\n lines.push('(no data — check your API key)', '')\n }\n\n return lines.join('\\n').trimEnd()\n}\n\n/** The one registered `/commandcode` command. */\nexport function commandDefinition<C extends CommandCodeConnectionOptions>(\n deps: CommandCodeCommandDeps<C>,\n): CommandDefinition {\n const { adapter } = deps\n return {\n name: 'commandcode',\n description: 'Command Code account usage dashboard',\n input: { hint: '[status]' },\n handler: async () => {\n try {\n const report = await adapter.getUsage()\n return { kind: 'success', text: renderReport(report) }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n kind: 'error',\n text: `Could not fetch Command Code usage: ${message}`,\n }\n }\n },\n }\n}\n\n/** Register the command on `ctx.commands` (called from the plugin entry). */\nexport function applyCommands<C extends CommandCodeConnectionOptions>(\n ctx: Context,\n deps: CommandCodeCommandDeps<C>,\n): void {\n ctx.commands.register(commandDefinition(deps))\n}\n","/**\n * dsh-commandcode-provider — DeepSeek Harness LLM provider plugin for Command\n * Code (unofficial; ported from pi-commandcode-provider@0.5.1).\n *\n * Registers the `commandcode` provider route on `ctx.llm` and declares it in\n * the configurable-provider directory, so the web Models page shows a\n * \"Command Code\" card with an API-key field and the model picker lists the\n * live Command Code model catalog. Connection facts resolve per request over\n * the optional `llm-commandcode` user-settings section and the credential\n * seam, so a changed key, endpoint, or cache path reaches the next request\n * without a restart.\n *\n * ```yaml\n * - id: llm-commandcode\n * name: \"@mars-sea/dsh-commandcode-provider\"\n * config:\n * apiKeyEnv: COMMANDCODE_API_KEY\n * ```\n *\n * The `name` is the full package specifier as installed in the profile's\n * node_modules: the loader imports it as a module, and pnpm links packages by\n * their true (scoped) name — a bare `dsh-commandcode-provider` fails to\n * resolve (ERR_MODULE_NOT_FOUND) and crashes the app on boot. The value must\n * be quoted in YAML: an unquoted scalar starting with `@` fails to parse.\n *\n * @module dsh-commandcode-provider\n */\n\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport z from '@deepseek-ai/schemastery'\nimport { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'\nimport { assertUsableApiKey, LlmError } from '@deepseek-ai/dsh-llm'\nimport { credentialRef, type CredentialRef } from '@deepseek-ai/dsh-credentials'\nimport { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment'\nimport { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'\nimport { CommandCodeAdapter, DEFAULT_API_BASE, resolveAuthFileApiKey } from './adapter.ts'\nimport { DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS } from './adapter.ts'\nimport type { CommandCodeConnectionOptions } from './adapter.ts'\nimport { applyCommands } from './commands.ts'\n\nexport {\n COMMAND_CODE_CLI_VERSION,\n DEFAULT_API_BASE,\n DEFAULT_GENERATE_MAX_TOKENS,\n DEFAULT_MAX_OUTPUT_TOKENS,\n DEFAULT_REQUEST_TIMEOUT_MS,\n DEFAULT_STREAM_IDLE_TIMEOUT_MS,\n CommandCodeAdapter,\n KNOWN_EFFORTS,\n KNOWN_IMAGE_MODELS,\n projectSlugFromPath,\n resolveAuthFileApiKey,\n} from './adapter.ts'\nexport type { CommandCodeAdapterDeps, CommandCodeConnectionOptions, CommandCodeUsageReport, ResolveAttachments } from './adapter.ts'\nexport { applyCommands, commandDefinition } from './commands.ts'\nexport type { CommandCodeCommandDeps } from './commands.ts'\n\nexport const name = 'llm-commandcode'\nexport const inject = ['llm']\n\nconst NS = settingsNamespace('llm-commandcode')\nconst DEFAULT_API_KEY_ENV = 'COMMANDCODE_API_KEY'\n\n/** The single provider route this plugin owns. */\nexport const PROVIDER = 'commandcode'\n/** Default models cache path (mirrors the pi plugin's on-disk cache). */\nexport const DEFAULT_MODELS_CACHE_PATH = join(homedir(), '.commandcode', 'models-cache.json')\n\n/**\n * Plugin config, validated by the same-named schemastery schema and doubling\n * as the `llm-commandcode` settings-section shape. Every field is optional:\n * a missing API key resolves through {@link Config.apiKeyEnv} at each request\n * (the web Models page writes it), with the official Command Code CLI auth\n * file (`~/.commandcode/auth.json`) as the last fallback.\n */\nexport interface Config {\n /** Credential reference (environment-variable name) resolved per request; defaults to `COMMANDCODE_API_KEY`. */\n apiKeyEnv?: string\n /** Literal API key override (composition config only); takes precedence over `apiKeyEnv`. */\n apiKey?: string\n /** API base; defaults to the public Command Code Provider API. */\n apiBase?: string\n /** Working directory reported to the API; defaults to the process cwd. */\n workingDir?: string\n /** Model catalog cache path; defaults to `~/.commandcode/models-cache.json`. */\n modelsCachePath?: string\n /** Milliseconds to wait for the generate response's first byte; defaults to 60s. */\n requestTimeoutMs?: number\n /** Milliseconds a stream may stall before being treated as a dead connection; defaults to 120s. */\n streamIdleTimeoutMs?: number\n}\n\nexport const Config: z<Config> = z.object({\n apiKeyEnv: z.string().role('credential-ref').default(DEFAULT_API_KEY_ENV),\n apiKey: z.string(),\n apiBase: z.string(),\n workingDir: z.string(),\n modelsCachePath: z.string(),\n requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),\n streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),\n})\n\n/** One resolution's complete request facts: connection plus credential reference. */\nexport interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {\n apiKeyEnv: CredentialRef\n}\n\n/**\n * The one explicit resolve step from raw config to validated connection\n * facts. Programmatic construction may bypass Schemastery normalization, so\n * every default is re-judged here — for the composition entry at load and for\n * each settings snapshot at its first use.\n */\nexport function resolveAdapterOptions(config: Config): ResolvedCommandCodeOptions {\n return {\n apiKeyEnv: credentialRef(config.apiKeyEnv ?? DEFAULT_API_KEY_ENV),\n apiBase: config.apiBase ?? DEFAULT_API_BASE,\n workingDir: config.workingDir ?? process.cwd(),\n modelsCachePath: config.modelsCachePath ?? DEFAULT_MODELS_CACHE_PATH,\n requestTimeoutMs: config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,\n streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS,\n }\n}\n\nexport function apply(ctx: Context, config: Config): void {\n let current: () => Config = () => config\n let lastRaw: Config | undefined\n let lastGood: ResolvedCommandCodeOptions | undefined\n const options = (): ResolvedCommandCodeOptions => {\n const raw = current()\n if (raw === lastRaw && lastGood !== undefined) return lastGood\n const next = resolveAdapterOptions(raw)\n lastRaw = raw\n lastGood = next\n return next\n }\n options()\n\n const resolveApiKey = async (connection: ResolvedCommandCodeOptions): Promise<string> => {\n // 1. A literal key in composition config wins outright.\n const literal = current().apiKey\n if (literal) return assertUsableApiKey(literal, 'llm-commandcode', 'config.apiKey')\n // 2. The credential seam (web Models page) or the trusted environment.\n const ref = connection.apiKeyEnv\n const credentials = ctx.get('credentials')\n if (credentials !== undefined) {\n const hit = await credentials.resolve(ref)\n if (hit !== undefined) return assertUsableApiKey(hit.value, 'llm-commandcode', ref)\n } else {\n const ambient = launchEnvironmentOf(ctx).get(ref)\n if (ambient !== undefined && ambient.value.length > 0) {\n return assertUsableApiKey(ambient.value, 'llm-commandcode', ref)\n }\n }\n // 3. Last resort: reuse the official Command Code CLI login (~/.commandcode/auth.json).\n const authFileKey = resolveAuthFileApiKey()\n if (authFileKey) return assertUsableApiKey(authFileKey, 'llm-commandcode', '~/.commandcode/auth.json')\n throw new LlmError(\n `llm-commandcode: no API key for provider route \"${PROVIDER}\"; store ${ref} through the`\n + ' credentials service (the web Models page writes it), export it in the launching'\n + ' environment, set config.apiKey, or run `command-code login` to write'\n + ' ~/.commandcode/auth.json',\n 'MISSING_CREDENTIAL',\n )\n }\n\n const adapter = new CommandCodeAdapter({\n options,\n resolveApiKey,\n // The durable attachment service carries image bytes referenced by\n // ImageBlock; resolved lazily only when a request actually has images.\n resolveAttachments: () => {\n const attachments = ctx.get('attachments')\n return attachments === undefined ? undefined : attachments\n },\n })\n // The Models page card: a configurable provider with a settings address.\n // settingsPath [] means the whole `llm-commandcode` section configures it.\n ctx.llm.registerConfigurableProviders([\n { provider: PROVIDER, displayName: 'Command Code', settingsNs: NS, settingsPath: [] },\n ])\n // The live route: this is what makes models requestable under `commandcode`.\n ctx.llm.registerAdapter([PROVIDER], adapter)\n\n // The /commandcode usage command rides the optional `commands` service: a\n // child fiber injects it, so it registers whenever the profile mounts\n // dsh-commands and the fiber simply never activates when it does not.\n ctx.inject(['commands'], (commandCtx) => {\n applyCommands(commandCtx, { adapter })\n })\n\n installSettingsSection(ctx, NS, Config, config, {\n setSource: (source) => {\n current = source\n },\n // Everything the adapter reads is resolved per request, so a settings\n // change needs no registration-level action.\n onChange: () => {},\n })\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mars-sea/dsh-commandcode-provider",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Unofficial DeepSeek Harness LLM provider plugin for Command Code, ported from pi-commandcode-provider (MIT). Registers the 'commandcode' provider route with a Models-page card and live model catalog.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -10,9 +10,23 @@
|
|
|
10
10
|
"types": "./lib/index.d.ts",
|
|
11
11
|
"default": "./lib/index.js"
|
|
12
12
|
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"default": "./lib/client.js"
|
|
15
|
+
},
|
|
13
16
|
"./src/*": "./src/*",
|
|
14
17
|
"./package.json": "./package.json"
|
|
15
18
|
},
|
|
19
|
+
"dsh": {
|
|
20
|
+
"bundle": {
|
|
21
|
+
"patch": "./cordis.patch.yml"
|
|
22
|
+
},
|
|
23
|
+
"client": {
|
|
24
|
+
"inject": [
|
|
25
|
+
"@deepseek-ai/dsh-client-connection"
|
|
26
|
+
],
|
|
27
|
+
"platform": "web"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
16
30
|
"files": [
|
|
17
31
|
"lib",
|
|
18
32
|
"cordis.patch.yml",
|
|
@@ -27,11 +41,6 @@
|
|
|
27
41
|
"typecheck": "tsc --noEmit",
|
|
28
42
|
"test": "node --import tsx --test tests/**/*.test.ts"
|
|
29
43
|
},
|
|
30
|
-
"dsh": {
|
|
31
|
-
"bundle": {
|
|
32
|
-
"patch": "./cordis.patch.yml"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
44
|
"engines": {
|
|
36
45
|
"node": ">=22"
|
|
37
46
|
},
|
|
@@ -60,6 +69,7 @@
|
|
|
60
69
|
"license": "MIT",
|
|
61
70
|
"peerDependencies": {
|
|
62
71
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
72
|
+
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6",
|
|
63
73
|
"@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
|
|
64
74
|
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.6",
|
|
65
75
|
"@deepseek-ai/dsh-launch-environment": "^0.1.0-rc.6",
|
|
@@ -70,6 +80,7 @@
|
|
|
70
80
|
},
|
|
71
81
|
"devDependencies": {
|
|
72
82
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
83
|
+
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6",
|
|
73
84
|
"@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
|
|
74
85
|
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.6",
|
|
75
86
|
"@deepseek-ai/dsh-launch-environment": "^0.1.0-rc.6",
|