@crazyhappyone/dsh-tui 0.1.0-alpha.5 → 0.1.0-alpha.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.zh.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "DeepSeek Harness 终端界面的可安装源码运行时 launcher 与公开源码镜像。"
2
+ description: "DeepSeek Harness 终端界面:一个 dsh 插件 bundle 及其 launcher。"
3
3
  kind: "package-group"
4
4
  ---
5
5
 
@@ -13,10 +13,31 @@ kind: "package-group"
13
13
 
14
14
  ## 概述
15
15
 
16
- `@crazyhappyone/dsh-tui` 提供 `dsh-tui` 命令,用于运行和安全更新 DeepSeek Harness 终端界面。Launcher 管理专用 DSH 源码 checkout,因此不会 pull、reset、stash 或 clean 贡献者的开发 checkout。本公开仓库还镜像 `packages/tui/` bundle 与 renderer 源码以供审查。Agent、会话、工具、持久化、provider、权限与 profile 组装仍由 DSH 拥有。
16
+ `@crazyhappyone/dsh-tui` 是 DeepSeek Harness(dsh)的终端界面,以 dsh **插件 bundle** 加 `dsh-tui` launcher 的形式发布。包声明了 `dsh.bundle`,因此 dsh 会像安装其他插件一样把它装进 profile;launcher 只负责自动完成这一安装并启动 profile。本仓库拥有插件源码(`tui/`、`tui-render/`)。Agent、会话、工具、持久化、provider、权限与 profile 组装仍由 DSH 拥有。
17
+
18
+ ## 0.1.0-alpha.51 版本
19
+
20
+ 本预发布版面向 DSH `0.2.1-rc.2`,修复实时输出穿插到输入区、滚轮逐行追赶,减少历史渲染开销,并加入设置多级导航。同时修复失败流式文本残留、SSH 登录提示和 `/cd` 后的文件路径问题(#14–#16)。完整变更与验证边界见 [changelog](CHANGELOG.md#010-alpha51--2026-10-21)。
21
+
22
+ ```sh
23
+ npm install --global @crazyhappyone/dsh-tui@next
24
+ dsh-tui probe
25
+ dsh-tui
26
+ ```
27
+
28
+ 轻量模式和源码模式现在默认使用 `$DSH_HOME/profiles/`(通常为 `~/.dsh/profiles/`)下的官方 `deepseek-tui` profile。`/model` 读取 `ctx.llm`,模型选择调用 DSH 的 `agentDefaultModel`;本 bundle 不读取私有模型配置,也不复制其他 profile 的 provider。Magpie 会写入已有的 `profiles/*/cordis.patch.yml`;若在新 profile 创建前已接入 Magpie,创建后刷新其 DSH 接入/模型目录,再重启 TUI。
29
+
30
+ 旧版轻量模式使用 `tui`,源码模式使用 `deepseek-tui`。两个目录均保留,不自动删除或迁移。继续使用原轻量 profile 可运行 `dsh-tui --profile tui`。使用其他已挂载 TUI 的官方 profile 时传入 `--profile NAME`;两种模式的安装和启动都会使用同一个指定目标。各 profile 仍由 DSH 独立配置;只写入 `web` 的 provider 不会自动变成全局配置。
31
+
32
+ 正在运行的源码链接安装可通过 `/reload` 加载新构建,但会保留进程原先选中的 profile。若从 `tui` 切换到 `deepseek-tui`,需退出后重新启动,或显式指定 profile。恢复的 Goal 需执行 `/goal resume` 才会自动续跑。
33
+
34
+ `/settings` 现在支持多级浏览。Enter 进入分类、namespace、Provider、model 或字段;←/Esc 返回上一层;`/` 搜索;`i` 编辑对象 JSON;Space 切换支持的值。保存后停留当前位置,继续使用 DSH 官方 settings/credentials 服务。本次 PTY 对照中,9 行滚动到达目标从 168.07 ms 降至 17.93 ms;2,812 个有正文快照的比较帧未发现正文不一致或越界。这是合成数据的 PTY 实测,不是硬件延迟保证。数据与边界见[修复验证](docs/performance-audit-2026-10-21/fix-verification.md)。
17
35
 
18
36
  ## 目录
19
37
 
38
+ - [版本与升级](#010-alpha51-版本)
39
+ - [Changelog](CHANGELOG.md)
40
+ - [终端能力](#终端能力)
20
41
  - [截图](#screenshots)
21
42
  - [安装 launcher](#install-the-launcher)
22
43
  - [运行与更新](#run-and-update)
@@ -33,9 +54,9 @@ kind: "package-group"
33
54
  <a id="screenshots"></a>
34
55
  ## 截图
35
56
 
36
- 空闲首页:
57
+ 交互式设置面板(`/settings`):
37
58
 
38
- ![dsh-tui 空闲首页](assets/screenshots/dsh-tui-home.png)
59
+ ![dsh-tui 设置面板](assets/screenshots/dsh-tui-settings.png)
39
60
 
40
61
  发送 `你好` 后:
41
62
 
@@ -51,7 +72,7 @@ kind: "package-group"
51
72
  `dsh-tui` 支持两种运行模式:
52
73
 
53
74
  ### 模式 A:轻量模式(推荐,零 Clone 秒开)
54
- 只需通过 npm 全局安装官方核心引擎与 TUI launcher(仅数十 MB),即可直接运行:
75
+ 全局安装官方引擎与 TUI launcher:
55
76
 
56
77
  ```text
57
78
  npm install --global @deepseek-ai/dsh
@@ -60,7 +81,18 @@ dsh-tui probe
60
81
  dsh-tui
61
82
  ```
62
83
 
63
- 在此模式下,launcher 会自动探测全局 `dsh`,并挂载预构建的 TUI 扩展,无需克隆 2GB 的 monorepo 仓库。
84
+ 首次启动会执行 `dsh plugin --profile deepseek-tui add <包目录>`,把本包链接进 `deepseek-tui` profile,排在 `@deepseek-ai/dsh-base` 之后;之后的启动检测到已安装,直接执行 `dsh --profile deepseek-tui`。不使用 launcher 时,也可以手动执行同样的两条 dsh 命令:
85
+
86
+ ```text
87
+ dsh plugin --profile deepseek-tui add @crazyhappyone/dsh-tui@next
88
+ dsh --profile deepseek-tui
89
+ ```
90
+
91
+ 在 dsh 0.2 中以 bundle 方式安装是必需的:`/settings` 的修改写入 profile 自己的 patch 层,该层在 bundle 层之后生效;而 `--patch` overlay 位于 profile 层之后,dsh 会拒绝对此类 overlay 所拥有的行写入设置。
92
+
93
+ 探测顺序为绝对路径 `DSH_BIN`、PATH、npm/pnpm 全局包、工作目录和 launcher 目录可解析的本地包;每个探测子进程限时十秒,以覆盖冷启动。显式 `DSH_BIN` 失败时不会选择其他安装。包内 bin 使用 launcher 的 Node 执行。
94
+
95
+ 本 bundle 面向 dsh `0.2` 发布线(基于 `0.2.1-rc.2` 构建,并在 `0.2.1-alpha.2` 上验证)。包把 `@deepseek-ai/dsh` `>=0.2.0-rc.2 <0.3.0` 声明为可选 peer,因此手动安装时由 dsh 自身的插件兼容性检查把关;launcher 会拒绝其他引擎发布线并给出确切的安装命令,设置 `DSH_TUI_SKIP_ENGINE_CHECK=1` 可跳过 launcher 的检查。
64
96
 
65
97
  ### 模式 B:源码模式(开发者调试)
66
98
  若需对 TUI 或 DSH 底层代码进行修改与调试:
@@ -110,13 +142,13 @@ dsh-tui update
110
142
  <a id="configure-the-source-runtime"></a>
111
143
  ## 配置源码运行时
112
144
 
113
- 默认值跟踪所有者的 private `feat/deepseek-tui` 分支。该仓库需要 Git authorization。其他用户可以选择一个兼容且可访问的仓库与 ref。
145
+ 默认使用官方公开仓库的 `dsh-v0.2.1-rc.2` tag。源码模式会将本插件链接到现有 `deepseek-tui` profile,保留用户 patch;缺少 Typert 运行时产物时先调用上游生成器。随后通过 `pnpm exec node` 启动源码 CLI,不运行每次都会安装整个工作区的 `predsh` 钩子。
114
146
 
115
147
  | 环境变量 | 默认值 | 用途 |
116
148
  |---|---|---|
117
149
  | `DSH_TUI_RUNTIME_DIR` | `~/.local/share/dsh-tui/runtime` | 仅供 launcher 使用的专用 clone |
118
- | `DSH_TUI_SOURCE_URL` | `https://github.com/zhangyang-crazy-one/deepseek-harness.git` | `update` clone 和 fetch 的 Git remote |
119
- | `DSH_TUI_SOURCE_REF` | `feat/deepseek-tui` | `update` fetch 的 branch 或 ref |
150
+ | `DSH_TUI_SOURCE_URL` | `https://github.com/deepseek-ai/deepseek-harness.git` | `update` clone 和 fetch 的 Git remote |
151
+ | `DSH_TUI_SOURCE_REF` | `dsh-v0.2.1-rc.2` | `update` fetch 的 branch 或 ref |
120
152
  | `DSH_TUI_PNPM` | `pnpm` | 安装和启动所用的 pnpm executable |
121
153
 
122
154
  所有值都不得为空,运行时目录必须是绝对路径。Launcher 会拒绝 symbolic-link runtime path,也会拒绝不是 Git checkout 的现有目录。Child command 接收参数数组,不使用 shell interpolation。
@@ -128,15 +160,68 @@ dsh-tui update
128
160
  <a id="relationship-to-dsh"></a>
129
161
  ## 与 DSH 的关系
130
162
 
131
- TUI 是 DSH profile 层,而不是独立的 agent 运行时:
163
+ TUI 是 dsh 插件 bundle,不是 fork,也不是独立的 agent 运行时:
132
164
 
133
165
  ```text
134
- @deepseek-ai/dsh-base
135
- └─ @deepseek-ai/dsh-tui profile patch, terminal lifecycle, controller
136
- └─ @deepseek-ai/dsh-tui-render Ink projection and terminal I/O
166
+ @deepseek-ai/dsh-base dsh 核心 bundle(agent、会话、工具)
167
+ └─ @crazyhappyone/dsh-tui 本 bundle:cordis.patch.yml + dist/*
168
+ ├─ tui-startup (./startup) 命令行 provider(任务、--resume、--cwd)
169
+ ├─ tui (./settings) /settings 中可编辑的 TUI 偏好(volatile)
170
+ ├─ tui-runtime (./runtime) 终端生命周期与控制器
171
+ ├─ schedule (./schedule) 官方定时服务与共享会话激活
172
+ └─ zen-proxy (./zen-proxy) 可选的 OpenCode Zen 请求头代理
173
+ ```
174
+
175
+ 各行通过包的 `exports` 引用插件,因此设置修改会在 profile patch 中记录可移植的行名。本 bundle 还插入已发布的 dsh 插件 `session-stats`、`message-feedback`、`tool-ask-user`、`workspace-changes`、`tool-schedule`、`time-context`、`tool-present`,以及用于账号登录回调的回环 `webserver`,并覆盖 `system-prompt`、`tools`、`session-query-sqlite` 与 `session-title-llm`。
176
+
177
+ ### 定时提醒与用户提问
178
+
179
+ 模型可使用官方 `schedule_create`、`schedule_list`、`schedule_update`、`schedule_delete` 工具。`/schedules` 跨会话列出提醒:↑↓ 或 PgUp/PgDn 选择、Enter 查看完整提示词、再次 Enter 打开所属会话、`r` 刷新、`d` 请求取消确认(`y` 确认,`n` 或 Esc 保留)。切换到其他会话后,投递仍恢复原会话。任务跨重启持久化;计时器仅在当前进程运行时工作,关闭期间到期的任务在下次启动时处理。投递回执表示已持久写入 inbox,不表示模型已完成回答。适配层打包固定 DSH 构建版本的官方定时服务和会话激活助手;升级上游后需重跑集成检查。
180
+
181
+ 用户提问支持整批逐题作答、多选和自定义文本。Space 切换多选项;`c` 打开自定义编辑器;Enter 保存文本、提交选项。无选项的问题直接进入编辑器。Tab 打开完整问题与回答供滚动阅读,Tab 或 Esc 返回编辑。编辑器内 Esc 丢弃本次编辑(纯文本问题则取消提问);选项页 Esc 取消整次请求。粘贴的多行回答保留原文,不改变聊天草稿。
182
+
183
+ 凭据编辑器及 `/key`、`/login` 的密钥参数显示圆点遮罩,凭据引用名保持可见。遮罩只影响显示:编辑和提交保留原值,取消后恢复聊天草稿。
184
+
185
+ ### 时间上下文、交付文件与图片粘贴
186
+
187
+ 官方 `time-context` 插件在准备模型步骤时提供当前时间和进程所在时区,十分钟后刷新。这类模型上下文不会增加人类对话中的消息行。
188
+
189
+ 模型可用官方 `present` 工具显式交付现有文件,每次最多八个。`/deliveries` 列出当前会话已记录的交付文件:↑↓ 选择、Enter 预览 UTF-8 文本、`o` 请求本机系统应用打开所选文件、Esc 返回。PDF、Office、图片等二进制格式显示文件信息,并通过系统应用查看。文本读取默认限制为 1 MiB。已完成回合也会显示交付路径与说明;记录可随会话恢复,但文件必须仍存在且可访问。
190
+
191
+ 剪贴板图片粘贴在 macOS 使用 `pngpaste -`([安装:`brew install pngpaste`](https://github.com/jcsalterego/pngpaste)),Wayland 使用 `wl-paste`,X11 使用 `xclip`。缺少助手时显示明确错误;读取默认五秒超时,关闭 TUI 时取消,并保留既有图片大小限制与校验。macOS 命令和子进程行为已在 Linux 上自动验证,macOS 实机验证仍待完成。
192
+
193
+ ### DeepSeek 账号、模型与工作目录
194
+
195
+ - 当 dsh 挂载 `deepseek-account` 服务时,`/login` 打开 DeepSeek 账号页:Enter 发起浏览器登录,`o` 重新打开授权链接,`c` 取消;登录后页面显示账号资料与余额。浏览器回调到本 bundle 的 `webserver` 行,监听 `127.0.0.1` 上由系统分配的端口。通过 SSH 使用时,页面显示 `ssh -L` 端口转发命令而不打开浏览器;在 `webserver` 行固定 `port` 可让转发保持稳定。关闭重开页面会继续已有登录并恢复转发提示;按 `o` 才显式打开链接。未挂载账号服务时,`/login` 与 `/key` 打开 API 密钥编辑器。
196
+ - `/logout` 确认后退出登录,已保存的 API 密钥保留。
197
+ - `/model` 在 `deepseek-account` 与 `deepseek-official` 目录为空时列出它们,并注明使模型可用的步骤(`/login` 或 `/key`),且拒绝选中此类行。
198
+ - `/cd [路径]` 在 dsh 0.2.1 及以后版本查看或切换会话工作目录。顶栏、`@` 提及与 `/files` 随之切换,文件面板输入的相对路径也以当前会话目录为基准;模型会收到变更通知;已有 shell 保留各自的目录。
199
+ - `/plugins` 将官方 bundle 单独分组。文件尚未安装的官方目录条目标记为可安装,按 Space 打开其精确安装目标的安装预览。
200
+
201
+ ### OpenCode Zen 免费模型
202
+
203
+ 打包后的 patch 挂载了 `zen-proxy`(vendored 自
204
+ [zhangyang-crazy-one/dsh-zen-proxy](https://github.com/zhangyang-crazy-one/dsh-zen-proxy),
205
+ fork 自 [Yee-h/dsh-zen-proxy](https://github.com/Yee-h/dsh-zen-proxy)):进程内 localhost
206
+ 代理,注入 Zen 要求的 OpenCode 官方客户端头(`User-Agent`、
207
+ `x-opencode-client/session/request/project`)。没有它,Zen 会返回
208
+ `400 MissingSessionID` / `429 FreeUsageLimitError`,因为 dsh 始终发送自己的
209
+ attribution `User-Agent`。
210
+
211
+ 使用方法:通过 TUI `/settings` provider 表单(或 profile 的 `cordis.patch.yml`)
212
+ 添加一个指向该代理的 provider,并把密钥放在 credentials 中:
213
+
214
+ ```yaml
215
+ - id: llm-pi-ai
216
+ config:
217
+ providers:
218
+ opencode:
219
+ api: openai-completions
220
+ baseURL: http://127.0.0.1:4097/v1
221
+ apiKeyEnv: OPENCODE_API_KEY
137
222
  ```
138
223
 
139
- 集成源码位于 private [`feat/deepseek-tui` DSH 分支](https://github.com/zhangyang-crazy-one/deepseek-harness/tree/feat/deepseek-tui/packages/tui)。影响 DSH 服务、profile 组合、组装后 CLI snapshot 或 Agent Note 的变更归该 monorepo 所有,并遵循其中的架构、测试与文档规则。
224
+ 代理只监听 `127.0.0.1:4097`;在 profile 的 `cordis.patch.yml` 中加入 `- id: zen-proxy` 并设置 `disabled: true` 即可关闭。
140
225
 
141
226
  -----
142
227
 
@@ -145,7 +230,8 @@ TUI 是 DSH profile 层,而不是独立的 agent 运行时:
145
230
 
146
231
  | 包 | DSH 形态 | 职责 |
147
232
  |---|---|---|
148
- | [`tui/`](tui/README.zh.md) | Profile bundle 与运行时插件 | 在 `dsh-base` 上组合终端层、拥有 live 终端会话,并将用户动作映射到 DSH 服务 |
233
+ | [`tui/`](tui/README.zh.md) | 运行时、启动与设置插件 | 在 `dsh-base` 上组合终端层、拥有 live 终端会话,并将用户动作映射到 DSH 服务 |
234
+ | [`plugins/zen-proxy/`](https://github.com/zhangyang-crazy-one/dsh-zen-proxy) | Vendored 插件(打包为 `dist/zen-proxy.js`) | 进程内 OpenAI 兼容代理,注入 OpenCode Zen 客户端头;在 `cordis.patch.yml` 中挂载 |
149
235
  | [`tui-render/`](tui-render/README.zh.md) | Library | 通过 Ink 投影控制器状态,但不拥有 agent、持久化或模型请求 |
150
236
 
151
237
  -----
@@ -153,9 +239,20 @@ TUI 是 DSH profile 层,而不是独立的 agent 运行时:
153
239
  <a id="development-workflow"></a>
154
240
  ## 开发工作流
155
241
 
156
- 在完整 DSH checkout 中实现 TUI 行为,运行受变更行为影响的检查,并将确认后的 `packages/tui/` tree 导出到这里。在本仓库中使用 `npm test` 开发 launcher;通过 packed tarball 测试安装,不使用 workspace link。
242
+ `tui/` 与 `tui-render/` 基于上游 dsh checkout 编译,版本为 `src/launcher.js` 中 `COMPATIBLE_DSH_VERSION` 指定的 tag:
157
243
 
158
- Launcher runtime 必须与开发 checkout 分离。要安全测试其他源码,请将 `DSH_TUI_RUNTIME_DIR` 指向临时绝对目录,并配置 `DSH_TUI_SOURCE_URL` 与 `DSH_TUI_SOURCE_REF`。
244
+ ```text
245
+ git clone --branch dsh-v0.2.1-rc.2 https://github.com/deepseek-ai/deepseek-harness ../deepseek-harness
246
+ (cd ../deepseek-harness && pnpm install)
247
+ npm run build # 或 DSH_SOURCE=/path/to/checkout npm run build
248
+ (cd ../deepseek-harness && pnpm install) # 链接挂载后的插件开发依赖
249
+ npm run dev:setup # 同样支持 DSH_SOURCE;生成类型声明并链接编辑器依赖
250
+ npm run typecheck # 严格检查源码和测试
251
+ ```
252
+
253
+ `npm run build` 把插件包与 `cordis.patch.yml` 复制进 checkout 的 `packages/tui/` workspace,用 esbuild 打包 `dist/index.js`、`dist/startup.js`、`dist/settings.js`、`dist/schedule.js` 与 `dist/zen-proxy.js`,并逐个检查导入。插件测试在 checkout 中运行 `npx vitest run packages/tui`,launcher 测试在本仓库运行 `npm test`;发布前运行 `npm run pack:check`。通过 packed tarball 或 `dsh plugin --profile <name> add <本目录>` 测试安装。
254
+
255
+ 本地 `tsconfig.json` 用于独立插件仓库的编辑器检查;构建时使用 `tsconfig.workspace.json` 作为 checkout 中的包配置,保留上游项目引用。`dev:setup` 构建依赖类型声明并链接整个 `node_modules` 目录,保留 pnpm 的相对链接。更新上游或公开类型后重新执行 build 和 setup;编辑器若仍显示旧的缺失模块错误,需要重载 TypeScript 项目。
159
256
 
160
257
  -----
161
258
 
@@ -178,21 +275,42 @@ npx --yes npm@11 trust github @crazyhappyone/dsh-tui \
178
275
  每次发布时,先提升 `package.json` 版本,等待 CI 通过并合并,然后创建 tag 与 package version 一致的 GitHub Release:
179
276
 
180
277
  ```text
181
- gh release create v0.1.0-alpha.4 --prerelease --generate-notes
278
+ gh release create v0.1.0-alpha.50 --prerelease --notes-file docs/releases/v0.1.0-alpha.50.md
182
279
  ```
183
280
 
184
281
  `Publish npm` workflow 会在发布前再次运行测试与 packed-package 验证。不要重新运行旧的 release tag:npm version 不可覆盖。提升到 stable 必须使用新版本并发布 non-prerelease GitHub Release。
185
282
 
186
283
  -----
187
284
 
285
+ ## 终端能力
286
+
287
+ `/timeline` 浏览逐条 Session 事件、真实工具/token/时长与完整 JSON;`/attachments` 列出类型化图片/文件引用并打开本地副本;`/review` 读取逐文件官方 diff。`/files` 对已展开目录合并自动刷新,并保留手工 `r` 刷新。
288
+
289
+ `/settings` 使用 Tab/Shift+Tab 切换分组,持久化 `tui.theme=dark|light`。`/keys` 列出各场景按键;`/bind ACTION Ctrl+X`、`/bind ACTION default`、`/unbind ACTION` 修改输入区快捷键并检查冲突。Shift+方向键/Home/End 按字素选择,输入/粘贴替换选区,Ctrl+Y 复制普通选区;凭据选区禁止复制。审批、提问及计划评审中可用 Alt+H 打开只读帮助。
290
+
291
+ `/agents` 支持 `c` 续聊(Enter 排队、Ctrl+Enter 引导)、`s` 请求停止、`v` 在 100 列以上并排查看。窄终端保留单面板。人工控制保留目录给出的准确直接父会话地址;Stop 回执不表示执行已经停止。
292
+
293
+ `/preset` 浏览 standard/PTC/minimal/cordis 声明,`/preset ID` 在首轮前选择隔离组合;开始对话后保持锁定。`/market QUERY` 检索已配置索引,`i` 进入插件管理器预览及确认,`n/p` 翻页。`/inspect` 浏览官方 Host provider,`/inspect host PROVIDER METHOD JSON` 查询具体契约。`/focus` 隐藏中间工具卡片,不修改模型工具或会话日志。外部语言包可调用 `registerTuiLocale`;围栏代码采用有界的按语言词法高亮。
294
+
295
+ Goal 页脚区分自动续跑和需要 `/goal resume` 恢复的停止状态。后台开启的回合会更新运行状态及 Ctrl+C 取消路径。更新正在运行的源码安装后使用 `/reload`;恢复的 Goal 需显式 `/goal resume` 重新授权续跑。
296
+
297
+ 验证及平台边界见 [P1/P2 验收记录](docs/p1-p2-verification-2026-10-20.md)。
298
+
299
+ -----
300
+
188
301
  <a id="known-limitations"></a>
189
302
  ## 已知限制
190
303
 
191
- - **Private 默认源码** — npm 安装是 public,但默认 DSH runtime remote 需要仓库授权;匿名用户必须配置一个可访问的兼容源码。
304
+ - **第三方插件需匹配引擎版本** — 旧认证插件如果只声明兼容 dsh 0.1.x,会被 dsh 0.2 拒绝加载;launcher 不自动授予版本豁免。
192
305
  - **需要源码工具链** — 初始化运行时需要 Git、pnpm 与 DSH 支持的 Node 版本。
193
306
  - **不自动解决冲突** — dirty 或 non-fast-forward runtime history 需要显式人工操作或新的运行时目录。
194
307
  - **运行时更新不是 launcher 更新** — `dsh-tui update` 刷新 DSH 源码与依赖;npm 更新 launcher package。
195
308
  - **集成分支拥有发布就绪状态** — launcher 成功不能替代 DSH 分支的行为、snapshot、build 或 hygiene 检查。
309
+ - **源码模式也使用本插件** — 配置的 checkout 必须与本 bundle 的 dsh 版本兼容。历史 profile 中的 `@deepseek-ai/dsh-tui` 旧 bundle 应替换为 `@crazyhappyone/dsh-tui`;迁移前备份 profile,并保留用户设置。
310
+ - **提醒需要进程运行** — `/schedules` 使用官方服务及持久化原会话;重启可恢复待投递提醒,关闭进程期间不运行计时器。
311
+ - **终端能力边界** — 图片及 PDF/Office 文档使用元数据/文本或系统打开。Ink 没有 IME composition 事件,预编辑由终端原生处理。`INK_SCREEN_READER=true` 使用普通输出流,关闭鼠标捕获、备用屏幕及动画;真实 IME/屏幕阅读器设备和 macOS 剪贴板尚未验证。
312
+ - **账号登录需要可访问的 Platform** — 主机无法访问 `platform.deepseek.com` 时登录报告 `no-response`(服务无响应),此时请使用 `/key`。
313
+ - **dsh 0.2 不支持物理删除会话** — 0.2 的会话持久化以归档代替删除,而 TUI 组合未挂载 workspace 归档服务,因此会话列表会提示无法删除。
196
314
 
197
315
  <a id="dev-note"></a>
198
316
  ## 开发备注
package/bin/dsh-tui.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  /** Command-line entry for the dsh-tui source-runtime launcher. */
3
3
 
4
- import { lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
4
+ import { lstatSync, mkdirSync, readFileSync } from 'node:fs'
5
5
  import { constants, homedir } from 'node:os'
6
6
  import { fileURLToPath } from 'node:url'
7
7
  import { spawnSync } from 'node:child_process'
8
+ import { createRequire } from 'node:module'
9
+ import { join } from 'node:path'
8
10
  import { parseLauncherInvocation, resolveLauncherSettings, runLauncher } from '../src/launcher.js'
9
11
 
10
12
  function inspectPath(path) {
@@ -28,7 +30,11 @@ function run(command, args, options = {}) {
28
30
  env: process.env,
29
31
  maxBuffer: capture ? 16 * 1024 * 1024 : undefined,
30
32
  shell: false,
31
- stdio: options.stdio === 'inherit' ? 'inherit' : capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
33
+ timeout: options.timeoutMs,
34
+ killSignal: 'SIGKILL',
35
+ stdio: options.stdio === 'inherit' ? 'inherit'
36
+ : options.stdio === 'output' ? ['ignore', 'inherit', 'inherit']
37
+ : capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
32
38
  })
33
39
  if (result.error !== undefined) {
34
40
  return { status: 1, stdout: '', stderr: result.error.message }
@@ -61,10 +67,19 @@ try {
61
67
  packageVersion: manifest.version,
62
68
  env: process.env,
63
69
  adapters: {
70
+ nodeExecutable: process.execPath,
71
+ currentDirectory: process.cwd(),
72
+ resolveDshPackage(directory) {
73
+ try {
74
+ return createRequire(join(directory, 'package.json')).resolve('@deepseek-ai/dsh/package.json')
75
+ } catch (error) {
76
+ if (error.code === 'MODULE_NOT_FOUND') return undefined
77
+ throw error
78
+ }
79
+ },
64
80
  inspectPath,
65
81
  makeDirectory: path => mkdirSync(path, { recursive: true, mode: 0o700 }),
66
82
  readText: path => readFileSync(path, 'utf8'),
67
- writeText: (path, content) => writeFileSync(path, content, 'utf8'),
68
83
  run,
69
84
  writeOut: text => process.stdout.write(text),
70
85
  writeError: text => process.stderr.write(text),
package/cordis.patch.yml CHANGED
@@ -1,30 +1,35 @@
1
- # The deepseek-tui bundle patch: terminal-surface mode directly over dsh-base.
1
+ # yaml-language-server: $schema=none
2
+ # The dsh-tui bundle layer: a terminal surface over @deepseek-ai/dsh-base.
2
3
  # It mounts no Host, HTTP server, Web runtime, or browser plugin. An ordinary
3
- # provider plugin parses --resume/--cwd and the task positional, then the
4
- # terminal driver creates an Agent through the core registry and prints its
5
- # durable result. Unlike the web surface, the agent plane stays process-wide:
6
- # dsh-base keeps tool-bash/tool-fs/tool-todo and friends because the TUI is
7
- # single-session and composes its agent process-wide.
4
+ # provider plugin parses --resume/--cwd and the task positional; the terminal
5
+ # driver then creates an Agent through the core registry. The UI displays one
6
+ # Session; scheduled reminders can activate other Sessions in the same process.
7
+ #
8
+ # Install it as a bundle so user settings (written to the profile layer)
9
+ # apply after this layer:
10
+ # dsh plugin --profile tui add @crazyhappyone/dsh-tui
11
+ # dsh --profile tui
12
+ # Rows target the dsh 0.2 Loader and name plugins through this package's
13
+ # exports, so profile-layer edits record a portable row name.
8
14
 
15
+ # The working directory reaches the model through the runtime-context
16
+ # message; dsh 0.2.1 registers no cwd prompt variable.
9
17
  - id: system-prompt
10
18
  config:
11
- persona: >-
12
- You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
13
- You are interacting with the user through the deepseek-tui terminal interface.
19
+ personaPrefix: >-
20
+ You are a coding agent powered by the {{model}} model.
21
+ You are interacting with the user through the dsh-tui
22
+ terminal interface.
14
23
 
15
- # The shared module-reload HMR row stays off; the launcher's watch-only
16
- # fallback still keeps the user patch layers live until the run exits.
24
+ # HMR stays disabled. Profile and home patch edits apply on the next
25
+ # process start; /reload restarts the terminal with the current session.
17
26
  - id: hmr
18
27
  disabled: true
19
28
 
20
29
  - id: tools
21
30
  config:
22
31
  # Keep the same temporary process-wide Code Mode opt-in as the Web surface.
23
- mode: !!js process.env.DSH_TOOLS_MODE
24
-
25
- - id: approval
26
- config:
27
- policy: !!js "(process.env.DSH_PERMISSION_MODE ?? 'workspace-write') === 'danger-full-access' ? 'never' : 'ask'"
32
+ mode: native
28
33
 
29
34
  # Full-text session search stays disabled until the first Ctrl+K search (the
30
35
  # base row's `openAt: never`). The TUI has no browser-latency concern, so the
@@ -34,23 +39,107 @@
34
39
  path: ':memory:'
35
40
  openAt: first-search
36
41
 
42
+ # The first-prompt title gets the whole first prompt, not the base 4 KiB.
43
+ - id: session-title-llm
44
+ config:
45
+ targetWords: 5
46
+ targetCjkCharacters: 10
47
+ maxInputBytes: 16384
48
+ maxOutputTokens: 64
49
+ timeoutMs: 60000
50
+
37
51
  - insert:
52
+ # Official task storage/runtime over the terminal's shared cold-Session
53
+ # activation owner. No browser Session API or upload transport is mounted.
54
+ - id: schedule
55
+ name: '@crazyhappyone/dsh-tui/schedule'
56
+
57
+ - id: tool-schedule
58
+ name: '@deepseek-ai/dsh-tool-schedule'
59
+
60
+ # Durable current-time context uses the process time zone and refreshes
61
+ # at most once per ten minutes, matching the official provider defaults.
62
+ - id: time-context
63
+ name: '@deepseek-ai/dsh-time-context'
64
+ config:
65
+ refreshIntervalMs: 600000
66
+
67
+ # Explicit file deliveries stay in the owning Session log.
68
+ - id: tool-present
69
+ name: '@deepseek-ai/dsh-tool-present'
70
+ config:
71
+ maxFiles: 8
72
+
73
+ # Per-round token/duration stats for the terminal status line.
74
+ - id: session-stats
75
+ name: "@deepseek-ai/dsh-session-stats"
76
+
77
+ # Base owns the shared storage/domain/projection-cache stack. This layer
78
+ # contributes only the TUI-facing feedback consumer over that stack.
79
+ - id: message-feedback
80
+ name: "@deepseek-ai/dsh-message-feedback"
81
+ config:
82
+ maxNoteBytes: 8192
83
+
84
+ - id: tool-ask-user
85
+ name: "@deepseek-ai/dsh-tool-ask-user"
86
+
38
87
  - id: tui-startup
39
- name: "./dist/startup.js"
88
+ name: "@crazyhappyone/dsh-tui/startup"
89
+
90
+ # TUI presentation preferences (locale, Enter behavior, notifications,
91
+ # scrollbar, thinking visibility). Every field is volatile, so /settings
92
+ # edits land in the profile layer and apply without a remount.
93
+ - id: tui
94
+ name: "@crazyhappyone/dsh-tui/settings"
95
+
96
+ # In-process OpenCode Zen header proxy (vendored from
97
+ # zhangyang-crazy-one/dsh-zen-proxy, MIT). Listens on localhost
98
+ # only; point an opencode provider baseURL at
99
+ # http://127.0.0.1:4097/v1 to reach the Zen free tier.
100
+ # Remove this row to disable the listener.
101
+ - id: zen-proxy
102
+ name: "@crazyhappyone/dsh-tui/zen-proxy"
103
+ config:
104
+ host: 127.0.0.1
105
+ port: 4097
106
+
107
+ # Loopback route carrier for the DeepSeek account browser callback
108
+ # (/login). Port 0 takes a free port from the OS; pin a port here when
109
+ # an SSH session forwards it. Only routes registered by mounted plugins
110
+ # are served, and the address must stay a loopback literal.
111
+ - id: webserver
112
+ name: '@deepseek-ai/dsh-host-webserver'
113
+ config:
114
+ host: 127.0.0.1
115
+ port: 0
116
+
117
+ # Records each top-level turn's changed files from git working-tree
118
+ # snapshots; the TUI projects a bounded transcript card from Host
119
+ # summaries. Bounds match the plugin Config defaults.
120
+ - id: workspace-changes
121
+ name: '@deepseek-ai/dsh-workspace-changes'
122
+ config:
123
+ timeoutMs: 30000
124
+ outputMaxBytes: 8388608
125
+ maxFiles: 500
126
+ maxFileBytes: 2097152
127
+ diffTimeoutMs: 100
40
128
 
41
129
  # Reads its task from the ordinary tuiStartup provider.
42
130
  - id: tui-runtime
43
- name: "./dist/index.js"
131
+ name: "@crazyhappyone/dsh-tui/runtime"
44
132
  inject: [tuiStartup]
45
133
  config:
46
134
  task: !!js ctx.tuiStartup.task
47
135
  resume: !!js ctx.tuiStartup.resume
48
136
  cwd: !!js ctx.tuiStartup.cwd
137
+ model: !!js ctx.tuiStartup.model
49
138
  frameStats: !!js ctx.tuiStartup.frameStats
50
- # Render policy (overscan + stream pacing + scroll pacing + cache budgets).
51
- # Loaded by the runtime Config schema; the values below mirror the
52
- # schema defaults so the deployment layer owns the tunables. Override
53
- # here to retune without touching the plugin.
139
+ # Render policy (overscan, stream pacing, scroll pacing, cache).
140
+ # The values mirror the runtime Config schema defaults so the
141
+ # deployment layer owns the tunables; override in the profile patch
142
+ # to retune without touching the plugin.
54
143
  renderPolicy:
55
144
  transcriptOverscan: 16
56
145
  stream:
@@ -71,3 +160,8 @@
71
160
  cache:
72
161
  maxRows: 4096
73
162
  maxBytes: 4194304
163
+
164
+ # Terminal marketplace catalog uses the Host service; installs retain plugin-manager approval.
165
+ - insert:
166
+ - id: plugin-market
167
+ name: "@deepseek-ai/dsh-plugin-market"