DeepSeek Harness 插件

Jesse-njx/dsh-tmuxctl

Star 数 ★ 0 分类 开发与运行时 收录于 2026-08-14

掌控你的 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-tmuxctlDeepSeek 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 tmuxapt 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_capturetmux_runtmux_watch 完成都会追加一条持久的 tmux/capture 事件({ paneId, target, lineCount, truncated, preview, fullText })。客户端会话节点(@dsh-tmuxctl/bundle/client)把它渲染成默认折叠、可展开的卡片:折叠时显示头部 + 预览,展开时显示完整文本(带滚动上限)。回放纯净性成立 —— 预览来自持久载荷,绝不重新抓取;渲染器只读 node.data

默认安全

三条硬规则,在工具层强制,并在 pre-execute 瀑布里再次兜底:

  1. 没有显式命名 target,绝不碰不是我们创建的 pane。 每个作用于 pane 的工具都要求非空 target,并按 ^[%A-Za-z0-9_.:-]+$ 校验 —— 缺失或空白 target 是校验错误,不是猜测。(requireExplicitTarget: false 可选地退回到本插件最近创建的 pane —— 依然永远不会是外来 pane。)tmux_run/tmux_watch 未给 target 时创建新窗口(无会话时新建会话),绝不无名操作已有 pane 的内容。
  2. 破坏性操作必须显式标记 + 审批。 tmux_kill 要求 confirm: true(schema const、函数体内再次检查,并由单调 guard 强制 —— 之后的监听器无法撤销)。config.approval 列出的操作走审批通道 —— 装了 dsh-tool-approval 时弹出 approval/request没有则拒绝(fail closed)
  3. 绝不把自由文本当 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-panesend-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 上的 mock tmux,逐参数(base64,无损)记录 argv 并返回罐头 -F 输出;单元测试不需要安装 tmux
  • test/integration.test.ts —— 在私有 socket 上拉起真实 tmux new -d -s dshtest,跑 send/capture 往返、断言 list/layout 输出、端到端跑 tmux_run/tmux_watch,最后用 tmux_killconfirm: 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 中的 %paneIdcreatedByUs: 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

License

MIT

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →