掌控你的 tmux 面板:list/send-keys/capture、在面板中运行长任务并 watch,破坏性命令需审批。
安装
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:Jesse-njx/dsh-tmuxctl
GitHub 来源的插件在安装时会在你的机器上执行构建脚本。请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
tmux 的控制面 —— 让智能体驱动你已经打开的终端,而不只是「认识」它们。
dsh-tmuxctl 是 DeepSeek Harness 的插件 bundle。DSH 内核自带的 dsh-tmux-context 是被动插件,只标注 harness 进程住在哪个 pane;tmuxctl 是主动的一半 —— 把你现有的 tmux 网格(服务器、日志 tail、REPL、构建 watcher)变成智能体可以列举、输入、抓取、重排并监看的可寻址表面,并且默认安全。因为每个进程都由 tmux 持有,被监看的构建在 harness 重启后依然存活:pane 还在,智能体按 %paneId 重新挂上去即可。
纯 CLI 包装 tmux 二进制 —— 没有 daemon,也没有我们自己常驻的进程。
安装
# 安装到你对话/智能体所在的 profile(web,或你的 headless profile):
dsh plugin add github:Jesse-njx/dsh-tmuxctl
# 或者,发布到 npm 之后:
dsh plugin add @dsh-tmuxctl/bundle
一条命令同时安装两半:
- 宿主一半 —— 八个
tmux_*工具、破坏性操作的审批门、tmux/capture快照事件; - 客户端一半 ——
tmux-capture会话节点,把每次快照渲染成 Web 聊天里默认折叠、可展开的卡片,日志尾巴不再淹没对话记录。
机器上需要安装 tmux 且在 PATH 中(brew install tmux、apt install tmux 等)。
工具一览
| 工具 | 作用 | 规范输出 |
|---|---|---|
tmux_list |
会话、窗口(含 layout)、pane(target、%paneId、尺寸、当前命令、active、是否本插件创建) |
{ sessions, windows, panes } |
tmux_send_keys |
向 pane 逐字输入文本(send-keys -l --),可选按 Enter |
{ target, sent, enter } |
tmux_capture |
抓取 pane 可见文本,可选 -S -<lines> 带上回滚 |
{ target, text, lineCount, truncated } |
tmux_split |
在目标旁分屏,新 pane 标记为 created-by-us | { paneId, target } |
tmux_swap |
结构性交换两个 pane —— 不发按键 | { src, dst } |
tmux_run |
在新 pane 运行命令,等待输出稳定(两次轮询相同)或超时 | { paneId, text, stabilized, elapsedMs } |
tmux_watch |
在新 pane 后台运行命令;shell 返回 / doneRegex 命中 / 超时即完成;结果注入为持久上下文 |
{ kind: 'background', jobId } |
tmux_kill |
杀 server/session/window/pane —— 必须 confirm: true,并按配置走审批 |
{ scope, target?, killed } |
所有输出都是结构化 JSON,Code Mode 有真正的 API(await tools.tmux_list(...));人类可读的说明留在各工具的 render 里。
监听模式 ——「跑这个,好了告诉我」
tmux_run 阻塞当前 turn;tmux_watch 不阻塞:它通过 ctx.jobs 注册后台任务,每 panePollMs 轮询 capture-pane,在以下任一时刻完成:
- pane 前台命令回到 shell(
#{pane_current_command}变回bash/zsh/$SHELL/…),或 doneRegex命中抓取到的输出,或- 超时(
timeoutMin,默认watchTimeoutMin)—— pane 保持存活。
完成后,输出尾部作为持久上下文注入(agent.inject,form notice),下一次模型请求可见 —— inject 不是唤醒;同时发出 tmux/capture 快照卡片。job_kill 或取消任务会停止轮询,但绝不杀 pane:pane 归 tmux 所有。若 pane 中途消失,任务以 { status: 'failed', detail: 'pane-gone' } 结束。
快照卡片
每次 tmux_capture、tmux_run、tmux_watch 完成都会追加一条持久的 tmux/capture 事件({ paneId, target, lineCount, truncated, preview, fullText })。客户端会话节点(@dsh-tmuxctl/bundle/client)把它渲染成默认折叠、可展开的卡片:折叠时显示头部 + 预览,展开时显示完整文本(带滚动上限)。回放纯净性成立 —— 预览来自持久载荷,绝不重新抓取;渲染器只读 node.data。
默认安全
三条硬规则,在工具层强制,并在 pre-execute 瀑布里再次兜底:
- 没有显式命名 target,绝不碰不是我们创建的 pane。 每个作用于 pane 的工具都要求非空 target,并按
^[%A-Za-z0-9_.:-]+$校验 —— 缺失或空白 target 是校验错误,不是猜测。(requireExplicitTarget: false可选地退回到本插件最近创建的 pane —— 依然永远不会是外来 pane。)tmux_run/tmux_watch未给 target 时创建新窗口(无会话时新建会话),绝不无名操作已有 pane 的内容。 - 破坏性操作必须显式标记 + 审批。
tmux_kill要求confirm: true(schemaconst、函数体内再次检查,并由单调 guard 强制 —— 之后的监听器无法撤销)。config.approval列出的操作走审批通道 —— 装了dsh-tool-approval时弹出approval/request;没有则拒绝(fail closed)。 - 绝不把自由文本当 tmux 命令发。
send-keys始终用-l -- <text>:模型文本永远不会被重解释成 tmux 键名(C-c)或命令。控制键是独立的显式参数(enter: true),绝不夹带在文本里。
配置
plugins:
dsh-tmuxctl:
socket: default # tmux -L socket 名,或 "default"
requireExplicitTarget: true # 未命名 target 时绝不操作非本插件创建的 pane
approval: [kill-server, kill-session, kill-window, "send-keys:*"] # 走审批的操作
panePollMs: 2000 # run/watch 轮询间隔
watchTimeoutMin: 120 # tmux_watch 默认超时(分钟)
runTimeoutMs: 120000 # tmux_run 默认超时
stabilizeMs: 2000 # tmux_run 默认静默期
captureMaxBytes: 100000 # 抓取字节预算(超出标记 truncated)
shells: [bash, zsh, fish, sh, dash, ksh, tcsh, csh, nu, elvish, xonsh, pwsh, powershell]
crosstalkPeer: "" # 可选:watch 完成时把尾部发给这个 dsh-crosstalk 对端
approval 项是 op glob:kill-server / kill-session / kill-window / kill-pane 和 send-keys:<target>。"send-keys:*" 让每次按键发送都走审批(锁定部署用;默认只对 kill 设卡)。
与生态组合
- dsh-routines / jobs —— 定时 routine 可以调用
tmux_run/tmux_watch在常驻 pane 里跑 cron 任务(夜间构建、抓取尾部)。除共享的ctx.jobs接口外无耦合。 - dsh-crosstalk —— 设置
crosstalkPeer: <session>后,watch 完成时会把捕获尾部send_message给该对端(「repo-A 构建完成」)。运行时检测;缺席则只做本地注入。 - dsh-tool-approval —— 安装后,
config.approval里的 kill 会在你的 UI 弹出approval/request;不装则这些操作被拒绝(fail closed)。
开发
pnpm install
pnpm typecheck
pnpm build # 宿主(tsc)+ 客户端 bundle(esbuild)→ lib/
pnpm test # node --test;mock-tmux 单元测试始终运行,
# 真实 tmux 集成测试做特性检测(缺席则跳过)
test/fixtures/mock-tmux.sh—— PATH 上的 mocktmux,逐参数(base64,无损)记录 argv 并返回罐头-F输出;单元测试不需要安装 tmux。test/integration.test.ts—— 在私有 socket 上拉起真实tmux new -d -s dshtest,跑 send/capture 往返、断言 list/layout 输出、端到端跑tmux_run/tmux_watch,最后用tmux_kill(confirm: true)清理。- 客户端 bundle(
lib/client.js)由scripts/build-client.mjs构建,以包 id 通过window.__ModuleLoader__.load(...)注册,harness 直接把它作为/plugins/@dsh-tmuxctl/bundle/client.js提供 —— 安装时零构建步骤。
已知限制(v0.1)
- watch 轮询器是进程内的,和所有
ctx.jobs生产者一样。pane 及其进程在 harness 重启后仍然存活(归 tmux 所有),模型可按tmux_list中的%paneId(createdByUs: true)重新挂载,但轮询循环本身不跨重启。 - shell 返回检测器认识一组固定 shell 名外加
$SHELL;不在config.shells里的非常规登录 shell 需要doneRegex(或加配置项)。 - 抓取受
captureMaxBytes限制;超长回滚按字节预算截断(标记truncated)。 - target 限制为
^[%A-Za-z0-9_.:-]+$—— 含空格或非常规字符的会话名不可寻址(这是刻意的安全取舍)。 - SSH 远程 tmux、插件管理器集成、GUI 分屏编辑器不在 v0.1 范围。
非目标(v0.1)
tmux 服务器管理 UI;插件管理器集成;SSH 远程 tmux;GUI 分屏布局编辑器。没有 daemon,也没有我们自己的持久化状态 —— tmux 是 pane 的唯一事实来源。
Promo
promo/slideshow.html—— 60 秒键盘翻页宣传片(1280×720),配promo/narration.txt。
License
MIT
链接
同类插件
hust-open-atom-club/oh-dsh★ 161
社区发行版:TUI、桌面端与 Web UI 统一体验,分层安装、一步到位。
Jayden-X-L/forkprobe★ 65
同一任务并行试跑多个技能,对比结果选出最优。
vlln/plugin-registry★ 33
插件生态基建:浏览器面板管理官方 repository 插件(0 patch)+ make-dsh-plugin 插件开发引导技能。
forrestchang/dsh-multica-runtime★ 28
让 dsh 运行时跑在 Multica 上。
DietCokewithSugar/dsh-user-experience★ 18
帮你发现项目中可能存在的用户体验问题:自动走查 React/TypeScript 源码,定位问题并给出具体优化建议。
omdsh-dev/dsh-plugin-check★ 17
插件健康检查:扫描清单协议/patch 格式/构建陷阱,零依赖只读。