dsh-oc-tui 0.1.2 → 0.1.3

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.
@@ -1,401 +1,417 @@
1
- # DeepSeek Harness TUI 用户手册
2
-
3
- `dsh-oc-tui` 是运行在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(dsh)进程内的**终端界面(TUI)插件**,以 profile app 的形式挂载。它把 dsh 的持久事件流渲染到终端里——流式回复、工具卡片、待办列表、思考块——并把你输入的内容送回 agent。
4
-
5
- 它**不是**独立的 agent 运行时:模型路由、工具执行、授权、命令、会话存储和凭据仍由 dsh 负责,插件只负责终端里的输入与显示。
6
-
7
- 本手册面向**安装与使用**,开发者回路见[第 8 节](#8-开发)。
8
-
9
- ## 目录
10
-
11
- - [1. 功能特性](#1-功能特性)
12
- - [2. 安装前准备](#2-安装前准备)
13
- - [3. 安装](#3-安装)
14
- - [4. 启动](#4-启动)
15
- - [5. 使用](#5-使用)
16
- - [5.1 快捷键](#51-快捷键)
17
- - [5.2 斜杠命令](#52-斜杠命令)
18
- - [5.3 交互式提示:授权与提问](#53-交互式提示授权与提问)
19
- - [5.4 思考强度](#54-思考强度)
20
- - [5.5 上下文仪表与遥测](#55-上下文仪表与遥测)
21
- - [5.6 设置菜单](#56-设置菜单)
22
- - [5.7 程序内更新](#57-程序内更新)
23
- - [6. 工作原理](#6-工作原理)
24
- - [7. 故障排查](#7-故障排查)
25
- - [8. 开发](#8-开发)
26
- - [9. 已知限制](#9-已知限制)
27
- - [10. 目录结构](#10-目录结构)
28
- - [11. 许可证](#11-许可证)
29
-
30
- ## 1. 功能特性
31
-
32
- | | |
33
- | --- | --- |
34
- | **持久会话** | 新建、恢复、列出、删除会话;转录内容从持久事件日志重建,恢复后的会话与离开时完全一致。 |
35
- | **实时流式** | 回复与思考逐 token 流式渲染;思考内容在独立可折叠框中,流式期间保持折叠。 |
36
- | **工具活动** | 工具卡片带一行摘要(`read src/app.ts`、`run npm test`),运行中显示流动 spinner,结果按 Markdown 渲染。 |
37
- | **交互式提问** | 模型可以暂停并向你提问——选项列表、多选、自由文本、可滚动的计划评审,全部在终端内完成。 |
38
- | **内联授权** | `approval/request` 询问用 `y` / `n` 直接回答,无需离开界面。 |
39
- | **遥测页脚** | 会话 token、平均首 token 时间(TTFT)、解码吞吐、KV 缓存命中率,均由持久事件折叠得出。 |
40
- | **上下文仪表** | 实时上下文占用(`ctx ▓▓░░ 32K/128K 25%`),点击可展开构成明细。 |
41
- | **思考强度** | `Tab` 循环切换当前模型真实支持的推理等级;`Ctrl+E` 打开滑块。选择按请求应用并持久化。 |
42
- | **共享设置** | 与 WebUI 相同的 Host 设置命名空间——通用、会话、各 provider 模型配置、凭据——持久化到 `$DSH_HOME/settings.yaml`。 |
43
- | **程序内更新** | 在 TUI 内检测并切换 `@deepseek-ai/dsh` 与 `dsh-oc-tui` 版本,Windows 上采用延迟安装避免静默损坏。 |
44
- | **零依赖终端引擎** | raw 模式、备用屏幕、差分单元缓冲、真彩 ANSI、CJK 宽度感知、SGR + 旧式 X10 鼠标解码、IME 光标锚定。 |
45
-
46
- ## 2. 安装前准备
47
-
48
- | 项目 | 要求 |
49
- | --- | --- |
50
- | Node.js | >= 22 |
51
- | dsh CLI | 已安装 `@deepseek-ai/dsh`,例如 `npm install -g @deepseek-ai/dsh` |
52
- | pnpm | 已加入 PATH(`dsh plugin` 内部转发给 pnpm) |
53
- | 终端 | 交互式终端(Windows Terminal / ConPTY、iTerm2、GNOME Terminal 等) |
54
- | 模型配置 | `$DSH_HOME/settings.yaml` 与 `$DSH_HOME/.credentials.yaml` 中已配置可用的模型路由(与 Web GUI 共用) |
55
-
56
- ```sh
57
- dsh --version
58
- pnpm --version
59
- ```
60
-
61
- **兼容性**:本插件已在 dsh `0.1.2-rc.1`(以及 `0.1.1-rc.2`)上验证。dsh 0.1.2 重命名了部分会话 API——`Session.events` 改为 `snapshotEvents()`——插件会读取宿主实际提供的那个访问器,因此同一份构建可同时服务这两条版本线。
62
-
63
- ## 3. 安装
64
-
65
- ### 3.1 从 npm 安装
66
-
67
- 本包已发布到 npm,包名 [`dsh-oc-tui`](https://www.npmjs.com/package/dsh-oc-tui)。装进 `tui` profile:
68
-
69
- ```sh
70
- dsh plugin --profile tui add -w dsh-oc-tui
71
- ```
72
-
73
- 也可以把启动器装到全局,这样 `dsh-oc-tui` 命令就在 PATH 上,它等价于 `dsh --profile tui`:
74
-
75
- ```sh
76
- npm install -g dsh-oc-tui
77
- ```
78
-
79
- 本插件同时已收录进 [awesome-dsh-plugin](https://awesome-dsh-plugin.com/zh/p/rayafriandion/dsh-oc-tui/) 插件市场(分类:UI 增强)。
80
-
81
- ### 3.2 版本渠道:只有稳定版
82
-
83
- **npm 包与 awesome-dsh-plugin 市场收录都只提供稳定版(stable release),不含任何预发布(pre-release / rc / alpha / beta)。** 因此 `npm install` 拿到的一定是最近一个稳定版,而不会是候选版本。
84
-
85
- 本手册描述的是当前源码树,可能领先于已发布版本——手册里写到的功能,只有在对应版本发布到 npm 之后才能从稳定版拿到。
86
-
87
- 想用预发布版本、或本仓库里尚未发布的改动,需要显式从源码安装:
88
-
89
- ```sh
90
- npm pack # 生成 dsh-oc-tui-<版本>.tgz
91
- dsh plugin --profile tui add -w ./dsh-oc-tui-<版本>.tgz
92
- ```
93
-
94
- ### 3.3 一键安装脚本
95
-
96
- 仓库内置 Linux/macOS 与 Windows 安装脚本:检查 Node.js >= 22、确保 pnpm 可用、把插件装进 `tui` profile,加 `--launcher`/`-Launcher` 还会全局安装 `dsh-oc-tui` 启动器。
97
-
98
- ```sh
99
- # Linux / macOS
100
- curl -fsSL https://raw.githubusercontent.com/rayafriandion/dsh-oc-tui/main/install.sh | bash
101
- ```
102
-
103
- ```powershell
104
- # Windows(PowerShell)
105
- powershell -ExecutionPolicy Bypass -Command "iwr https://raw.githubusercontent.com/rayafriandion/dsh-oc-tui/main/install.ps1 -OutFile install.ps1; & .\install.ps1"
106
- ```
107
-
108
- 在源码目录里也可直接运行 `./install.sh`(Linux/macOS)或 `.\install.ps1`(Windows)。其他选项:`--local`(`-Local`)安装当前源码目录;`--source <spec>`(`-Source <spec>`)指定自定义来源;`--profile <name>`(`-Profile <name>`)指定非默认 profile。
109
-
110
- ### 3.4 从本地目录或 tarball 安装
111
-
112
- ```sh
113
- npm pack # 生成 dsh-oc-tui-<版本>.tgz
114
- dsh plugin --profile tui add -w ./dsh-oc-tui-<版本>.tgz
115
- ```
116
-
117
- `dsh plugin` 会把相对路径按你执行命令时所在的目录解析后再转发给 pnpm。
118
-
119
- ### 3.5 为什么必须带 `-w`
120
-
121
- profile 目录把自己声明为 pnpm workspace 根(`pnpm-workspace.yaml` → `packages: [.]`),因此裸 `add` 会被 pnpm 拒绝并报 `ERR_PNPM_ADDING_TO_ROOT`。加上 `-w` 后依赖写入 profile 自己的 manifest——这正是它应有的位置。之后 `dsh plugin` 会把 `dsh.profile.bundles` 与已安装状态对齐。
122
-
123
- ### 3.6 安装做了什么
124
-
125
- 1. `dsh plugin` 首次使用时初始化 `$DSH_HOME/profiles/tui`(`@deepseek-ai/dsh-base` 加一个空的用户补丁层)。
126
- 2. pnpm 把 `dsh-oc-tui` 装进该 profile 的 `node_modules`。
127
- 3. 因为本包声明了 `dsh.bundle.patch`,dsh 自动把 `dsh-oc-tui` 追加到 `dsh.profile.bundles`。
128
- 4. `dsh --profile tui` 组合 base 层、本 bundle 的行、以及你自己的补丁——无需手动编辑任何配置。
129
-
130
- ### 3.7 验证安装
131
-
132
- ```sh
133
- dsh --profile tui --dump-config
134
- ```
135
-
136
- 输出中应出现 `# == dsh-oc-tui` 这一层,包含 `tui-startup`、`tui-app`、`agent-presets`(预设名册)与 `tool-ask-user`(提问工具)四行。
137
-
138
- ## 4. 启动
139
-
140
- ### 4.1 标准启动
141
-
142
- ```sh
143
- dsh --profile tui
144
- ```
145
-
146
- 启动后先进入标题界面,**发送第一条消息即创建会话**。常用参数:
147
-
148
- ```sh
149
- dsh --profile tui --resume <sessionId> # 恢复已保存的会话
150
- dsh --profile tui --model <modelId> # 新会话默认模型
151
- dsh --profile tui --provider <route> # 新会话默认 provider 路由
152
- dsh --profile tui --no-sidebar # 不显示侧边栏
153
- dsh --profile tui --help # 查看 TUI 自己的参数帮助
154
- ```
155
-
156
- dsh 启动器只把 `web` 和 `plugin` 硬编码为裸子命令,所以 `--profile tui` 才是正规形态。想要直接敲 `dsh tui`,可以加一行别名:
157
-
158
- ```powershell
159
- function tui { dsh --profile tui @args } # 写入 PowerShell $PROFILE
160
- ```
161
-
162
- ```bat
163
- doskey tui=dsh --profile tui $* :: CMD
164
- ```
165
-
166
- ### 4.2 便捷启动器 dsh-oc-tui
167
-
168
- 本包附带 `dsh-oc-tui` 命令,等价于 `dsh --profile tui`,但会在启动前检查 `tui` profile 是否已装好本插件;若没有,会直接打印一次性安装命令,而不是进入一个空转的 profile。
169
-
170
- ```sh
171
- dsh-oc-tui # 启动 tui profile
172
- dsh-oc-tui --profile mytui # 启动名为 mytui 的 profile
173
- dsh-oc-tui --help # 启动器帮助
174
- dsh-oc-tui --version # 启动器版本
175
- ```
176
-
177
- 它优先使用 PATH 中的 `dsh`,找不到时回退到 `npx --yes @deepseek-ai/dsh`。安装到 PATH:`npm install -g dsh-oc-tui`。
178
-
179
- | 环境变量 | 作用 |
180
- | --- | --- |
181
- | `DSH_TUI_PROFILE` | 未指定 `--profile` 时的默认 profile 名(默认 `tui`) |
182
- | `DSH_TUI_SKIP_CHECK` | 设为 `1` 跳过 profile 预检(高级安装方式使用) |
183
-
184
- ## 5. 使用
185
-
186
- ### 5.1 快捷键
187
-
188
- | 按键 | 作用 |
189
- | --- | --- |
190
- | `Enter` | 发送消息 |
191
- | `Ctrl+Enter` / `Shift+Enter` / `Alt+Enter` | 输入换行 |
192
- | `Ctrl+C` | 清空非空输入框 / 取消进行中的轮次;空闲时连按两次退出 |
193
- | `Ctrl+P` | 打开设置菜单 |
194
- | `Ctrl+E` | 打开/关闭输入框下方的思考强度滑块 |
195
- | `Tab` | 会话页面:循环切换思考强度;设置页面:切换左侧菜单(Main/Model/Update) |
196
- | `Ctrl+N` | 新建会话 |
197
- | `Ctrl+D` | 在 设置 → 管理会话 中删除当前选中的会话(连按两次确认) |
198
- | `Ctrl+L` | 清空当前转录视图 |
199
- | `Up` / `Down` | 在多行输入中上下移动光标;位于第一行/最后一行时改为浏览输入历史 |
200
- | `Left` / `Right` | 在输入框中左右移动光标 |
201
- | `PgUp` / `PgDn` | 滚动转录区 |
202
- | `Esc` | 关闭上下文仪表盘 / 关闭强度滑块 / 关闭帮助 / 取消授权询问 |
203
- | `y` / `n` | 回答界面内的授权询问 |
204
-
205
- **鼠标**:滚轮滚动页面——会话页滚动转录区,设置窗口打开时滚动设置窗口。在转录区按住左键拖动可选中文字,随后按右键把所选文字复制到剪贴板。
206
-
207
- ### 5.2 斜杠命令
208
-
209
- 内建命令:`/help` `/settings` `/new` `/resume <id>` `/model <id>` `/provider <route>` `/clear` `/cancel` `/quit`(`/exit` 等效)。
210
-
211
- dsh 的人类命令(`/compact`、`/goal`、`/plan` 等)会转发给 `ctx.commands`,不经过模型轮次执行。**这些命令需要有活动会话**:在标题屏上敲会得到 `/<name>: start a session first` 提示,而不是被静默丢弃。因此想用 `/plan` 进入计划模式,请先随便发一条消息建立会话。
212
-
213
- ### 5.3 交互式提示:授权与提问
214
-
215
- **授权询问**:工具需要许可时,输入框区域显示 `Approval · <工具> · y allow / n deny`。`y` 允许一次、`n` 拒绝、`Esc` 取消。插件同时尊重生效的权限预设——自动批准的预设不会弹询问。
216
-
217
- **模型提问**:模型可以通过 `ask_user_question` 工具直接向你提问。该工具由本 bundle 的 `tool-ask-user` 行声明(`dsh-base` 只挂载 `user-questions` **服务**、不挂载工具,而 TUI 会话是从 base 进程级组合的、不走 agent 预设),并由一个弹窗应答:
218
-
219
- | 按键 | 作用 |
220
- | --- | --- |
221
- | `Up` / `Down` | 在选项间移动(循环) |
222
- | `Space` | 切换高亮项(多选)或选中它(单选) |
223
- | `Enter` | 继续:单选会选中并前进;在自由文本行上进入编辑;多选则确认已勾选集合 |
224
- | 任意可打印字符 | 跳到自由文本行并开始输入 |
225
- | `PgUp` / `PgDn`、滚轮 | 滚动较长的计划/明细区 |
226
- | `Esc` | 让出(defer):不在本界面回答;编辑状态下按 `Esc` 先退回选项 |
227
- | `Ctrl+C` | 仍然取消进行中的轮次,待答问题随之撤回 |
228
-
229
- 问题**一次问一个**(与 WebUI 的 composer 完全一致),答案编码也相同:自由文本答案在单选问题中**替换**已选选项,在多选题中**与已选项并存**。
230
-
231
- 带 `plan-review` 意图的问题(`exit_plan_mode` 发出的那种)会把计划 markdown 渲染在上方可滚动区域,下面是 `Approve` / `Keep planning`。选 `Approve` 会退出计划模式并继续执行;选其它则继续保持计划。
232
-
233
- `Esc` 让出是刻意设计而非"取消":若没有其它应答者,工具会收到 `no user-questions answerer accepted the request` 报错——这个结果不会被误当成你做出了选择。
234
-
235
- ### 5.4 思考强度
236
-
237
- 输入框右上角显示当前生效的思考强度等级名,与 `provider · model` 标签对角。
238
-
239
- - 会话页面按 `Tab` 循环当前模型支持的等级(到最高档后回到最低档),`Shift+Tab` 反向;改动即时保存。
240
- - `Ctrl+E` 打开输入框下方的滑块:`Tab` 或 `←`/`→` 调整并保存,`Esc` 或 `Ctrl+E` 关闭。
241
- - 等级来自 provider 适配器实际声明的可选值(`ctx.llm.resolveModelInfo`):布尔型思考模型只显示两端,DeepSeek 的 `Off`/`High`/`Max` 只显示这三档,全档位模型显示它声明的每一级——不会退化成 `none → max` 的一刀切刻度。
242
-
243
- 选择通过 `agent/request` waterfall 应用到该会话的请求,并保存到 `agent-default-model.reasoningEffort`。
244
-
245
- ### 5.5 上下文仪表与遥测
246
-
247
- 状态栏带实时上下文占用条,数据来自 token-meter 的 `contextPressure` 投影(与 Web 界面输入框右侧的环形仪表同源):当前上下文长度 / 模型上下文窗口,占用升高时转入警示色与错误色。点击占用条会打开明细面板(再次点击或 `Esc` 关闭),显示占用读数与按启发式拆分的构成占比——系统提示词、工具、对话消息——对应 WebUI 的 ContextMeter 弹窗。当 profile 缺少 token-meter 投影时,占用条自动隐藏,不影响其它功能。
248
-
249
- 页脚报告会话 token、平均首 token 时间、解码吞吐与缓存命中率,均由持久的 step/chunk/message 事件折叠得出。
250
-
251
- ### 5.6 设置菜单
252
-
253
- 按 `Ctrl+P` 打开。菜单覆盖与 WebUI 相同的 Host 设置命名空间,修改通过 `ctx.settings` 持久化到 `$DSH_HOME/settings.yaml`。左侧菜单栏用 `Tab`(或鼠标点击)在三个标签间切换,`↑/↓` 移动、`Enter` 打开选项列表、`Esc` 返回,分区标题行不可选中:
254
-
255
- - **Main**:General(Busy Enter 行为、默认 agent 预设、权限预设)、Sessions(新建会话、管理会话)、System(Provider API 配置提示、更新管理器快捷入口、设置文件路径)
256
- - **Model**:默认 provider / model / 思考强度;随后是你添加过的每个 provider 一组,组内为 Provider URL、Provider API key、Models
257
- - **Update**:见 [5.7](#57-程序内更新)
258
-
259
- 只显示你在设置文件中**手动添加过**的 provider;系统预设但从未添加的不会显示。
260
-
261
- **Models 自动获取**:在某个 provider 的 **Models** 行按 `Enter`,TUI 调用 `ctx.llm.discoverModels` 获取该 provider 的完整模型目录(内置路由直接取内置目录,自定义路由则请求其端点),弹出勾选窗口用 `[x]`/`[ ]` 选择(`Enter` 切换、`Esc` 返回)。选中的模型保存到该 provider 的 `models` 配置;在列出的模型上按 `Enter` 可把它设为默认模型。
262
-
263
- 默认 agent 预设来自 profile 挂载的 `agent-presets` 名册(内置预设 + 你在 `$DSH_HOME/.agent-presets` 下自建的预设)。注意 TUI 会话始终从进程级 base 组合生成,因此该默认值只在按预设创建会话时生效。
264
-
265
- WebUI 专属选项(`ui-theme` 外观、`locale` 语言)不在 TUI 中显示,因为它们在终端里没有效果。
266
-
267
- ### 5.7 程序内更新
268
-
269
- `Ctrl+P → Update` 页面负责检测并切换 dsh 与 TUI 自身的版本,所有检查与安装都通过 `npm` / `dsh plugin`(即 pnpm)执行,因此会尊重你配置的 registry 与镜像。状态行**只以稳定版为目标**:
270
-
271
- - `Update available → x.y.z`:有更新稳定版(金色高亮)
272
- - `Up to date`:已是最新
273
- - `No stable release — pick from Versions`:registry 上还没有稳定版,不会主动提示,需自行从版本列表选择
274
- - `Install damaged — reinstall below`:全局 dsh 安装处于新旧混杂的损坏状态,需重装
275
-
276
- `Enter` 打开某个包的 **Versions** 完整版本列表(新→旧),`[latest]`(绿)、`[next]`(青)、`(installed)`(浅蓝)等标识区分频道与当前版本;选中任意版本(含 rc/alpha)后按 `Enter` 出现 y/n 确认(文案含目标版本号)。`Check now` 重新拉取 registry;`Startup check` 开关启动后 2 秒的静默稳定版检查。
277
-
278
- 安装过程后台进行、不阻塞界面、不会自动重启;完成后 toast `... installed — restart to apply`,重启后生效。
279
-
280
- **Windows 上的 dsh 更新(重要)**:Windows 会锁定运行中程序加载的原生 DLL,而 `npm install -g` 需要替换整个 dsh 目录——若更新时有任何 dsh 进程在运行,npm 可能**静默留下新旧混杂的损坏安装**并仍报成功。因此更新器有三层保护:
281
-
282
- 1. **dsh 安装在 Windows 上延迟到 TUI 退出后执行**:确认后 toast 提示 `will install when this TUI exits`,关闭 TUI 时一个独立小进程等待本进程退出、执行 `npm install`,并把结果写入 `$DSH_HOME/tui-dsh-install.json`,下次打开 Update 页自动核对。
283
- 2. **每次直接安装后校验磁盘实际版本**与目标是否一致,不一致(静默损坏)时 toast 报 `install corrupt` 并给出修复指引。
284
- 3. **已损坏的安装会被标出**(Status 行 `Install damaged — reinstall below`),而不是报一个虚假的成功。
285
-
286
- macOS/Linux 没有 DLL 锁,但检测到其它 dsh 进程运行时也会拒绝安装。
287
-
288
- ## 6. 工作原理
289
-
290
- - 插件是用 `tui` profile 加载的 Cordis 函数插件。`lib/startup.js` 解析本应用的命令行参数并提供 `tuiStartup` 服务;`lib/index.js` 拥有 UI 主循环。
291
- - `lib/term.js` 是零依赖终端引擎:raw 模式、备用屏幕、差分单元缓冲、按键解码(真彩 ANSI、CJK 宽度感知)。它把隐藏的终端光标停在输入光标处,使系统 IME 的候选窗锚定在输入框内;同时理解 SGR 与旧式 X10 两种鼠标编码,滚轮/点击字节不会漏进输入文本。
292
- - `lib/ui.js` 是响应式视图模型与渲染器(DeepSeek 蓝白主题、会话栏、转录区、多行输入框、命令建议、遥测页脚)。转录行按块缓存,每帧只实体化可见窗口,流式绘制合并,活动块按短节流重绘——因此渲染成本有界,输出速度不随历史增长而下降。思考内容折叠以保持转录可读,运行中的工具与思考块用流动 spinner 提示。
293
- - `lib/metrics.js` 把持久的 step/chunk/message 事件折叠为 token、TTFT、吞吐与缓存命中指标。
294
- - `lib/interrupt.js` 管理 stdin 与 `SIGINT` 共用的清空/取消/二次退出状态机。
295
- - `lib/markdown.js` 把模型输出(标题、列表、引用、代码、行内样式)渲染为带样式的行。
296
- - `lib/updates.js` 隔离 Update 页面的全部 npm/pnpm 交互——registry 查询、无依赖 semver 比较、dsh 安装探测、异步安装——一律走 `child_process.spawn`,从不使用 `spawnSync`。
297
- - 会话通过 `ctx.agents` 创建/恢复,转录由持久日志重建并由 `session/event` 实时驱动(含 `assistant/chunk`),模型默认值来自 `ctx.agentDefaultModel`,授权询问在内联应答 `approval/request` waterfall。
298
- - `ask_user_question` 通过 `user-questions/request` waterfall 应答:这是一个按 agent 作用域分发的 Cordis waterfall,弹窗要么返回答案、要么用 `next()` 委托。请求中止时以普通错误拒绝,让服务自己报出 `ASK_ABORTED`;发给其它 agent 的请求原样委托。
299
-
300
- ## 7. 故障排查
301
-
302
- | 现象 | 原因与处理 |
303
- | --- | --- |
304
- | `pnpm failed in profile directory` / `ERR_PNPM_ADDING_TO_ROOT` | profile 是 pnpm workspace 根,`add`/`remove` 要加 `-w`。 |
305
- | 安装时报 `ENOENT: … dsh-oc-tui-<版本>.tgz` | profile 仍引用你已删除的 tarball。先 `dsh plugin --profile tui remove -w dsh-oc-tui`,再重新 add。 |
306
- | `pnpm not found on PATH` | 安装 pnpm(`npm install -g pnpm`)后重试。 |
307
- | `--dump-config` 里没有 TUI 层 | 安装未完成或包名拼写不一致;重跑 add 并检查 `dsh.profile.bundles`。 |
308
- | 启动后立刻退出或没有界面 | stdin/stdout 必须都是 TTY,不要重定向或管道;再确认模型路由与凭据文件就绪。 |
309
- | `--resume` 或 管理会话 不可用 | 两者都需要共享的 `sessionQuery` 服务;确保 `dsh.profile.bundles` 第一项仍是 `@deepseek-ai/dsh-base`。 |
310
- | `--resume` 报 `no agent factory registered` | 与 agent-loop 行的启动竞态;当前构建会重试。旧构建可先启动、再用 `/resume <id>`。 |
311
- | 更新 dsh 后启动报 loader 错误(`State`、`./internal`) | 全局 dsh 处于新旧混杂状态:关闭所有 dsh 进程后 `npm install -g @deepseek-ai/dsh@<版本>`。 |
312
- | 改了源码不生效 | profile 里装的是 tarball 副本,需要重新打包再安装(见第 8 节)。 |
313
-
314
- ## 8. 开发
315
-
316
- ```sh
317
- npm run check # 对 lib/、bin/ 做 node --check
318
- npm test # 独立冒烟测试(不需要 dsh)
319
- ```
320
-
321
- **安装即构建,所以顺序是:改源码 → 打包 → 重新安装。** profile 里装的是本插件的 **tarball** 副本,而 profile 的热更新监视的是 profile 目录、不是插件目录——改这个检出不会影响任何东西,除非重新打包并重装:
322
-
323
- ```sh
324
- dsh plugin --profile tui remove -w dsh-oc-tui # 先摘掉旧依赖
325
- Remove-Item .\*.tgz # 再删掉旧 tarball
326
- npm pack
327
- dsh plugin --profile tui add -w .\dsh-oc-tui-<版本>.tgz
328
- ```
329
-
330
- **顺序不能颠倒**:pnpm 在 add 时会解析 profile 现有的 `file:` 依赖,指向已删除 tarball 会让整个安装以 `ENOENT` 中止。
331
-
332
- 装完要**逐文件哈希核对**(版本号本身证明不了任何事):
333
-
334
- ```powershell
335
- foreach ($rel in @('lib\index.js','lib\ui.js','lib\util.js','lib\term.js','lib\metrics.js',
336
- 'lib\interrupt.js','lib\web-settings.js','lib\updates.js','lib\markdown.js',
337
- 'lib\startup.js','bin\dsh-oc-tui.js','cordis.patch.yml')) {
338
- $a = (Get-FileHash ".\$rel").Hash
339
- $b = (Get-FileHash "$env:USERPROFILE\.dsh\profiles\tui\node_modules\dsh-oc-tui\$rel").Hash
340
- if ($a -ne $b) { "DIFFERS: $rel" }
341
- }
342
- ```
343
-
344
- 然后必须**真正跑起来**:只到标题屏不算验证——会话开启路径才是宿主 API 断裂暴露的地方,所以要发一条消息。`--resume` 要**单独测**,它是 apply 期路径,可能输掉一个其它路径不会遇到的启动竞态。
345
-
346
- ### 8.1 零安装开发引导
347
-
348
- 想跳过打包,可以让 profile 直接引用本检出的绝对路径:
349
-
350
- ```sh
351
- dsh --profile tui --dump-config # 先初始化 base profile
352
- ```
353
-
354
- ```yaml
355
- # $DSH_HOME/profiles/tui/cordis.patch.yml
356
- - insert:
357
- - id: tui-startup
358
- name: 'file:///D:/Projects/DeepSeekHarnessPlugins/dsh-oc-tui/lib/startup.js'
359
- - id: tui-app
360
- name: 'file:///D:/Projects/DeepSeekHarnessPlugins/dsh-oc-tui/lib/index.js'
361
- config:
362
- sidebar: true
363
- showReasoning: true
364
- ```
365
-
366
- 插件对 dsh 各包的 import 会通过 `$DSH_HOME/profiles/node_modules` 共享回退解析,因此无需在插件目录里安装依赖。
367
-
368
- ## 9. 已知限制
369
-
370
- - 零依赖终端引擎尚未暴露 IME 组字与 bracketed-paste 图片附件。
371
- - 插件不支持热重载:profile 的 HMR 根是 profile 目录,运行中的 TUI 保持它启动时的那份副本。
372
- - `dsh tui` 作为裸子命令需要 shell 别名——原版启动器只硬编码了 `web` 与 `plugin`。
373
- - dsh 的人类斜杠命令需要活动会话;标题屏上会提示先建立会话。
374
- - `Esc` 让出问题不会取消工具调用,而是委托;没有其它应答者时该工具调用会失败。WebUI composer 支持的**逐题跳过**尚未实现。
375
- - `--resume`、设置 → 管理会话、上下文仪表依赖 `@deepseek-ai/dsh-base` 挂载的服务(`sessionQuery`、`sessionProjections`);手工搭建的 profile 需自行提供。
376
- - Windows 上的延迟 dsh 安装只等待**调度它的那个 TUI**,不是机器上所有 dsh 进程;执行前请关掉其它 TUI 窗口(以及 `dsh web`)。
377
-
378
- ## 10. 目录结构
379
-
380
- ```
381
- lib/index.js 插件入口:agents、事件、输入、命令、授权、用户提问
382
- lib/startup.js 命令行参数提供者(tuiStartup 服务)
383
- lib/term.js 终端引擎(raw 模式、屏幕、按键解码)
384
- lib/ui.js 响应式视图模型 + 渲染器(含问答弹窗)
385
- lib/metrics.js 持久事件遥测统计
386
- lib/interrupt.js Ctrl+C 生命周期状态机
387
- lib/web-settings.js WebUI 设置投影
388
- lib/updates.js 程序内更新(npm registry + 安装)
389
- lib/markdown.js Markdown -> 带样式文本行
390
- lib/util.js 文本/显示工具
391
- bin/dsh-oc-tui.js 便捷启动器
392
- install.sh 一键安装脚本(Linux/macOS)
393
- install.ps1 一键安装脚本(Windows)
394
- cordis.patch.yml bundle 补丁层(TUI 行、预设名册、提问工具)
395
- docs/用户手册.md 本手册
396
- tests/smoke.test.mjs 独立冒烟测试
397
- ```
398
-
399
- ## 11. 许可证
400
-
401
- [LGPL-3.0-or-later](../LICENSE)
1
+ # DeepSeek Harness TUI 用户手册
2
+
3
+ `dsh-oc-tui` 是运行在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(dsh)进程内的**终端界面(TUI)插件**,以 profile app 的形式挂载。它把 dsh 的持久事件流渲染到终端里——流式回复、工具卡片、待办列表、思考块——并把你输入的内容送回 agent。
4
+
5
+ 它**不是**独立的 agent 运行时:模型路由、工具执行、授权、命令、会话存储和凭据仍由 dsh 负责,插件只负责终端里的输入与显示。
6
+
7
+ 本手册面向**安装与使用**,开发者回路见[第 8 节](#8-开发)。
8
+
9
+ ## 目录
10
+
11
+ - [1. 功能特性](#1-功能特性)
12
+ - [2. 安装前准备](#2-安装前准备)
13
+ - [3. 安装](#3-安装)
14
+ - [4. 启动](#4-启动)
15
+ - [5. 使用](#5-使用)
16
+ - [5.1 快捷键](#51-快捷键)
17
+ - [5.2 斜杠命令](#52-斜杠命令)
18
+ - [5.3 交互式提示:授权与提问](#53-交互式提示授权与提问)
19
+ - [5.4 思考强度](#54-思考强度)
20
+ - [5.5 会话统计与上下文仪表](#55-会话统计与上下文仪表)
21
+ - [5.6 设置菜单](#56-设置菜单)
22
+ - [5.7 程序内更新](#57-程序内更新)
23
+ - [6. 工作原理](#6-工作原理)
24
+ - [7. 故障排查](#7-故障排查)
25
+ - [8. 开发](#8-开发)
26
+ - [9. 已知限制](#9-已知限制)
27
+ - [10. 目录结构](#10-目录结构)
28
+ - [11. 许可证](#11-许可证)
29
+
30
+ ## 1. 功能特性
31
+
32
+ | | |
33
+ | --- | --- |
34
+ | **持久会话** | 新建、恢复、列出、删除会话;转录内容从持久事件日志重建,恢复后的会话与离开时完全一致。 |
35
+ | **实时流式** | 回复与思考逐 token 流式渲染;思考内容在独立可折叠框中,流式期间保持折叠。 |
36
+ | **工具活动** | 工具卡片带一行摘要(`read src/app.ts`、`run npm test`),运行中显示流动 spinner,结果按 Markdown 渲染。 |
37
+ | **交互式提问** | 模型可以暂停并向你提问——选项列表、多选、自由文本、可滚动的计划评审,全部在终端内完成。 |
38
+ | **内联授权** | `approval/request` 询问用 `y` / `n` 直接回答,无需离开界面。 |
39
+ | **遥测页脚** | 会话统计条:轮次/步数、LLM 与工具耗时、平均首 token 时间(TTFT)、解码吞吐、KV 缓存命中率与输入/输出 token,均由持久事件折叠得出。 |
40
+ | **统计窗口** | 点击统计条或上下文仪表条,或输入 `/stats`,打开会话统计与 token 用量明细窗口。 |
41
+ | **上下文仪表** | 实时上下文占用(`ctx ▓▓░░ 32K/128K 25%`),明细窗口内含按系统提示词/工具/消息拆分的构成占比。 |
42
+ | **思考强度** | `Tab` 循环切换当前模型真实支持的推理等级;`Ctrl+E` 打开滑块。选择按请求应用并持久化。 |
43
+ | **共享设置** | 与 WebUI 相同的 Host 设置命名空间——通用、会话、各 provider 模型配置、凭据——持久化到 `$DSH_HOME/settings.yaml`。 |
44
+ | **程序内更新** | 在 TUI 内检测并切换 `@deepseek-ai/dsh` 与 `dsh-oc-tui` 版本,Windows 上采用延迟安装避免静默损坏。 |
45
+ | **零依赖终端引擎** | raw 模式、备用屏幕、差分单元缓冲、真彩 ANSI、CJK 宽度感知、SGR + 旧式 X10 鼠标解码、IME 光标锚定。 |
46
+
47
+ ## 2. 安装前准备
48
+
49
+ | 项目 | 要求 |
50
+ | --- | --- |
51
+ | Node.js | >= 22 |
52
+ | dsh CLI | 已安装 `@deepseek-ai/dsh`,例如 `npm install -g @deepseek-ai/dsh` |
53
+ | pnpm | 已加入 PATH(`dsh plugin` 内部转发给 pnpm) |
54
+ | 终端 | 交互式终端(Windows Terminal / ConPTY、iTerm2、GNOME Terminal 等) |
55
+ | 模型配置 | `$DSH_HOME/settings.yaml` 与 `$DSH_HOME/.credentials.yaml` 中已配置可用的模型路由(与 Web GUI 共用) |
56
+
57
+ ```sh
58
+ dsh --version
59
+ pnpm --version
60
+ ```
61
+
62
+ **兼容性**:本插件已在 dsh `0.1.2-rc.1`(以及 `0.1.1-rc.2`)上验证。dsh 0.1.2 重命名了部分会话 API——`Session.events` 改为 `snapshotEvents()`——插件会读取宿主实际提供的那个访问器,因此同一份构建可同时服务这两条版本线。
63
+
64
+ ## 3. 安装
65
+
66
+ ### 3.1 从 npm 安装
67
+
68
+ 本包已发布到 npm,包名 [`dsh-oc-tui`](https://www.npmjs.com/package/dsh-oc-tui)。装进 `tui` profile:
69
+
70
+ ```sh
71
+ dsh plugin --profile tui add -w dsh-oc-tui
72
+ ```
73
+
74
+ 也可以把启动器装到全局,这样 `dsh-oc-tui` 命令就在 PATH 上,它等价于 `dsh --profile tui`:
75
+
76
+ ```sh
77
+ npm install -g dsh-oc-tui
78
+ ```
79
+
80
+ 本插件同时已收录进 [awesome-dsh-plugin](https://awesome-dsh-plugin.com/zh/p/rayafriandion/dsh-oc-tui/) 插件市场(分类:UI 增强)。
81
+
82
+ ### 3.2 版本渠道:只有稳定版
83
+
84
+ **npm 包与 awesome-dsh-plugin 市场收录都只提供稳定版(stable release),不含任何预发布(pre-release / rc / alpha / beta)。** 因此 `npm install` 拿到的一定是最近一个稳定版,而不会是候选版本。
85
+
86
+ 本手册描述的是当前源码树,可能领先于已发布版本——手册里写到的功能,只有在对应版本发布到 npm 之后才能从稳定版拿到。
87
+
88
+ 想用预发布版本、或本仓库里尚未发布的改动,需要显式从源码安装:
89
+
90
+ ```sh
91
+ npm pack # 生成 dsh-oc-tui-<版本>.tgz
92
+ dsh plugin --profile tui add -w ./dsh-oc-tui-<版本>.tgz
93
+ ```
94
+
95
+ ### 3.3 一键安装脚本
96
+
97
+ 仓库内置 Linux/macOS 与 Windows 安装脚本:检查 Node.js >= 22、确保 pnpm 可用、把插件装进 `tui` profile,加 `--launcher`/`-Launcher` 还会全局安装 `dsh-oc-tui` 启动器。
98
+
99
+ ```sh
100
+ # Linux / macOS
101
+ curl -fsSL https://raw.githubusercontent.com/rayafriandion/dsh-oc-tui/main/install.sh | bash
102
+ ```
103
+
104
+ ```powershell
105
+ # Windows(PowerShell)
106
+ powershell -ExecutionPolicy Bypass -Command "iwr https://raw.githubusercontent.com/rayafriandion/dsh-oc-tui/main/install.ps1 -OutFile install.ps1; & .\install.ps1"
107
+ ```
108
+
109
+ 在源码目录里也可直接运行 `./install.sh`(Linux/macOS)或 `.\install.ps1`(Windows)。其他选项:`--local`(`-Local`)安装当前源码目录;`--source <spec>`(`-Source <spec>`)指定自定义来源;`--profile <name>`(`-Profile <name>`)指定非默认 profile。
110
+
111
+ ### 3.4 从本地目录或 tarball 安装
112
+
113
+ ```sh
114
+ npm pack # 生成 dsh-oc-tui-<版本>.tgz
115
+ dsh plugin --profile tui add -w ./dsh-oc-tui-<版本>.tgz
116
+ ```
117
+
118
+ `dsh plugin` 会把相对路径按你执行命令时所在的目录解析后再转发给 pnpm。
119
+
120
+ ### 3.5 为什么必须带 `-w`
121
+
122
+ profile 目录把自己声明为 pnpm workspace 根(`pnpm-workspace.yaml` → `packages: [.]`),因此裸 `add` 会被 pnpm 拒绝并报 `ERR_PNPM_ADDING_TO_ROOT`。加上 `-w` 后依赖写入 profile 自己的 manifest——这正是它应有的位置。之后 `dsh plugin` 会把 `dsh.profile.bundles` 与已安装状态对齐。
123
+
124
+ ### 3.6 安装做了什么
125
+
126
+ 1. `dsh plugin` 首次使用时初始化 `$DSH_HOME/profiles/tui`(`@deepseek-ai/dsh-base` 加一个空的用户补丁层)。
127
+ 2. pnpm 把 `dsh-oc-tui` 装进该 profile 的 `node_modules`。
128
+ 3. 因为本包声明了 `dsh.bundle.patch`,dsh 自动把 `dsh-oc-tui` 追加到 `dsh.profile.bundles`。
129
+ 4. `dsh --profile tui` 组合 base 层、本 bundle 的行、以及你自己的补丁——无需手动编辑任何配置。
130
+
131
+ ### 3.7 验证安装
132
+
133
+ ```sh
134
+ dsh --profile tui --dump-config
135
+ ```
136
+
137
+ 输出中应出现 `# == dsh-oc-tui` 这一层,包含 `tui-startup`、`tui-app`、`agent-presets`(预设名册)与 `tool-ask-user`(提问工具)四行。
138
+
139
+ ## 4. 启动
140
+
141
+ ### 4.1 标准启动
142
+
143
+ ```sh
144
+ dsh --profile tui
145
+ ```
146
+
147
+ 启动后先进入标题界面,**发送第一条消息即创建会话**。常用参数:
148
+
149
+ ```sh
150
+ dsh --profile tui --resume <sessionId> # 恢复已保存的会话
151
+ dsh --profile tui --model <modelId> # 新会话默认模型
152
+ dsh --profile tui --provider <route> # 新会话默认 provider 路由
153
+ dsh --profile tui --no-sidebar # 不显示侧边栏
154
+ dsh --profile tui --help # 查看 TUI 自己的参数帮助
155
+ ```
156
+
157
+ dsh 启动器只把 `web` 和 `plugin` 硬编码为裸子命令,所以 `--profile tui` 才是正规形态。想要直接敲 `dsh tui`,可以加一行别名:
158
+
159
+ ```powershell
160
+ function tui { dsh --profile tui @args } # 写入 PowerShell $PROFILE
161
+ ```
162
+
163
+ ```bat
164
+ doskey tui=dsh --profile tui $* :: CMD
165
+ ```
166
+
167
+ ### 4.2 便捷启动器 dsh-oc-tui
168
+
169
+ 本包附带 `dsh-oc-tui` 命令,等价于 `dsh --profile tui`,但会在启动前检查 `tui` profile 是否已装好本插件;若没有,会直接打印一次性安装命令,而不是进入一个空转的 profile。
170
+
171
+ ```sh
172
+ dsh-oc-tui # 启动 tui profile
173
+ dsh-oc-tui --profile mytui # 启动名为 mytui 的 profile
174
+ dsh-oc-tui --help # 启动器帮助
175
+ dsh-oc-tui --version # 启动器版本
176
+ ```
177
+
178
+ 它优先使用 PATH 中的 `dsh`,找不到时回退到 `npx --yes @deepseek-ai/dsh`。安装到 PATH:`npm install -g dsh-oc-tui`。
179
+
180
+ | 环境变量 | 作用 |
181
+ | --- | --- |
182
+ | `DSH_TUI_PROFILE` | 未指定 `--profile` 时的默认 profile 名(默认 `tui`) |
183
+ | `DSH_TUI_SKIP_CHECK` | 设为 `1` 跳过 profile 预检(高级安装方式使用) |
184
+
185
+ ## 5. 使用
186
+
187
+ ### 5.1 快捷键
188
+
189
+ | 按键 | 作用 |
190
+ | --- | --- |
191
+ | `Enter` | 发送消息 |
192
+ | `Ctrl+Enter` / `Shift+Enter` / `Alt+Enter` | 输入换行 |
193
+ | `Ctrl+C` | 清空非空输入框 / 取消进行中的轮次;空闲时连按两次退出 |
194
+ | `Ctrl+P` | 打开设置菜单 |
195
+ | `Ctrl+E` | 打开/关闭输入框下方的思考强度滑块 |
196
+ | `Tab` | 会话页面:循环切换思考强度;设置页面:切换左侧菜单(Main/Model/Update) |
197
+ | `Ctrl+N` | 新建会话 |
198
+ | `Ctrl+D` | 在 设置 → 管理会话 中删除当前选中的会话(连按两次确认) |
199
+ | `Ctrl+L` | 清空当前转录视图 |
200
+ | `Up` / `Down` | 在多行输入中上下移动光标;位于第一行/最后一行时改为浏览输入历史 |
201
+ | `Left` / `Right` | 在输入框中左右移动光标 |
202
+ | `PgUp` / `PgDn` | 滚动转录区 |
203
+ | `Esc` | 关闭会话统计窗口 / 关闭强度滑块 / 关闭帮助 / 取消授权询问 / 中断正在运行的轮次 / 清空正在输入的提示词 |
204
+ | `Esc Esc` | 空闲且输入框为空时打开 rewind 选择器 |
205
+ | `y` / `n` | 回答界面内的授权询问 |
206
+
207
+ **鼠标**:滚轮滚动页面——会话页滚动转录区,设置窗口打开时滚动设置窗口。在转录区按住左键拖动可选中文字,随后按右键把所选文字复制到剪贴板。会话页的统计条与上下文仪表条都是点击目标(见 [5.5](#55-会话统计与上下文仪表))。
208
+
209
+ ### 5.2 斜杠命令
210
+
211
+ 内建命令:`/help` `/settings` `/new` `/resume <id>` `/model <id>` `/provider <route>` `/rewind` `/stats` `/clear` `/cancel` `/quit`(`/exit` 等效)。
212
+
213
+ **Rewind(回退)**:`Esc Esc`(或 `/rewind`)列出当前会话的提示词。选择"恢复对话"会以所选提示词之前的事件**分叉出一个新会话**,父会话完整留在磁盘上——与 dsh 自身的 `session/fork` 行为一致;选择器默认停在最近一条真实提示词上,因此连按两次 `Enter` 即回退最后一轮。`/rewind <n|last> [conversation|code|both]` 可跳过选择器直接执行。分叉出的新会话从**空 inbox** 开始:切点落在某轮 turn 之前,也会切掉那一轮对 inbox 的认领,因此父会话里排队过的输入(**包括你这次要丢掉的那条提示词**)不会被再次投递,它们仍留在父会话日志里;若有此类输入,结果行会显示 `dropped N inherited pending input`。**恢复文件**是尽力而为且有护栏的:必须在 git 工作区中(否则提示 `files not restored (not a git worktree)` 且不改动任何文件);已跟踪文件用 `HEAD` 覆盖且不触碰索引;只有当日志中该路径的首次写入发生在回退点之后时,才会删除未跟踪文件。所有被覆盖或删除的内容都会先复制到 `$DSH_HOME/rewind-backups/<sessionId>/<时间戳>/`,结果行会给出该目录。由于日志不保存文件内容,已跟踪文件回到的是最近一次提交,而非回退点当时的状态。
214
+
215
+ dsh 的人类命令(`/compact`、`/goal`、`/plan` 等)会转发给 `ctx.commands`,不经过模型轮次执行。**这些命令需要有活动会话**:在标题屏上敲会得到 `/<name>: start a session first` 提示,而不是被静默丢弃。因此想用 `/plan` 进入计划模式,请先随便发一条消息建立会话。
216
+
217
+ ### 5.3 交互式提示:授权与提问
218
+
219
+ **授权询问**:工具需要许可时,输入框区域显示 `Approval · <工具> · y allow / n deny`。`y` 允许一次、`n` 拒绝、`Esc` 取消。插件同时尊重生效的权限预设——自动批准的预设不会弹询问。
220
+
221
+ **模型提问**:模型可以通过 `ask_user_question` 工具直接向你提问。该工具由本 bundle 的 `tool-ask-user` 行声明(`dsh-base` 只挂载 `user-questions` **服务**、不挂载工具,而 TUI 会话是从 base 进程级组合的、不走 agent 预设),并由一个弹窗应答:
222
+
223
+ | 按键 | 作用 |
224
+ | --- | --- |
225
+ | `Up` / `Down` | 在选项间移动(循环) |
226
+ | `Space` | 切换高亮项(多选)或选中它(单选) |
227
+ | `Enter` | 继续:单选会选中并前进;在自由文本行上进入编辑;多选则确认已勾选集合 |
228
+ | 任意可打印字符 | 跳到自由文本行并开始输入 |
229
+ | `PgUp` / `PgDn`、滚轮 | 滚动较长的计划/明细区 |
230
+ | `Esc` | 让出(defer):不在本界面回答;编辑状态下按 `Esc` 先退回选项 |
231
+ | `Ctrl+C` | 仍然取消进行中的轮次,待答问题随之撤回 |
232
+
233
+ 问题**一次问一个**(与 WebUI 的 composer 完全一致),答案编码也相同:自由文本答案在单选问题中**替换**已选选项,在多选题中**与已选项并存**。
234
+
235
+ 带 `plan-review` 意图的问题(`exit_plan_mode` 发出的那种)会把计划 markdown 渲染在上方可滚动区域,下面是 `Approve` / `Keep planning`。选 `Approve` 会退出计划模式并继续执行;选其它则继续保持计划。
236
+
237
+ `Esc` 让出是刻意设计而非"取消":若没有其它应答者,工具会收到 `no user-questions answerer accepted the request` 报错——这个结果不会被误当成你做出了选择。
238
+
239
+ ### 5.4 思考强度
240
+
241
+ 输入框右上角显示当前生效的思考强度等级名,与 `provider · model` 标签对角。
242
+
243
+ - 会话页面按 `Tab` 循环当前模型支持的等级(到最高档后回到最低档),`Shift+Tab` 反向;改动即时保存。
244
+ - `Ctrl+E` 打开输入框下方的滑块:`Tab` 或 `←`/`→` 调整并保存,`Esc` 或 `Ctrl+E` 关闭。
245
+ - 等级来自 provider 适配器实际声明的可选值(`ctx.llm.resolveModelInfo`):布尔型思考模型只显示两端,DeepSeek 的 `Off`/`High`/`Max` 只显示这三档,全档位模型显示它声明的每一级——不会退化成 `none → max` 的一刀切刻度。
246
+
247
+ 选择通过 `agent/request` waterfall 应用到该会话的请求,并保存到 `agent-default-model.reasoningEffort`。
248
+
249
+ ### 5.5 会话统计与上下文仪表
250
+
251
+ 输入框上方一行是**会话统计条**,对应 Web 界面输入框下方的统计行——用 `│` 分隔、按同样的顺序列出同一批数字:
252
+
253
+ ```
254
+ ▤ 1 turn · 2 steps│LLM 1.3s · tools 1.2s│TTFT avg 400ms · 20.0 tok/s│cache 55%│in 110 · out 30
255
+ ```
256
+
257
+ - `turns` / `steps` 统计**已结束的步**(`step/end`):失败、取消、触顶的步同样计入;`LLM` 是 `step/start` → 组装完成的回复,`tools` 是配对的 `tool/call` → `tool/result`。
258
+ - `TTFT avg` 是每步首 token 延迟的平均值,`tok/s` 是解码吞吐(首 token → 组装完成之间的输出 token)。
259
+ - `cache` 是提示词侧缓存命中率(缓存读取 ÷ 全部计费输入),`in` / `out` 是本次会话累计的计费输入与输出 token。
260
+ - 极窄终端会**整组**丢弃放不下的尾部数字并标出 `│…`,不会把某个数字截成两半;完整数字始终在明细窗口里。
261
+ - 会话还没有任何已结束的步、也没有任何 token 计费时,统计条整行隐藏,把该行还给转录区。
262
+
263
+ 统计条整行是点击目标:点击它(或点击状态栏右端的上下文仪表条 `ctx ▓▓░░ 32K/128K 25%`,或输入 `/stats`)打开**会话统计窗口**,再次点击、点击别处或按 `Esc` 关闭。窗口把同一个统计条拆成明细行(`usage` / `duration` / `speed` / `tokens` / `cache`),并附上上下文占用读数与按启发式拆分的构成占比——系统提示词、工具、对话消息——对应 WebUI 的 ContextMeter 弹窗。
264
+
265
+ 数据来源与 Web 界面一致,优先级为**投影优先、本地折叠兜底**:token-meter 的 `tokenUsage`、`contextPressure`、`contextBreakdown` 投影由 `dsh-base` 挂载;`sessionStats` 投影只有 Web 应用层 bundle 才挂载,因此 TUI 自己按同样的规则折叠持久日志里的 `step` / `chunk` / `message` / `tool` 事件。profile 缺少某个投影时,对应数字自动退回本地折叠而不影响其它功能;两者都不存在时(例如尚无任何事件)该行/该组自动隐藏。
266
+
267
+ ### 5.6 设置菜单
268
+
269
+ 按 `Ctrl+P` 打开。菜单覆盖与 WebUI 相同的 Host 设置命名空间,修改通过 `ctx.settings` 持久化到 `$DSH_HOME/settings.yaml`。左侧菜单栏用 `Tab`(或鼠标点击)在三个标签间切换,`↑/↓` 移动、`Enter` 打开选项列表、`Esc` 返回,分区标题行不可选中:
270
+
271
+ - **Main**:General(Busy Enter 行为、默认 agent 预设、权限预设)、Sessions(新建会话、管理会话)、System(Provider API 配置提示、更新管理器快捷入口、设置文件路径)
272
+ - **Model**:默认 provider / model / 思考强度;随后是你添加过的每个 provider 一组,组内为 Provider URL、Provider API key、Models
273
+ - **Update**:见 [5.7](#57-程序内更新)
274
+
275
+ 只显示你在设置文件中**手动添加过**的 provider;系统预设但从未添加的不会显示。
276
+
277
+ **Models 自动获取**:在某个 provider 的 **Models** 行按 `Enter`,TUI 调用 `ctx.llm.discoverModels` 获取该 provider 的完整模型目录(内置路由直接取内置目录,自定义路由则请求其端点),弹出勾选窗口用 `[x]`/`[ ]` 选择(`Enter` 切换、`Esc` 返回)。选中的模型保存到该 provider 的 `models` 配置;在列出的模型上按 `Enter` 可把它设为默认模型。
278
+
279
+ 默认 agent 预设来自 profile 挂载的 `agent-presets` 名册(内置预设 + 你在 `$DSH_HOME/.agent-presets` 下自建的预设)。注意 TUI 会话始终从进程级 base 组合生成,因此该默认值只在按预设创建会话时生效。
280
+
281
+ WebUI 专属选项(`ui-theme` 外观、`locale` 语言)不在 TUI 中显示,因为它们在终端里没有效果。
282
+
283
+ ### 5.7 程序内更新
284
+
285
+ `Ctrl+P → Update` 页面负责检测并切换 dsh 与 TUI 自身的版本,所有检查与安装都通过 `npm` / `dsh plugin`(即 pnpm)执行,因此会尊重你配置的 registry 与镜像。状态行**只以稳定版为目标**:
286
+
287
+ - `Update available → x.y.z`:有更新稳定版(金色高亮)
288
+ - `Up to date`:已是最新
289
+ - `No stable release — pick from Versions`:registry 上还没有稳定版,不会主动提示,需自行从版本列表选择
290
+ - `Install damaged — reinstall below`:全局 dsh 安装处于新旧混杂的损坏状态,需重装
291
+
292
+ `Enter` 打开某个包的 **Versions** 完整版本列表(新→旧),`[latest]`(绿)、`[next]`(青)、`(installed)`(浅蓝)等标识区分频道与当前版本;选中任意版本(含 rc/alpha)后按 `Enter` 出现 y/n 确认(文案含目标版本号)。`Check now` 重新拉取 registry;`Startup check` 开关启动后 2 秒的静默稳定版检查。
293
+
294
+ 安装过程后台进行、不阻塞界面、不会自动重启;完成后 toast `... installed — restart to apply`,重启后生效。
295
+
296
+ **Windows 上的 dsh 更新(重要)**:Windows 会锁定运行中程序加载的原生 DLL,而 `npm install -g` 需要替换整个 dsh 目录——若更新时有任何 dsh 进程在运行,npm 可能**静默留下新旧混杂的损坏安装**并仍报成功。因此更新器有三层保护:
297
+
298
+ 1. **dsh 安装在 Windows 上延迟到 TUI 退出后执行**:确认后 toast 提示 `will install when this TUI exits`,关闭 TUI 时一个独立小进程等待本进程退出、执行 `npm install`,并把结果写入 `$DSH_HOME/tui-dsh-install.json`,下次打开 Update 页自动核对。
299
+ 2. **每次直接安装后校验磁盘实际版本**与目标是否一致,不一致(静默损坏)时 toast 报 `install corrupt` 并给出修复指引。
300
+ 3. **已损坏的安装会被标出**(Status 行 `Install damaged — reinstall below`),而不是报一个虚假的成功。
301
+
302
+ macOS/Linux 没有 DLL 锁,但检测到其它 dsh 进程运行时也会拒绝安装。
303
+
304
+ ## 6. 工作原理
305
+
306
+ - 插件是用 `tui` profile 加载的 Cordis 函数插件。`lib/startup.js` 解析本应用的命令行参数并提供 `tuiStartup` 服务;`lib/index.js` 拥有 UI 主循环。
307
+ - `lib/term.js` 是零依赖终端引擎:raw 模式、备用屏幕、差分单元缓冲、按键解码(真彩 ANSI、CJK 宽度感知)。它把隐藏的终端光标停在输入光标处,使系统 IME 的候选窗锚定在输入框内;同时理解 SGR 与旧式 X10 两种鼠标编码,滚轮/点击字节不会漏进输入文本。
308
+ - `lib/ui.js` 是响应式视图模型与渲染器(DeepSeek 蓝白主题、会话栏、转录区、多行输入框、命令建议、会话统计条、状态栏)。转录行按块缓存,每帧只实体化可见窗口,流式绘制合并,活动块按短节流重绘——因此渲染成本有界,输出速度不随历史增长而下降。思考内容折叠以保持转录可读,运行中的工具与思考块用流动 spinner 提示。
309
+ - `lib/metrics.js` 把持久的 step/chunk/message/tool 事件折叠为整场会话的轮次/步数、耗时、TTFT、吞吐、缓存命中与计费 token(与 Web 界面的 `sessionStats` / `tokenUsage` 投影同规则),并在 profile 提供投影时以投影值为准。
310
+ - `lib/interrupt.js` 管理 stdin 与 `SIGINT` 共用的清空/取消/二次退出状态机。
311
+ - `lib/markdown.js` 把模型输出(标题、列表、引用、代码、行内样式)渲染为带样式的行。
312
+ - `lib/updates.js` 隔离 Update 页面的全部 npm/pnpm 交互——registry 查询、无依赖 semver 比较、dsh 安装探测、异步安装——一律走 `child_process.spawn`,从不使用 `spawnSync`。
313
+ - 会话通过 `ctx.agents` 创建/恢复,转录由持久日志重建并由 `session/event` 实时驱动(含 `assistant/chunk`),模型默认值来自 `ctx.agentDefaultModel`,授权询问在内联应答 `approval/request` waterfall。
314
+ - `ask_user_question` 通过 `user-questions/request` waterfall 应答:这是一个按 agent 作用域分发的 Cordis waterfall,弹窗要么返回答案、要么用 `next()` 委托。请求中止时以普通错误拒绝,让服务自己报出 `ASK_ABORTED`;发给其它 agent 的请求原样委托。
315
+
316
+ ## 7. 故障排查
317
+
318
+ | 现象 | 原因与处理 |
319
+ | --- | --- |
320
+ | `pnpm failed in profile directory` / `ERR_PNPM_ADDING_TO_ROOT` | profile 是 pnpm workspace 根,`add`/`remove` 要加 `-w`。 |
321
+ | 安装时报 `ENOENT: … dsh-oc-tui-<版本>.tgz` | profile 仍引用你已删除的 tarball。先 `dsh plugin --profile tui remove -w dsh-oc-tui`,再重新 add。 |
322
+ | `pnpm not found on PATH` | 安装 pnpm(`npm install -g pnpm`)后重试。 |
323
+ | `--dump-config` 里没有 TUI 层 | 安装未完成或包名拼写不一致;重跑 add 并检查 `dsh.profile.bundles`。 |
324
+ | 启动后立刻退出或没有界面 | stdin/stdout 必须都是 TTY,不要重定向或管道;再确认模型路由与凭据文件就绪。 |
325
+ | `--resume` 或 管理会话 不可用 | 两者都需要共享的 `sessionQuery` 服务;确保 `dsh.profile.bundles` 第一项仍是 `@deepseek-ai/dsh-base`。 |
326
+ | `--resume` 报 `no agent factory registered` | 与 agent-loop 行的启动竞态;当前构建会重试。旧构建可先启动、再用 `/resume <id>`。 |
327
+ | 更新 dsh 后启动报 loader 错误(`State`、`./internal`) | 全局 dsh 处于新旧混杂状态:关闭所有 dsh 进程后 `npm install -g @deepseek-ai/dsh@<版本>`。 |
328
+ | 改了源码不生效 | profile 里装的是 tarball 副本,需要重新打包再安装(见第 8 节)。 |
329
+
330
+ ## 8. 开发
331
+
332
+ ```sh
333
+ npm run check # 对 lib/、bin/ 做 node --check
334
+ npm test # 独立冒烟测试(不需要 dsh)
335
+ ```
336
+
337
+ **安装即构建,所以顺序是:改源码 → 打包 → 重新安装。** profile 里装的是本插件的 **tarball** 副本,而 profile 的热更新监视的是 profile 目录、不是插件目录——改这个检出不会影响任何东西,除非重新打包并重装:
338
+
339
+ ```sh
340
+ dsh plugin --profile tui remove -w dsh-oc-tui # 先摘掉旧依赖
341
+ Remove-Item .\*.tgz # 再删掉旧 tarball
342
+ npm pack
343
+ dsh plugin --profile tui add -w .\dsh-oc-tui-<版本>.tgz
344
+ ```
345
+
346
+ **顺序不能颠倒**:pnpm 在 add 时会解析 profile 现有的 `file:` 依赖,指向已删除 tarball 会让整个安装以 `ENOENT` 中止。
347
+
348
+ 装完要**逐文件哈希核对**(版本号本身证明不了任何事):
349
+
350
+ ```powershell
351
+ foreach ($rel in @('lib\index.js','lib\ui.js','lib\util.js','lib\term.js','lib\metrics.js',
352
+ 'lib\interrupt.js','lib\web-settings.js','lib\updates.js','lib\markdown.js',
353
+ 'lib\startup.js','bin\dsh-oc-tui.js','cordis.patch.yml')) {
354
+ $a = (Get-FileHash ".\$rel").Hash
355
+ $b = (Get-FileHash "$env:USERPROFILE\.dsh\profiles\tui\node_modules\dsh-oc-tui\$rel").Hash
356
+ if ($a -ne $b) { "DIFFERS: $rel" }
357
+ }
358
+ ```
359
+
360
+ 然后必须**真正跑起来**:只到标题屏不算验证——会话开启路径才是宿主 API 断裂暴露的地方,所以要发一条消息。`--resume` 要**单独测**,它是 apply 期路径,可能输掉一个其它路径不会遇到的启动竞态。
361
+
362
+ ### 8.1 零安装开发引导
363
+
364
+ 想跳过打包,可以让 profile 直接引用本检出的绝对路径:
365
+
366
+ ```sh
367
+ dsh --profile tui --dump-config # 先初始化 base profile
368
+ ```
369
+
370
+ ```yaml
371
+ # $DSH_HOME/profiles/tui/cordis.patch.yml
372
+ - insert:
373
+ - id: tui-startup
374
+ name: 'file:///D:/Projects/DeepSeekHarnessPlugins/dsh-oc-tui/lib/startup.js'
375
+ - id: tui-app
376
+ name: 'file:///D:/Projects/DeepSeekHarnessPlugins/dsh-oc-tui/lib/index.js'
377
+ config:
378
+ sidebar: true
379
+ showReasoning: true
380
+ ```
381
+
382
+ 插件对 dsh 各包的 import 会通过 `$DSH_HOME/profiles/node_modules` 共享回退解析,因此无需在插件目录里安装依赖。
383
+
384
+ ## 9. 已知限制
385
+
386
+ - 零依赖终端引擎尚未暴露 IME 组字;图片附件已支持:bracketed paste 的原始图片字节、`data:image/...;base64,...` 数据 URL、本地图片路径或图片 URL 都会变成 `[Image N]` 附件,而粘贴一段无法识别的文本时会向终端请求剪贴板(OSC 52)。
387
+ - 插件不支持热重载:profile 的 HMR 根是 profile 目录,运行中的 TUI 保持它启动时的那份副本。
388
+ - `dsh tui` 作为裸子命令需要 shell 别名——原版启动器只硬编码了 `web` 与 `plugin`。
389
+ - dsh 的人类斜杠命令需要活动会话;标题屏上会提示先建立会话。
390
+ - `Esc` 让出问题不会取消工具调用,而是委托;没有其它应答者时该工具调用会失败。WebUI composer 支持的**逐题跳过**尚未实现。
391
+ - `--resume`、设置 → 管理会话、会话统计条与上下文仪表依赖 `@deepseek-ai/dsh-base` 挂载的服务(`sessionQuery`、`sessionProjections`);手工搭建的 profile 需自行提供。`sessionStats` 投影只由 Web 应用层 bundle 挂载,缺少时 TUI 自行从会话日志折叠同样的数字。
392
+ - Windows 上的延迟 dsh 安装只等待**调度它的那个 TUI**,不是机器上所有 dsh 进程;执行前请关掉其它 TUI 窗口(以及 `dsh web`)。
393
+
394
+ ## 10. 目录结构
395
+
396
+ ```
397
+ lib/index.js 插件入口:agents、事件、输入、命令、授权、用户提问
398
+ lib/startup.js 命令行参数提供者(tuiStartup 服务)
399
+ lib/term.js 终端引擎(raw 模式、屏幕、按键解码)
400
+ lib/ui.js 响应式视图模型 + 渲染器(含问答弹窗)
401
+ lib/metrics.js 整场会话统计 + token 用量折叠(Web 统计条 / tokenUsage 投影同规则)
402
+ lib/interrupt.js Ctrl+C 生命周期状态机
403
+ lib/web-settings.js WebUI 设置投影
404
+ lib/updates.js 程序内更新(npm registry + 安装)
405
+ lib/markdown.js Markdown -> 带样式文本行
406
+ lib/util.js 文本/显示工具
407
+ bin/dsh-oc-tui.js 便捷启动器
408
+ install.sh 一键安装脚本(Linux/macOS)
409
+ install.ps1 一键安装脚本(Windows)
410
+ cordis.patch.yml bundle 补丁层(TUI 行、预设名册、提问工具)
411
+ docs/用户手册.md 本手册
412
+ tests/smoke.test.mjs 独立冒烟测试
413
+ ```
414
+
415
+ ## 11. 许可证
416
+
417
+ [LGPL-3.0-or-later](../LICENSE)