一个dsh的终端样式插件,灵感来自Oh My Pi
37.5/100
综合分
8
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/30 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查有告警:未发布到 npm registry,仅可从源码安装。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add mytianyi0712/my-dpsk-harness-tui该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · market
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 4 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
⚠︎ 实装验证未通过(unknown · 2026/10/22) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意未发布到 npm registry,仅可从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包my-dpsk-harness-tui(未发布到 npm,仅可源码安装)
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/2 14:41:55
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 78 处,展示前 74 条)
- 发起网络请求scripts/dpsk.js:526
const response = await fetch(url, { headers: { 'User-Agent': 'dpsk' } }) - 发起网络请求scripts/dpsk.js:559
const response = await fetch(url, { headers: { 'User-Agent': 'dpsk' } }) - 请求外部主机api.github.comscripts/dpsk.js:969
const response = await fetch(`https://api.github.com/repos/${GITHUB_REPO}/releases/latest`, { - 发起网络请求scripts/dpsk.js:969
const response = await fetch(`https://api.github.com/repos/${GITHUB_REPO}/releases/latest`, { - 执行外部命令来自配置/变量scripts/daemon-client.js:143
child = spawn(wscript, ['//B', '//NoLogo', vbsPath, cmdPath], { - 执行外部命令来自配置/变量scripts/daemon-client.js:153
child = spawn(options.backend, options.args, { - 执行外部命令来自配置/变量scripts/daemon-client.js:193
const child = spawn(rendererPath, [], { stdio: ['inherit', 'inherit', 'ipc'], env: process.env, windowsHide: true }) - 读取本地文件scripts/generate-sha256sums.mjs:19
const version = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version - 读取本地文件scripts/generate-sha256sums.mjs:21
if (!existsSync(dist)) { - 读取本地文件scripts/generate-sha256sums.mjs:26
const names = readdirSync(dist).filter(name => name.startsWith('dpsk-renderer-')).sort() - 读取本地文件scripts/generate-sha256sums.mjs:35
const bytes = readFileSync(path) - 读取本地文件scripts/generate-sha256sums.mjs:38
size: statSync(path).size, - 写入本地文件scripts/generate-sha256sums.mjs:43
writeFileSync(join(root, 'SHA256SUMS.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8') - 读取本地文件scripts/verify-boundary.ts:47
const lines = (await readFile(file, 'utf8')).split(/ ? /) - 读取本地文件scripts/verify-boundary.ts:67
for (const entry of await readdir(dir, { withFileTypes: true })) { - 读取本地文件scripts/verify-boundary.ts:81
if (existsSync(RENDERER_DIR)) { - 读取本地文件scripts/verify-core-features.js:19
const packageJson = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf-8')) - 读取本地文件scripts/verify-core-features.js:72
if (existsSync(filePath)) { - 读取本地文件scripts/verify-core-features.js:73
const stats = statSync(filePath) - 读取本地文件scripts/verify-core-features.js:83
if (existsSync(stdSessionsPath)) { - 读取本地文件scripts/verify-core-features.js:84
const content = readFileSync(stdSessionsPath, 'utf-8') - 读取本地文件scripts/verify-ipc-protocol.ts:71
return readFileSync(join(ROOT, 'src', 'ipc', 'kinds.ts'), 'utf8') - 读取本地文件scripts/verify-ipc-protocol.ts:92
const text = await readFile(join(FIXTURE_DIR, name), 'utf8') - 读取本地文件scripts/verify-ipc-protocol.ts:111
if (!existsSync(RENDERER_KINDS)) { - 读取本地文件scripts/verify-ipc-protocol.ts:114
const source = readFileSync(RENDERER_KINDS) - 读取本地文件scripts/verify-ipc-protocol.ts:134
].map(path => readFileSync(path, 'utf8')).join('\n') - 读取本地文件scripts/verify-ipc-protocol.ts:135
const rendererApp = readFileSync(join(ROOT, 'renderer', 'src', 'app.tsx'), 'utf8') - 读取本地文件scripts/verify-license.ts:67
entries = await readdir(dir, { withFileTypes: true }) - 读取本地文件scripts/verify-license.ts:96
const noticeText = await readFile(NOTICE_FILE, 'utf8') - 读取本地文件scripts/verify-license.ts:122
await readdir(join(ROOT, path)) - 读取本地文件scripts/verify-license.ts:126
await readFile(join(ROOT, path)) - 读取本地文件scripts/verify-license.ts:141
bytes = await readFile(join(ROOT, item.path.replace(/ /g, sep))) - 读取本地文件scripts/verify-license.ts:178
const text = await readFile(file, 'utf8') - 读取本地文件scripts/verify-plugin-spec.ts:26
return JSON.parse(await readFile(join(fixturesDir, name), 'utf8')) as unknown - 读取本地文件scripts/verify-plugin-spec.ts:68
const raw = await readFile(join(packageRoot, 'dsh-plugin.json'), 'utf8') - 读取本地文件src/cli-config.ts:26
if (explicit !== undefined) return existsSync(explicit) ? explicit : undefined - 读取本地文件src/cli-config.ts:28
return existsSync(path) ? path : undefined - 读取本地文件src/cli-config.ts:35
text = readFileSync(path, 'utf8') - 执行外部命令固定命令src/host/autostart.ts:26
const out = execFileSync('reg', ['query', RUN_KEY, '/v', RUN_VALUE], { encoding: 'utf8', windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }) - 执行外部命令固定命令src/host/autostart.ts:70
execFileSync('reg', ['add', RUN_KEY, '/v', RUN_VALUE, '/t', 'REG_SZ', '/d', commandLine(command), '/f'], { windowsHide: true, stdio: 'ignore' }) - 执行外部命令固定命令src/host/autostart.ts:72
try { execFileSync('reg', ['delete', RUN_KEY, '/v', RUN_VALUE, '/f'], { windowsHide: true, stdio: 'ignore' }) } catch { } - 读取本地文件src/host/effect-ledger.ts:132
const raw = await readFile(this.file, 'utf8') - 写入本地文件src/host/effect-ledger.ts:162
await mkdir(dirname(this.file), { recursive: true }) - 写入本地文件src/host/effect-ledger.ts:163
await appendFile(this.file, line, 'utf8') - 读取本地文件src/host/effect-ledger.ts:173
const raw = await readFile(this.file, 'utf8') - 读取本地文件src/host/grants.ts:90
mtimeMs = (await stat(this.file)).mtimeMs - 读取本地文件src/host/grants.ts:112
const raw = await readFile(this.file, 'utf8') - 读取本地文件src/host/host-identity.ts:21
const text = readFileSync(new URL('../../dsh-plugin.json', import.meta.url), 'utf8') - 读取本地文件src/host/plugin-storage.ts:183
const raw = await readFile(this.file, 'utf8') - 写入本地文件src/host/plugin-storage.ts:212
await mkdir(dirname(this.file), { recursive: true }) - 写入本地文件src/host/plugin-storage.ts:214
await writeFile(temp, payload, 'utf8') - 写入本地文件src/host/plugin-storage.ts:216
await rename(temp, this.file) - 写入本地文件src/host/plugin-storage.ts:219
await writeFile(this.file, payload, 'utf8') - 读取本地文件src/host/renderer-log.ts:40
if (!existsSync(dir)) mkdirSync(dir, { recursive: true }) - 写入本地文件src/host/renderer-log.ts:40
if (!existsSync(dir)) mkdirSync(dir, { recursive: true }) - 写入本地文件src/host/renderer-log.ts:79
appendFileSync(logPath, text) - 读取本地文件src/host/renderer-process.ts:76
if (env !== undefined && env !== '' && existsSync(env)) return env - 读取本地文件src/host/renderer-process.ts:78
if (existsSync(cached)) return cached - 读取本地文件src/host/renderer-process.ts:80
if (existsSync(dev)) return dev - 读取本地文件src/index.ts:205
const pointer = (await readFile(dotGit, 'utf8')).trim() - 读取本地文件src/index.ts:214
const head = (await readFile(join(gitDir, 'HEAD'), 'utf8')).trim() - 读取本地文件src/index.ts:700
const raw = await readFile(path, 'utf8') - 读取本地文件src/index.ts:831
const stamp = JSON.parse(readFileSync(cleanupStampPath, 'utf8')) as { at?: unknown } - 写入本地文件src/index.ts:857
mkdirSync(dirname(cleanupStampPath), { recursive: true }) - 写入本地文件src/index.ts:858
writeFileSync(cleanupStampPath, `${JSON.stringify({ at: Date.now() })}\n`, 'utf8') - 读取本地文件src/session-list-cache.ts:51
return parseSessionListCache(readFileSync(path, 'utf8')) - 写入本地文件src/session-list-cache.ts:58
mkdirSync(dirname(path), { recursive: true }) - 写入本地文件src/session-list-cache.ts:59
await writeFile(path, `${JSON.stringify({ version: 1, sessions })}\n`, 'utf8') - 读取本地文件src/session-prune.ts:42
entries = await readdir(directory, { withFileTypes: true }) - 读取本地文件src/session-prune.ts:59
return (await readdir(directory)).includes('session.v4.jsonl.zstd') - 删除本地文件递归src/session-prune.ts:79
if (options.dryRun !== true) await rm(directory, { recursive: true, force: false }) - 读取本地文件src/settings-migrate.ts:97
text = readFileSync(path, 'utf8') - 读取本地文件src/std-contract.ts:122
const manifest = JSON.parse(await readFile(join(packageRoot, 'node_modules', packageName, 'package.json'), 'utf8')) as { version?: unknown } - 读取本地文件src/workspace-files.ts:41
const entries = await readdir(directory, { withFileTypes: true })
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
涉及外部主机:api.github.com
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/cordis-plugin-loader@deepseek-ai/dsh-agent@deepseek-ai/dsh-agent-default-model@deepseek-ai/dsh-agent-loop@deepseek-ai/dsh-agent-preset@deepseek-ai/dsh-agent-preset-registry@deepseek-ai/dsh-api-terminal-controller@deepseek-ai/dsh-attachment@deepseek-ai/dsh-cmdline@deepseek-ai/dsh-commands@deepseek-ai/dsh-compaction给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
2 张 · 点击查看大图功能亮点
- OMP风格终端界面
- 快捷键交互
README
由 DeepSeek 最新模型翻译生成my-dpsk-harness-tui
OMP 风格的 DeepSeek Harness(dsh)终端界面
简介
my-dpsk-harness-tui 是 DeepSeek Harness(dsh)的独立 profile bundle(插件),负责终端呈现、输入交互与会话相关的 TUI 能力;智能体、模型、工具、持久化和沙箱仍由 dsh harness 提供。
目前本项目正在迁移并合并 DeepSeek Harness 社区发起的公开适配器支持,正在向社区公开的 @dsh-std/adapter-dsh 与 dsh-ecosystem-spec v0.15 插件准入规范迁移;详细说明见 社区适配器迁移说明。
它内置 33 个 opencode 风格主题(原生 ThemeProvider,0.5.0 起配色由渲染器拥有)、会话与快捷键体系、/models 与 /permission 等斜杠命令,以及从 OMP 迁移来的微信桥插件(WeChat iLink),可直接通过 dsh --profile tui 启动使用。
[!IMPORTANT]
维护状态说明
- 项目目前已经可以作为 TUI 工具正常使用,必要功能均已测试通过。
- wechat-claw 通道的远程控制能力已开发完成。
展开完整文档(README 剩余 1 节)
my-dpsk-harness-tui
OMP 风格的 DeepSeek Harness(dsh)终端界面
简介
my-dpsk-harness-tui 是 DeepSeek Harness(dsh)的独立 profile bundle(插件),负责终端呈现、输入交互与会话相关的 TUI 能力;智能体、模型、工具、持久化和沙箱仍由 dsh harness 提供。
目前本项目正在迁移并合并 DeepSeek Harness 社区发起的公开适配器支持,正在向社区公开的 @dsh-std/adapter-dsh 与 dsh-ecosystem-spec v0.15 插件准入规范迁移;详细说明见 社区适配器迁移说明。
它内置 33 个 opencode 风格主题(原生 ThemeProvider,0.5.0 起配色由渲染器拥有)、会话与快捷键体系、/models 与 /permission 等斜杠命令,以及从 OMP 迁移来的微信桥插件(WeChat iLink),可直接通过 dsh --profile tui 启动使用。
[!IMPORTANT]
维护状态说明
- 项目目前已经可以作为 TUI 工具正常使用,必要功能均已测试通过。
- wechat-claw 通道的远程控制能力已开发完成。
- 由于本人近期发现利用dsh万物皆插件的性质+本地部署小模型或者接入其他低价模型,用来处理一些日常任务好像可以达成很不错的效果,可以随便定制自己想要的功能,因此本人准备继续缓慢维护该项目,只解决个人需求,如有其他功能需求,可以提交issue。
[!WARNING]
0.5.0 破坏性变更
- 改名:包名 dsh-omp-tui → my-dpsk-harness-tui,启动器 bin omdsh → dpsk(OMDSH_NO_BOOTSTRAP → DPSK_NO_BOOTSTRAP)。已安装的全局启动器需要重装(npm install --global ./<tgz>)才会生成 dpsk shim;旧的 omdsh shim 不会自动消失。dpsk 启动、dpsk check、dpsk update 都会把 profile 里的旧包从 dsh.profile.bundles 与依赖表里摘掉(两个包注册同一个 tui 服务,留着会让新包整体不激活),并把 node_modules/dsh-omp-tui 让位重命名为 dsh-omp-tui.dpsk-legacy-<时间戳>,内容保留不删除。
- settings 命名空间:tui → dpsk-tui,session-title-llm-tui → dpsk-session-title。激活时自动迁移一次并打印迁移报告;新命名空间已有值的字段不会被旧值覆盖,因此重启后不再写入。
- 渲染器是默认路径:终端交给 OpenTUI 渲染器子进程(Bun 编译的单文件产物,启动器按平台下载并校验 SHA-256,缓存于 ~/.dsh/dpsk/renderer/<version>-<platform>/,启动时自动确保在缓存里并把路径写入 DSH_RENDERER_PATH)。pi-tui 渲染基座已随 0.5.0 退出删除,没有回退路径;取不到产物时启动器报告并以非零码退出。渲染器路径下 dsh 的 stdout / stderr 改道写入 $DSH_HOME/logs/tui-<pid>.log。
- 首页(0.5.0):空会话且空闲时进 opencode 风格的首页——顶部会话 tab 条(编号格 + 标题 + active × + 右键菜单 + + ,Ctrl+Tab/Alt+↓ 切换、leader w 关闭、Ctrl+Shift+T 重开)、deepseek 字标、居中 prompt(agent auto · provider/model · effort)。发送首条消息后切到会话路由。
- 底部信息分两处:T<turn> S<step> · <tps> tok/s | <tokens> tokens · cache <pct> | <used>/<window> (<pct>%) 独占终端最后一行(会话、Home、停靠侧栏、终端面板都一样,缺哪项就整段省略)。它上面的工作目录行(完整路径 → 末级目录 → ... + 尾部,按终端 cell 宽度计算)跟着输入框走:Session 里贴着停靠的 prompt,Home 里跟着居中的 prompt 一起浮动,右侧是 shift+tab agents · ctrl+p commands(按键正文色、说明弱化色),空间不足时先隐藏提示再压缩目录,运行中右侧换成 spinner + esc interrupt。不再有 Home 专属底栏(⊙ N MCP /plugins、版本行)与 WeChat 状态行。
- 额外信息都在侧栏:WeChat 连接状态与登录二维码、harness 版本、MCP 服务器、插件清单、上下文占用、provider/model、workspace/branch 全部在右侧栏内;侧栏内容区可滚动(滚动条绝对定位在预留 padding 内),Home 与 Session 用同一条停靠规则(宽终端分栏、窄终端 overlay)。
- 渲染器已覆盖:transcript、composer(历史 ↑/↓、生成中回车排队插话、Alt+Up 编辑插话、斜杠补全、@ 路径补全)、对话框基座与选择器、命令面板 ctrl+p(含 skills 分类;文案全部走宿主字典——分节标题、动作行、过滤框 搜索、底部 移动/运行 在 zh 下都是中文,条目 id 与英文关键词仍可搜索)、settings 页(四字段 + max parallel tool calls(agent-loop 命名空间,写回带 ns)+ 左右提示模板(底栏固定后不再绘制,仅保留可编辑的存量值)+ key tools / key reasoning 两行 + 供应商表单与自定义模型表单入口)、会话轨与切换、模型 / 思考强度 / 权限 / 预设写回、微信桥二维码与状态行、/doctor /plugins、原生主题(themeName / themeMode / New designs)、i18n 标签(zh/en)、供应商表单(id / 名称 / 接口类型 / Base URL / API Key / 模型清单扁平行,走 form-spec / form-submit)、模型发现探测(表单里的 [ 探测上游模型 ] 行,Enter 把当前字段值送上去走 model-discover,宿主跑 ctx.llm.discoverModels 并把合并后的行发回 model-discovery,落进模型字段;探测与命名空间查找、凭据规则都在宿主)、自定义模型(单行模型 id,写 SESSION_TITLE_SETTINGS_NAMESPACE)、user questions(逐题提问,走 question-request / question-answer)、技能命令(skill: 条目随 command-catalog 重发抵达面板与斜杠补全)、子代理面板(↳ label · provider · mode 行,走 subagent-list,由同一份 subagent/descriptor 事件喂)、逐模型嵌套编辑器(provider 表单里的 [ 编辑模型 ] 行,Enter 发 model-list-request,宿主把当前扁平行解析成模型清单发回 model-list,选择框行 id (label);选中发 model-edit 带上打开编辑器时记住的那条扁平行,宿主为选中的模型建六字段表单(id / name / efforts / effort / context / max,form-spec 的 form: 'model');提交合并进记住的扁平行、替换被选中的模型(id 可重命名,空 id 保留原 id;efforts 为 none 写 false,空写不改动),合并后的行经 model-discovery 落回模型字段。扁行的编解码与表单提交、发现探测共用同一份函数,解析、编码、合并都在宿主)。
- 渲染器尚未覆盖:插件 scene 贡献(渲染器没有 scene host,extensions.scenes.installBackend 的 open 返回 false,fail-closed;与 React scene 契约的偏差记录在 docs/contracts.md)、插件静态转写行(transcriptEvents 没有插件行角色,extensions.renderers 的行不进渲染器 transcript)、/wechat- 结果(渲染器没有静态卡片面,这些行走 toast overlay;/skills /status /help 已在渲染器内实现为选择器/详情页)。键位:渲染器自己的 keymap 是固定的 leader 表(ctrl+x 和弦 + ctrl+p 面板 + ctrl+t 循环思考强度),不读 settings 页的 key tools / key reasoning 两行(值会写回 dpsk-tui 节,但渲染器没有数据驱动的键位表);从 cli.json 导入的 keybinds 同样不作用于渲染器;插件快捷键(tuiShortcuts)端到端接线:渲染器把带修饰键的按键规范化成 ctrl+alt+shift+base 串走 shortcut 上游,宿主把注册串两侧规范化后匹配并 dispatch。patches/(vendored turtle-ui patch)、verify:patch-surface 门禁与旧的宿主终端 UI 装配已随基座退出删除;渲染器实现并已覆盖 /export(写 $DSH_HOME/exports)与 /rename(走 session-op rename),多行字段(textarea)与 /skills 选择页也已在渲染器内落地。渲染器不再依赖旧的宿主终端 UI 库:src/tui-types.ts 提供本地类型,lib/ 不再包含该库的代码。
- 原生主题:渲染器 ThemeProvider 直接移植 opencode v2.0.32 的主题核心(色阶 / 语义 token / @dialog surface / generateSyntax)与全部内置主题,选择持久化在宿主 dpsk-tui 设置节(themeName / themeMode / newDesigns)。终端 system 主题由真实调色板生成;custom 主题从 (DSH_HOME ?? ~/.dsh)/themes 与项目祖先 .deepseek/themes 发现。宿主不再合成 token、也不再下发 theme 文档。对话流的取色按上游组件走:用户卡片 ┃ 用 agent 色 + background.raised.base,回答 markdown.text,思考头 text.feedback.warning.base(打开 60%、折叠满色),思考正文把每个 syntax token 重染成 text.muted(mutedSyntaxStyle,等价上游 generateThinkingSyntax),工具行/hint 用 text.muted,diff 走 diff. 六色。输入框竖条与 ╹/▀ 收口同色(hue.accent.200),元信息 agent auto · provider/model · effort 分别是强调 / 弱化 / 弱化+正文 / 警告+加粗。
- 界面语言:中文优先 —— 未配置 tui.locale 时一律用 zh-CN(不嗅 Intl/LANG:dsh 把插件跑在 ICU 裁剪过的 Electron 进程里,中文本机也会报 en-US),要英文就写 locale: en,或用 /settings 里的「界面语言」一行切换(写入 patch 层,重启生效)。斜杠命令名、命令面板、侧栏与底栏文案都取自同一份字典。CJK 限制:OpenTUI 把非 ASCII grapheme 存进原生 grapheme 池;终端未上报显式宽度能力时,CJK 标签在渲染器里序列化为空格(Windows Terminal / 已上报宽度的终端正常)。
- 命令与键位(渲染器路径):leader ctrl+x(n new / l sessions / m models / t themes / c compact / s settings / x export / q exit / b sidebar)、命令面板 ctrl+p、变体循环 ctrl+t;Tab 与 Enter 接受补全(命令与参数两段都走这套 popup;行是 opencode 形状——/name 描述 一行,标记只给 @ 引用行右对齐,不再是 omp 的 kind 列),↑/↓ 只在草稿首/末行才切历史,Alt+Up 编辑插话。斜杠命令一次 Enter 即生效:裸形式(/models /agents /effort /sessions /themes /status /permission /skills /settings /help /mcps /rename /plugins /doctor,名字对齐 opencode,旧的 /model /mode /think /resume /theme /details 仍作别名)在渲染器内打开选择器,声明了参数的宿主命令(如 /btw)插入 /name 等待参数,无参宿主命令直接发送;带参数的形式仍交宿主执行。不提供 /undo /redo:dsh harness 没有 git 快照层,本版本不做假实现。
- cli.json 一次性导入:OPENCODE_TUI_CONFIG(权威,指向缺失文件时不读别的配置)或 ~/.config/opencode/cli.json 里的 theme / keybinds / leader_timeout / scroll_speed / scroll_acceleration / diff_style / cursor / mouse 在启动时导入 dpsk-tui 节;已有值保留,cliImportedAt 记录已导入,重启写零,报告打印。
目录
- 简介
- 界面预览
- 功能概览
- 安装
- 启动与日常使用
- 核心守护进程与退出语义
- 配置
- 开发
- 项目结构
- 社区适配器迁移说明
- 变更记录
- 相关文档
- 许可
界面预览
截图来自本机 WezTerm 中实际启动的 dsh --profile tui。演示实例未配置 API key,因此截图聚焦于启动页、状态栏和内置帮助面板,不代表模型响应效果。
0.5.0 的底部已改为「指标行固定 + cwd 行跟随输入框」,额外信息移到右侧栏;截图可能仍是改动前的版本。实机验收清单见 docs/ACCEPTANCE_TUI_SHELL.md。
欢迎页
输入 /help 可查看快捷键和命令
工具卡支持折叠、展开和隐藏;助手正文、思考块、上下文卡和 Output 分隔栏使用独立的视觉层级。
功能概览
| 功能 | 说明 |
| 🎨 OMP 风格 TUI | 默认 Catppuccin 动态深浅主题、truecolor、Nerd Font 图标和简洁双横线编辑区 |
| 📐 响应式布局 | 工作目录固定在编辑器下方左侧,权限模式右对齐;模型、思考强度与 ctx 独立成行,窄终端优先保留权限与上下文 |
| 💬 会话管理 | 支持新建、命名、恢复和进程内切换持久化会话 |
| ⏱️ 插话队列 | 生成中按 Enter 把消息加入插话队列,在下一步(通常在工具调用完成后)送达模型;Alt+Up 编辑未生效插话并取消排队 |
| 🧠 模型与思考等级 | 通过 /models、/effort 选择 provider、model 和 reasoning effort |
| 🔀 工作模式 | 内置 standard、minimal、code、cordis 四套 agent preset 组合(dsh 0.2 不再随发行版提供预设名册,本 bundle 自带声明),也能发现本地安装的 agent preset |
| 🔐 权限模式 | 通过 /permission 在 read-only、workspace-write、full-access 等部署可用预设间切换 |
| 🌗 主题与本地化 | 内置 33 个 opencode 风格主题(renderer/src/theme/native/assets),支持 system 跟随终端明暗与 dark / light 固定模式;opencode2 始终可选,不再受思考样式开关影响;内置 zh-CN 与 en |
| 🧠 思考与工具呈现 | /settings 的 new thinking style 可在 classic 与 compact 间即时切换:compact 把正文前的多段思考与工具合并成一个 5–10 行自动尾随预览,工具显示为 Running/Done/Failed/Denied 单行,点击标题展开为经典 Markdown + 工具卡片 |
| 🗂 会话标签页 | OpenCode v2 横向 tab:稳定手动顺序、Home 草稿 tab、首条消息原位提升、hover 关闭、拖动重排、关闭/原位重开、真实 running 指示;Ctrl+1..0 选择 tab,Ctrl+Tab / Ctrl+Shift+Tab 循环,Ctrl+Shift+T 重开 |
| 🏠 路径显示 | 主界面路径默认把用户目录缩写为 ~(Windows 与 Linux 一致),设置页可切为完整路径;仅影响显示,不改变实际 cwd/请求/附件 |
| ⌨️ 命令与补全 | 支持斜杠命令、@ 会话/文件引用、路径补全和参数补全 |
| 💚 微信桥 | 扫码登录、白名单/配对码、@dsh 远程命令、进度/结果推送、wechat_send / wechat_status 工具 |
思考与工具显示
/settings → new thinking style 控制 transcript 的呈现方式,默认关闭,主题选择不受影响:
- classic(关闭):保留现有 opencode 单行 Thinking / Thought + ┃ 思考正文与工具行;只修正进行中的标题词并支持点击折叠。
- compact(打开):正文出现前,同一 turn 的 reasoning、tool call 和状态更新只显示一个标题,例如 - ⠋ Thinking 12s | Used 2 Tools。下方是 5–10 行自动尾随预览;工具为 Running: Read src/app.tsx、Done: Shell npm run check、Failed: ... / Denied: ... 单行,超宽以 ... 截断。
- 正文开始或 turn 结束后,标题变为 Thought 1m 05s | Used 2 Tools 并默认收起。点击标题展开完整 reasoning Markdown
- 一个 turn 内的中间 assistant 正文会结束当前 compact block;后续 reasoning/tools 会开启新的 compact block,不会让剩余步骤回落成 classic。
与 classic 工具卡片,再点收起;用户手动展开后,正文到达不会强制折叠。
- 用户卡片与下一条 thinking / reasoning / 正文之间固定保留一行空白,classic 与 compact 一致。
- show reasoning: hide 只隐藏自动预览/正文,仍保留可点击的标题与工具计数;完整内容可手动展开。
- 拖动选择使用主题选择色,单击不会留下零长度选区或小方块;提交、切换会话、开始新一轮会清理选区。leader y 仍可复制最后一条回答。
- resume 后列表项中的 strong 不再泄漏标记。OpenTUI 0.5.17 对列表项 inline strong 的 conceal 有缺陷,本版对列表项做定点去标记;升级到 0.6.6 需要新的 SyntaxStyle 资源所有权适配,未在本版强制升级。
会话标签页、选择器与路径显示
- tab strip 按用户手动顺序排列,host 的 lastActivity 只决定 /sessions 排序,不会重排顶部 tab。
- + / leader n 创建唯一的 Home 草稿 tab;首条消息成功后原位提升为真实 session,不产生第二个空 tab。
- active tab 常驻 ×,inactive tab hover 时出现关闭格;中键关闭、右键菜单、拖动重排、Ctrl+Shift+T 原位重开。
- running session 显示真实状态点,不伪造 unread/attention。OpenCode 横向 tab 本轮完成;tabs.mode/scope/layout/indicators 和 vertical rail 留到横向实机验收后的配置阶段。
- /sessions 每行只显示标题 + today / yesterday / last 7 days / 日期,不再显示 id、cwd、父子关系或 status;current 用左侧圆点标识。
- Ctrl+T 向前循环思考强度并在末尾回到第一个;Tab 仍是补全键,Ctrl+Shift+T 仍是重开 tab。
- 运行中的 footer 把 spinner + esc interrupt 放在原本 cwd 的左侧位置;右侧继续显示 shift+tab agent · ctrl+p commands,窄终端优先保留 interrupt。
- 向上滚动离开底部时,输入框上方右侧出现可点击的 Jump to latest / 跳到最新 overlay;它不占 transcript 行,补全面板打开时隐藏。
- 路径设置 path display: ~ / full path 默认 ~。显示层只缩写用户目录(C:\Users\me\x / /home/me/x -> ~/x),真实 workspace、shell、附件、terminal、剪贴板与导出路径保持完整。
会话排序与空会话
启动 TUI 时只创建内存中的空壳 agent;持久化层由 lazySessionPersistence 门控,直到第一条 user/message / assistant/message / tool/call 事件才真正创建会话文件。启动后直接退出、/new 后不发送、 resume 后不发送都不会新增可恢复的空会话。
host 还会用 live agent 的内存事件判断尚未物化的空壳:无标题且没有任何对话事件的 live 行同样标记 blank, picker 和 tab catalog 完全过滤这些行(包括 current),不会再画出不在磁盘上的 tui- ghost 会话。
旧版本留下的、经日志确认只有 setup 事件的 tui- 目录会在启动时安全清理,包含任何对话事件的目录不会删除。
会话列表、/sessions、host /resume picker 与 today/yesterday 分组统一使用 lastActivityAt(最后一条事件的 time,缺失时回退 createdAt)。刚用过的旧会话会回到顶部和今天分组。
安装
运行要求
| 项目 | 要求 |
| Node.js | ^22.19.0 或 >=24.0.0 |
| pnpm | 11.7.0 或兼容的 pnpm 11 |
| dsh | 0.2.0-rc.2 与 0.2.1-rc.2(两条线均已支持;开发依赖钉在新版);0.1.2-alpha.2/3 与 0.2.0-rc.1 也在 std adapter 覆盖线内 |
| 终端 | 推荐支持 truecolor;使用 Nerd Font 可获得完整图标显示 |
[!NOTE]
当前 dsh 仍处于 developer preview。首次安装建议固定 dsh 版本和插件 release tag。
从 GitHub Release 安装
没有 pnpm 时先安装固定版本:
npm install --global pnpm@11.7.0
然后安装当前 release tarball:
npx --yes @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile tui add \
https://github.com/mytianyi0712/my-dpsk-harness-tui/releases/download/v0.5.0/my-dpsk-harness-tui-0.5.0.tgz
tarball 已包含构建后的 lib/,用户机器无需编译本项目。
从 GitHub tag 安装
Git 安装会执行本项目的 prepare 构建。pnpm 11 默认阻止依赖构建脚本,因此必须显式允许本包:
npx --yes @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile tui add \
--allow-build=my-dpsk-harness-tui \
github:mytianyi0712/my-dpsk-harness-tui#v0.5.0
固定 tag 比直接使用 #main 更容易复现。完整的升级、卸载和安装排障说明见 docs/INSTALL.md。
全局安装启动器
如果希望直接使用 dpsk 启动器,也可以全局安装本地 tarball;包内的 dsh 宿主 peer 依赖为 optional,npm 11 不会因 peer 图触发 Arborist 的 null.children 崩溃:
npm install --global ./my-dpsk-harness-tui-0.5.0.tgz
npm install --global @deepseek-ai/dsh@0.2.0-rc.2
dpsk
[!NOTE]
dpsk 只负责调用 PATH 中的官方 dsh;若未安装官方 dsh,会显示 dsh is not recognized。
渲染器产物(默认路径)
终端交给独立的 OpenTUI 渲染器子进程。渲染器是 Bun 编译的单文件产物(74–99 MB,随平台而定),用户不需要安装 Bun:
dpsk # 渲染器界面(默认) dpsk renderer install # 下载当前平台产物,先比字节数再比 SHA-256,两者都过才落盘 dpsk renderer path # 打印缓存路径(无缓存时输出空行) dpsk renderer check # 本地哈希与随包 SHA256SUMS.json 比对
缓存目录 ~/.dsh/dpsk/renderer/<version>-<platform>/(platform 形如 win32-x64)。
启动时自动确保产物在缓存里并把路径写入 DSH_RENDERER_PATH;取不到产物时报告并以非零码退出(pi-tui 渲染基座已删除,没有回退路径)。DSH_RENDERER_PATH 可显式指定产物路径(测试与开发构建用)。dpsk check 会报告当前平台缓存产物的哈希与随包清单的比对。
产物覆盖 7 个平台:win32-x64、win32-arm64、linux-x64、linux-x64-musl、 linux-arm64、darwin-x64、darwin-arm64。启动器只下载当前平台那一个。
核心守护进程与退出语义
dpsk 默认先连接本地核心守护进程;守护进程未运行时会在后台启动它,再只启动当前终端的 TUI 外壳。
Windows 上后台 core 通过隐藏窗口启动,不会额外弹出终端窗口;非 Windows 平台使用 detached 子进程。
守护进程通过当前用户的本地命名管道(Windows)或 Unix domain socket(macOS/Linux)通信,令牌保存在 ~/.config/.mydpsk/runtime/daemon.token,不开放 TCP。
dpsk daemon status # 查看核心是否运行及 pid dpsk daemon start # 静默启动核心(不打开 TUI) dpsk daemon stop # 彻底结束核心及所有任务 dpsk daemon restart # 重启核心 dpsk daemon logs # 查看核心日志路径/最后 50 行
- /exit、空输入框 Ctrl+C、leader q 或直接关闭终端只退出 TUI;核心和正在跑的任务留在后台。
- 有运行中任务时,退出会先提示:退出界面并在后台继续、打开全部进行中会话、取消。
- /shutdown 与命令面板的全量退出命令会确认后终止核心。
- 设置页的开机启动核心注册操作系统登录项;状态从真实注册表、LaunchAgent 或 XDG autostart 文件读取。
- 需要旧的内嵌单进程行为时设置 DPSK_EMBEDDED=1。
启动与日常使用
启动会话
使用 npx
npx --yes @deepseek-ai/dsh@0.2.0-rc.2 --profile tui
npx --yes @deepseek-ai/dsh@0.2.0-rc.2 --profile tui --session my-id
npx --yes @deepseek-ai/dsh@0.2.0-rc.2 --profile tui --resume <session-id>
已安装 dsh launcher 后
dsh --profile tuish
dsh --profile tui --session my-id
dsh --profile tui --resume <session-id>
已安装本包的 dpsk 启动器后(自带 --profile tui,不要重复传)
dpsk # 渲染器界面(默认路径) dpsk --resume <session-id>
--resume 与 --session 互斥。新会话只有在首次产生用户消息、助手消息或工具调用后才会落库;空白会话不会出现在 /sessions 列表中。
快捷键
| 快捷键 | 作用 |
| Esc | 中断当前回合(弹窗打开时关闭弹窗) |
| Enter | 空闲时发送;生成中把当前消息加入插话队列 |
| Ctrl+P | 打开命令面板(补全 popup 打开时是 popup 的上一项) |
| Ctrl+T | 循环 reasoning effort 变体 |
| Ctrl+X | leader 和弦(2 秒窗口):n new / l sessions / m models / t themes / c compact / s settings / x export / q exit / b sidebar / w 关闭当前会话标签 / 1–9 选会话 |
| Ctrl+Tab / Ctrl+Shift+Tab | 循环切换会话标签(反向) |
| Ctrl+Shift+T | 重开最近关闭的会话标签(插回原位) |
| F2 / Shift+F2 | 循环切换模型(反向) |
| Shift+Tab | 循环切换智能体预设 |
| Tab | 接受补全(popup 打开时) |
| ↑ / ↓ | composer 历史(仅光标在首/末行时;popup 打开时为 popup 项) |
| Ctrl+N | popup 下一项 |
| Alt+Up | 编辑下一条尚未生效的插话(自动取消排队) |
| Alt+Left / Alt+Right | 切换 composer 抽屉的页签 |
| @ | 开始会话或文件引用补全 |
| /effort hi、/permission wo、/agents co、/sessions ses、/models cliproxy/ | 斜杠命令的参数补全(值来自宿主已推送的目录) |
| PageUp / PageDown | 弹窗翻页;无弹窗时滚动转写 |
| Ctrl+C / Ctrl+D / Ctrl+O / Ctrl+Q / Alt+L / Alt+M | 渲染器 keymap 不绑定这些键(pi-tui 基座已删除) |
常用命令
| 命令 | 作用 |
| /help | 查看快捷键和完整命令列表 |
| /models(别名 /model) | 打开专用模型选择器:按服务商分组、标出当前模型、可搜索;选中后经宿主持久化(F2 仍是快速循环) |
| /models、/agents、/effort、/sessions、/themes、/status、/permission、/skills(无参) | 打开对应选择器;名字对齐 opencode,旧的 /model /mode /think /resume /theme /details 仍可用 |
| /effort [level](别名 /think /variants /thinking) | 切换当前模型的推理强度;无参时打开选择器 |
| /new | 在当前项目、模型和权限模式下新建会话 |
| /sessions [id](别名 /resume /continue) | 列出或切换当前项目的持久化会话 |
| /status(别名 /details) | 查看会话标题、目录、模型、智能体、tokens 和上下文 |
| /agents [preset](别名 /mode) | 切换 dsh 智能体组合;无参时打开选择器 |
| /permission [preset] | 切换沙箱和审批策略 |
| /themes(别名 /theme) | 打开原生主题选择器(渲染器拥有主题目录) |
| /palette | 查看当前主题实际使用的颜色角色 |
| /settings | 打开可视化设置 |
| /skills | 列出可用技能 |
| /price [model-id] | 从程序 price.json 给当前模型指定价格;无参时打开价目选择器,结果写入 custom_price.json 并立即重算 |
| /restart | 通过 dpsk supervisor 重启 dsh,重载 DSH 侧配置;保留会话、标签和草稿 |
@label 会把目标会话的模型可见快照注入当前会话;具体 URI 由输入 @ 触发补全生成。更多命令以运行中的 /help 为准。
微信远程桥接(WeChat iLink)
本 bundle 内置了从 OMP 迁移来的微信桥插件:通过腾讯官方 ClawBot / iLink 通道,把 dsh 会话连接到微信,支持扫码登录、白名单/配对码、远程 @dsh 命令、自动进度汇报与结果推送、wechat_send / wechat_status 工具,以及 ask 提问同步推送到微信。
sh
在 dsh TUI 中扫码登录
/wechat-login
陌生微信用户会收到 6 位配对码,在 dsh 中批准
/wechat-pair 123456
查看桥状态
/wechat-status
微信里以 @dsh 开头的消息会被当作远程命令,不会进入会话(例如 @dsh status、@dsh models、@dsh think max、@dsh notify on)。@dsh chat 会切换到 chat 模式:之后的所有普通微信消息都实时分段回传模型文本,且不会注入进度汇报。@dsh work 会切换回 work 模式:之后的所有普通微信消息都在任务结束后回传完整结果,并按进度配置汇报。所有 @dsh 远程命令仅限微信侧调用,不会出现在本地 /help、命令补全菜单中,也无法在本地以 /dsh- 形式执行。模型可用 wechat_send 工具回复。
状态目录:~/.dsh/wechat-ilink/(可用环境变量 DSH_WECHAT_ILINK_STATE 覆盖)。登录二维码同时写入 ~/.dsh/wechat-ilink/login-qr.txt,方便无界面场景查看。
微信推送
微信桥支持把任务进度与结果自动推送到微信:
- 微信 work 模式:@dsh work 切换后,所有普通微信消息都会在任务结束后发送结果。模型回合每达到配置的间隔轮数时,会向微信推送一次进度。
- 微信 chat 模式:@dsh chat 切换后,所有普通微信消息在生成期间实时回传可见文本,不推送周期进度,也不会在结束时重复发送完整结果。
- 终端任务也推送:默认关闭。使用 /wechat-notify on 开启后,终端发起的任务也会在进度/完成时推送到微信;/wechat-notify off 关闭。
- 可视化配置:在 /settings 的 微信claw 标签页中可配置:
- 进度汇报:开 / 关
- 进度汇报间隔(轮)
- 终端任务推送微信:开 / 关
配置保存在 ~/.dsh/wechat-ilink/config.json,可通过环境变量 DSH_WECHAT_ILINK_STATE 改变状态目录。
使用 dpsk 启动 TUI
项目提供 dpsk 启动器(scripts/dpsk.js 为跨平台 bin,另有 scripts/dpsk 与 scripts/dpsk.cmd)。dpsk 会调用系统 PATH 中的官方 dsh 并启动 --profile tui;本项目不下载、不缓存 dsh。正常启动不打印日志、不迁移设置、不自动升级,只会做一次新旧包交替的自愈摘除(0.4.0 的 dsh-omp-tui 与本包注册同一个 tui 服务,留在 profile 里会遮住本包);首次尚无 tui profile 时会静默安装当前启动器包(可用 DPSK_NO_BOOTSTRAP=1 跳过)。同版本内容漂移请使用 dpsk check 用全局包覆盖 profile。从 GitHub 获取新版本并做适配请使用 dpsk update。官方 dsh 命令(web、plugin、显式 --profile 等)请直接使用 dsh。
sh
开发环境:把仓库 scripts 目录加入 PATH
export PATH="$HOME/my-dpsk-harness-tui/scripts:$PATH" # Git Bash / zsh
dpsk # 等价于 dsh --profile tui dpsk --resume <session-id> # 透传给 tui profile dpsk check # 比较全局包与 tui profile,有差异则用全局覆盖 dpsk update # 从 GitHub Release 安装最新包并适配
Windows cmd / PowerShell
将 <仓库>\scripts 加入 PATH,或直接运行 <仓库>\scripts\dpsk.cmd
安装发布包后,dpsk 会由 bin 入口安装到 profile 的 .bin 目录(例如 ~/.dsh/profiles/tui/node_modules/.bin/dpsk),把该目录加入 PATH 即可直接使用。
需要指定真实 dsh 可执行文件时设置 DSH_REAL;设置 DSH_DEBUG=1 可以只打印启动器解析出的命令,不启动 dsh。
会话归组与桌面端
官方桌面客户端按 dsh 的 workspace 注册表(~/.dsh/storages/workspace.json)把会话分到各个工作区下,没有被任何工作区记账的会话会进「未分组」。那份注册表属于 dsh 的 web 层,终端 profile 默认没有它,所以本 profile 自带 workspace-sessions 宿主行:它按需挂载 @deepseek-ai/dsh-workspace,用会话头里的 cwd 找到(必要时登记)对应工作区,再按标准 API attachSession 记账,新会话与已存在磁盘上的历史会话都会归位。@deepseek-ai/dsh-workspace 是可选依赖,装不上时行为退回原先的未分组,不会影响启动。
与桌面端同时运行时,两边各自持有 workspace.json 的内存副本(dsh 的 JSON 存储假设单写进程),后写的一侧会覆盖另一侧;被覆盖的归组会在下次终端启动的回填里自愈。想让桌面端立刻看到,先退出终端、再重启桌面客户端即可。
dsh 后端与会话库
dpsk 启动的 dsh 就是系统上的 dsh(PATH 或 DSH_REAL),会话库统一是 $DSH_HOME/sessions (默认 ~/.dsh/sessions),和桌面客户端同一份:桌面端建的会话在 TUI 的 /sessions 里能看到。
读日志的是后端自己的版本,所以两边版本不一致时,新版写下的会话(job/registered、 workspace/changes 这类新事件)旧后端读不出来,列表里会少几条——dpsk backend list 会把这层关系打出来: sh dpsk backend list # 可启动的后端、会话库、桌面端运行时版本、版本差提示 dpsk backend reset # 忘掉记住的选择,下次启动重新询问
系统里有多份 dsh(多个 Node 发行版各自全局安装,PATH 里会有多个)时,首次启动会列出它们让用户选一个,选择记在 ~/.config/.mydpsk/backend.json;只有一个时不询问。旧版 ~/.dsh/dpsk/backend.json 会在首次启动时复制过去,原文件保留。DSH_REAL 永远优先于记忆里的选择。
/sessions 的标题来自会话日志里的 session/title 事件。没有该事件的老会话,宿主会读取日志开头那条用户消息,用它的正文当标签;只有 setup 事件、没有任何对话的空壳会话(persisted 或 live)都按桌面端的同一规则隐藏。
新版本还通过 conversation-gated persistence 阻止空会话落盘,并在启动时清理历史空目录,所以列表里不再是一片 New Session 加 id,也不会每次启动多一条不可恢复的垃圾会话。会话按最后交互时间排序,详情见上文。
程序配置、费用估算与重启程序自有配置集中在 ~/.config/.mydpsk。DSH 的 profile、sessions、attachments 和 settings 仍归 $DSH_HOME,不会搬进这里: text ~/.config/.mydpsk/ config.json # 程序配置(外部编辑,热更新;含 lastSelection) price.json # 程序自带公开价格表(程序初始化/维护) custom_price.json # 用户覆盖表(/price 写入,也可手动编辑) backend.json # dpsk 记住的 dsh 后端(启动时读取) runtime/ # /restart 的临时 handoff
- price.json 是程序自带公开价格表,首次启动从随包表初始化;custom_price.json 初始为空,优先级高于 price.json。
- TUI 会把上次实际使用的 provider/model、思考强度与 agent mode 写入 config.json 的 lastSelection,下次启动自动恢复;恢复历史 session 时仍以该 session 自己的 request header 为准。
- /price 无参数时从 price.json 选择一条价格给当前模型;/price <model-id-or-alias> 直接按 id/别名匹配。保存后写入 custom_price.json 并立即重算当前会话费用。
- 手动修改价格时优先编辑 custom_price.json,不要把长期自定义价格写进程序维护的 price.json。
- 价目表是 USD / 1M tokens,分 input / cacheRead / cacheWrite / output;匹配支持 provider/model 精确值、路径前缀、源模型 id 别名、版本无关 family 匹配,以及 DeepSeek 的时区峰谷规则。
- custom_price.json 的热更新会即时生效;无效 JSON、schema 错误或半写入文件会保留 last-good 配置,不会把运行中的 TUI 打挂。
- 侧栏 context 区固定为 CTX(当前上下文)、SUM(累计 tokens,附 cache 命中率)和 $(本地估算 USD 花费)。未匹配到模型价格时显示 $ 未配置模型价格,不会伪造 $0.00。
- /restart 会通过 dpsk supervisor 重启当前 dsh 子进程,重新加载 DSH 侧配置;保留当前 session、tabs 和草稿。普通退出、Ctrl+C 和崩溃仍按正常语义退出。直接运行 dsh --profile tui 时该命令会提示不可用。
- dpsk 已记住的 backend 会用文件签名快速验证,命中时跳过 PATH 扫描和 dsh --version;DSH_REAL、路径失效或文件被替换时仍回退完整探测。
配置
模型连接
官方 DeepSeek API: sh
Git Bash / zsh
export DEEPSEEK_API_KEY='your-key'
PowerShell
$env:DEEPSEEK_API_KEY = 'your-key'
本地 OpenAI-compatible 网关: sh
Git Bash / zsh
export DEEPSEEK_BASE_URL='http://10.0.0.1:3000/v1'
PowerShell
$env:DEEPSEEK_BASE_URL = 'http://10.0.0.1:3000/v1'
环境变量必须在启动 dsh 的同一个 shell 中可见。启动后可用 /models 选择并持久化 provider、model 和思考等级。
TUI profile 配置
在 profile 的 cordis.patch.yml 中配置 id: tui 行,或使用 dsh settings 注入同一配置: yaml
- id: tui
config:
mode: standard # standard | minimal | code | cordis 或本地 preset id locale: zh-CN # zh-CN(默认)| en defaultReasoningEffort: max themeName: opencode # opencode | opencode2 | system | 主题文件 id themeMode: system # system | dark | light newDesigns: false # 实验:New designs(提供 opencode2 与新版布局)
- mode 只对空白会话生效;切换结果会写入会话日志,恢复会话时沿用。
- /themes 和 /settings 的选择写入 profile 的 patch 层(~/.dsh/profiles/tui/cordis.patch.yml),按行 id 寻址:本 bundle 的设置节在 dpsk-tui 行,标题模型在 dpsk-session-title 行。0.5.0 之前的行 id 是 tui 与 session-title-llm-tui,激活时自动迁移一次。
- /settings 分为“常规 / 模型与供应商 / 高级 / 微信claw”四页。开关、工具输出行数、并行数和默认思考等级在当前行回车即可切换;模型与供应商页集中默认模型、思考等级和供应商列表,Enter 循环思考等级。供应商表单中接口类型就地循环官方值:OAI 兼容 openai-completions、Response openai-responses、Message anthropic-messages;探测上游会直接合并进模型列表。
- 默认模式读取 dsh settings 中 agent-presets 行的 selectedDefault(用户选择的默认预设):设置页修改后,新会话启动会使用该默认模式,而不是固定回退到 standard。dsh 0.2 的预设注册表把「部署默认」和「用户选择默认」分开,defaultId 即两者合并后的结果。
- 主题由渲染器原生 ThemeProvider 拥有:themeName 从内置 opencode 文档、custom 主题文件((DSH_HOME ?? ~/.dsh)/themes 与项目祖先 .deepseek/themes)或 system(由终端调色板生成)中选一个;themeMode: system 跟随终端明暗,dark / light 固定一种,若该主题不支持则拒绝切换。newDesigns 打开后额外提供 opencode2。
- 0.4.0 的 themeDark / themeLight / themeSelected / themeCustom / themeSchema 只作为迁移输入保留,不再写入新设置节;旧值不会被当成当前主题。
- /permission 的可用选项以当前部署的权限预设为准,自定义 preset 也会进入命令提示和补全。
开发 sh
pnpm install
pnpm run check # verify:boundary + verify:contract + verify:plugin-spec + verify:ipc-protocol + verify:license + typecheck + test(宿主,Node)
pnpm run prepare
pnpm run check:renderer # 渲染器 typecheck + bun test(需要 Bun 1.4.2;--conditions browser 决定 Solid 客户端构建)
pnpm run build:renderer # bun build --compile:7 平台单文件产物写入 renderer/dist
pnpm run generate:sums # 生成 SHA256SUMS.json(渲染器产物的 SHA-256 与字节数)
门禁说明(dsh-ecosystem-spec v0.15 / @dsh-std/adapter-dsh 动态链接):
| 命令 | 作用 |
| pnpm run verify:boundary | @dsh-std/adapter-dsh 只允许在 src/dsh-adapter/ 内 import;@deepseek-ai/ 只允许在 src/dsh-host/(宿主渲染接缝)内 import;@opentui/ 与 solid-js 只允许在 renderer/ 内 import,且 renderer/ 不得 import @deepseek-ai/ |
| pnpm run verify:contract | 校验 @dsh-std/{core,manifest} 与框架 major;DSH harness 版本由 std adapter 吸收 |
| pnpm run verify:plugin-spec | 用官方 @dsh-std/manifest 校验 dsh-plugin.json 并构建 Host Descriptor |
| pnpm run verify:ipc-protocol | 宿主 ↔ 渲染器协议:kind 清单无重复、可选子集在全集内、每个 kind 的 payload interface 已声明、fixtures 每行是合法信封且方向正确 |
| pnpm run verify:license | NOTICE.md 登记表许可文本齐全、derived 路径存在、vendored 摘要一致、vendor/* 全量登记、声明派生的文件带 SPDX、随包分发 LICENSE + NOTICE.md |
| pnpm run check:renderer | 渲染器的 tsc --noEmit 与 bun test(--conditions browser --preload @opentui/solid/preload);CI 的 renderer job 与 release job 都跑这一组 |
常用辅助命令: sh
node --experimental-transform-types scripts/perf-probe.ts
测试使用 Node 原生 node:test 运行 .ts 文件,不依赖兄弟 harness checkout。修改源码后重新运行 pnpm run prepare,再使用 link: profile 验证: sh
npx --yes @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile tui add link:.
社区 std adapter(@dsh-std/adapter-dsh)由 profile 动态链接,不打进本包。dsh 0.2.0-rc.2 上终端 profile 无法激活它:adapter 自带行注入 sessionController,该服务由 @deepseek-ai/dsh-api-session-controller 提供,而后者又要求 fileUploads(依赖 connection)与 workspaceRegistry —— 属于 web profile 的客户端栈。装上后 dsh 每次启动都会打印一行 pending (waiting for service: sessionController) 的未激活警告,所以本 profile 不再安装它,dpsk update 也不再自动安装。
需要手工尝试时(仅在你自己补上 web 客户端栈的 profile 里才有意义): sh
dsh plugin --profile tui add @dsh-std/adapter-dsh@0.1.1-rc.4
adapter 缺失时本 TUI 的 my-dpsk-harness-tui/std-adapter / std-profile-loader 行降级为 no-op,TUI 正常启动,只是不会自动装载 std 插件。这两个子路径仍然导出,供手工组合 profile 使用。
[!NOTE]
dsh 仍处于 rc 阶段。上游接口变更时,先更新 docs/contracts.md,再同步代码、依赖版本,并运行测试与 dsh --profile tui 实机 smoke。
更多开发约定见 CONTRIBUTING.md。
项目结构 text src/ TUI、主题、提示、会话和设置实现;lazy-session-persistence / session-activity / session-prune 负责生命周期与排序 src/ipc/ 宿主 ↔ 渲染器协议 v2(信封、kind 清单、协商、通道) src/host/ grants、effect ledger、plugin storage、message observer、plugin host、渲染器 mount / spawn / 日志改道 / transcript 归一 / token 主题文档 src/dsh-adapter/ 上游投影 + 社区 std adapter 动态链接(std-link / std-adapter / std-profile-loader) src/plugin-spec/ Community v0.15:@dsh-std/manifest 解析 + 本宿主 registry / negotiation src/extensions/ tuiDialogs / tuiStatus / tuiShortcuts / tuiRenderers / tuiSettingsSections / tuiScenes src/components/ 共享形状与解析(content / text / provider 草稿 / 模型清单扁行 / status 选择器) renderer/ OpenTUI 渲染器工作区包(Bun):src/main.tsx 入口、src/app.tsx shell + store、src/ui/ 对话框基座与补全、src/keymap/、src/theme/{core,native,provider.tsx}、build.ts 7 平台 bun build --compile vendor/ vendored 上游资产(逐文件 SHA-256 登记) tests/ node:test 行为测试 + tests/ipc/fixtures/ 协议 fixtures SHA256SUMS.json 渲染器产物清单(启动器下载时校验哈希) cordis.patch.yml 将本 bundle 组合进 dsh profile 的配置 scripts/dpsk dpsk TUI 启动器(调用系统 PATH 中的 dsh;renderer install 下载并校验产物) docs/ 安装、发布和 harness 合约文档 docs/assets/ README 使用的实机截图
架构边界保持简单:dsh harness 负责智能体、模型、工具、持久化与沙箱;本仓库负责终端呈现和输入。渲染层是独立的 OpenTUI 渲染器子进程(Bun 编译的单文件产物),宿主与渲染器只通过 IPC 协议 v2 通信,渲染器不 import 任何 @deepseek-ai/,也不直接触碰 dsh 服务——一切会话、模型、权限、设置、微信桥操作都以 kind 请求宿主执行。pi-tui 渲染基座已随 0.5.0 退出删除。
社区互操作走 dsh-ecosystem-spec v0.15 与 @dsh-std/adapter-dsh:adapter 动态链接、可独立升级;dsh-plugin.json 由官方 @dsh-std/manifest 解析;src/host/ 提供授权与台账等宿主平面;src/extensions/ 提供社区扩展接缝。已知偏差:tuiScenes 场景后端 fail-closed(渲染器没有 scene host,open 返回 false),详见 docs/contracts.md。
社区适配器迁移说明
本项目正在迁移并合并 DeepSeek Harness 社区发起的公开适配器支持,目标是逐步摆脱对单一 @deepseek-ai/ 版本的硬绑定,让 TUI 通过社区标准接口与插件生态互通。
目前迁移到社区公开的 @dsh-std/adapter-dsh 适配器与 dsh-ecosystem-spec v0.15 插件准入规范,并已落地以下能力:
- 插件清单:dsh-plugin.json 采用 Community v0.15 结构,由 @dsh-std/manifest 官方解析器校验。
- 协议协商:接入 @dsh-std/core 的 ProtocolCatalog 与 ManifestDefinitionCatalog,对 commands、storage、messages、presentation 等公共协议及 tui.dsh/ 私有协议进行统一解析与五态协商(compatible / compatible_degraded / waiting_authorization / rejected / unknown)。
- 宿主平面:提供 tuiPluginHost、tuiPluginStorage、tuiMessageObserver、tuiEffectLedger 与统一授权存储,覆盖插件准入、命令归属、私有存储、消息订阅、效果台账与权限撤销。
- 诊断与门禁:内置 /plugins、/doctor 诊断命令,并在 CI 中运行 verify:boundary、verify:contract、verify:plugin-spec、verify:ipc-protocol、verify:license 与渲染器 typecheck/test。
- 版本策略:所有 @deepseek-ai/ 宿主包改为 optional peer,dsh 版本差异尽量由动态链接的 @dsh-std/adapter-dsh 吸收,adapter 可在 profile 内独立升级,无需重建本插件。
该迁移仍处于实验阶段,接口与行为可能随上游规范调整。当前已知限制:@dsh-std/adapter-dsh@0.1.1-rc.4(peer 范围覆盖 0.2.0-rc.1–0.2.1,2026-10-02 发布)在终端配置档中无法激活——它自带行注入 sessionController,该服务由 @deepseek-ai/dsh-api-session-controller 提供,而后者要求 fileUploads(依赖 connection)与 workspaceRegistry,属于 web 配置档的客户端栈。因此本配置档不安装 adapter,dpsk update 也不再安装;TUI 正常启动,社区 std 插件不会自动加载。npm 的 latest 标签仍是 0.1.1-rc.2(peer 范围不覆盖 0.2)。
变更记录
完整版本历史见 CHANGELOG.md。当前开发版本:0.5.0(未发布;UI 对齐与验收通过前不打 tag)。
相关文档
| 文档 | 说明 |
| docs/INSTALL.md | 安装、升级、卸载、本地开发和常见问题 |
| docs/contracts.md | dsh harness 合约唯一真相源 |
| docs/PUBLISHING.md | GitHub release、tarball 和 CI 发布流程 |
| CHANGELOG.md | 版本变更记录 |
| CONTRIBUTING.md | 开发与 PR 约定 |
| SECURITY.md | 安全策略与漏洞上报 |
许可
BSD-3-Clause,版权所有 © 2026 mytianyi0712 和 my-dpsk-harness-tui contributors。允许自由分发、修改与商业使用,但需保留原项目署名;完整条款见 LICENSE。
本项目包含来自第三方上游的代码、数据与资产,全部登记在 NOTICE.md(含完整许可文本与逐文件 SHA-256 摘要),分发时必须与 LICENSE 一起随包提供:
| 上游 | 许可 | 在本项目中的内容 |
| can1357/oh-my-pi (OMP) | MIT | src/wechat/ 的微信桥迁移(主题数据已随渲染器原生主题退出删除) |
| Tencent/openclaw-weixin | MIT | src/wechat/core/ 的 iLink 协议客户端、扫码登录、发送与状态层 |
| T-Auto/dsh-ecosystem-spec | MIT | vendor/dsh-ecosystem-spec/ 的 registry / permissions / contracts / protocols / conformance fixtures,及 src/plugin-spec/ 的随包副本 |
| @earendil-works/pi-tui | MIT | 0.4.x 的宿主渲染基座;0.5.0 起渲染器改用 OpenTUI,本包不再 import 或打包该库(登记保留作历史归属说明) |
| turtle1999/turtle-ui | BSD-3-Clause | 0.4.x 应用于 @earendil-works/pi-tui 的 vendored patch;0.5.0 已随基座退出删除 |
node scripts/verify-license.ts(pnpm run verify:license)校验登记表与源码/资产的一致性:派生文件必须带 provenance 头与 SPDX 标识,vendored 资产摘要必须与登记表一致,未登记的派生文件会使门禁失败。
[!NOTE]
命名与背书边界
本项目与 opencode / Anomaly、OMP / oh-my-pi、Pi、DeepSeek、Tencent 之间不存在隶属、赞助或背书关系;提及这些名称仅用于指代来源。上游商标权利归各自所有者,本包标识(包名、bin 名、logo、徽章)不使用这些名称作为标识。BSD-3-Clause 第 3 条同样禁止使用本包版权方或贡献者名义背书衍生产品。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
mytianyi0712/my-dpsk-harness-tui
一个dsh的终端样式插件,灵感来自Oh My Pi
相关插件 · 市场 / 管理
插件精选列表awesome-dsh-plugin/awesome-dsh-plugin
DeepSeek Harness(dsh)插件精选列表
★ 22,025未汉化DSH
73.6
新词雷达zhuyansen/new-term-radar
new-term-radar新词雷达
★ 2,049已汉化MCP
72.8
插件生态导航0xsline/awesome-deepseek-harness
安装 贡献指南 DeepSeek Docs 公开插件目录 Issues
★ 1,188已汉化DSH
72.6
技能注册中心iflytek/skillhub
企业级开源智能体技能注册中心——发布、发现并管理组织内可复用的技能包。
★ 5,358已汉化DSH
71.9
插件市场目录imsai-sh/awesome-deepseek-harness-plugins
面向 DeepSeek Harnessdsh生态的社区插件目录,共收录 13938 个插件含 PR 收录与…
★ 350已汉化DSH
71.8
插件精选清单Anil-matcha/awesome-dsh-plugin
DeepSeek Harnessdsh精选指南——DeepSeek…
★ 1,013已汉化DSH
71.7
这是你的仓库?认领条目