qaz320621/dsh-likemobaxtearm-chumc ↗★ 0

dsh-likemobaxtearm-chumc

SSH 控制台:本机 ssh 跑在真实 PTY 中,用户与 AI 共用同一会话、双向可见、全程审计;含 Windows/ConPTY 适配

包名
dsh-likemobaxtearm-chumc
兼容性
待验证
版本
1.0.0
许可证
MIT
最近更新
2026年10月4日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:qaz320621/dsh-likemobaxtearm-chumc

使用

你(操作者)

  • 终端:直接打字;Ctrl+C、全屏程序(vim/top)都正常。
  • 工具栏:
    • 权限三档:仅可看 / 问答 / 完全(对 AI 生效)
    • 收回控制:AI 正在跑命令时,一键打断它(真 Ctrl+C)并拿回输入权
    • 重命名:改标签名
  • 底部四个抽屉:
    • 日志:当前会话的日志,按 user / ai / system / remote 过滤,可刷新
    • 历史:全部日志文件(含 archive/ 里的归档),点「查看」只读打开(默认末尾 300 行)
    • 留言:给 AI 留言,三种类型 —— 提问、报错(自动附最近 20 行终端输出)、仅记录(只写日志,不消耗 AI token)
    • 待确认:AI 请求执行的命令;危险命令的告知窗口也在这里(可取消)
  • 断线重连:Harness 重启后标签会显示"会话已断开",按 r 或点「重新连接」——在原标签内重连,不会多开标签。 连接信息(target/user/port/name)存在浏览器里,所以重启后不用重打。
  • 最近连接:管理器里列出连过的目标,一键连接;新标签页里也会给出这个列表。
  • 会话切换由插件自己管:宿主标签条是 overflow: hidden 且不可改,所以会话不放在那里 —— 页面顶部的二级标签条是插件自己渲染的,能滚动:
    • 第一项 「控制台」是常驻页:主机管理、新建连接、日志目录都在这里,永远在、不可关闭;会话排在它后面;
    • 鼠标滚轮 → 横向滚动;◀ ▶ → 左右微调;▾ → 列出全部页面(滚动条之外也能直达);
    • ▾ 下拉点一下展开,点面板外部或按 Esc 自动收起(再点 ▾ 也能收起);
    • 每项 ✕ → 关闭该会话;Ctrl+Alt+← / Ctrl+Alt+→ → 依次切换;
    • 圆点配色:🟢 就绪、🟠 连接中 / 已退出、⚪ 已关闭或未知;红色只用于危险命令与危险操作按钮 (常驻的「控制台」页不带圆点 —— 它没有会话状态,不该显示任何告警色);
    • 关掉「SSH 控制台」这个宿主标签不会杀掉会话(AI 仍可继续操作),重新打开就能看到它们;
    • 旧版本留下的每会话标签:打开时会自动把会话并入控制台并自行关闭(没关掉就手动 ✕)。

AI(模型侧)

工具作用
ssh_list_hosts列出保存的主机(不含口令)
ssh_list_sessions当前 DSH 会话里的 SSH 会话(状态/权限/输入权/日志路径)
ssh_connect新建会话;会自动在右侧栏开一个可见标签
ssh_exec执行一条命令并等结束,返回输出 + 退出码(受三级权限约束)
ssh_send写入原始文本(可选回车),用于交互式程序、发 \u0003
ssh_read按行分页读取终端输出(去控制序列)
ssh_status单个会话的状态快照 + 最近输出
ssh_log读取 JSONL 审计日志(按 actor/kind 过滤)
ssh_disconnect关闭会话并回收进程树

权限语义:

  • 仅可看 → 写入类工具直接拒绝(SSH_PERMISSION_VIEW_ONLY),并要求 AI 请你切换权限,不会反复重试
  • 问答 → 命令进入待确认队列,你点「允许」才执行;超时未确认返回 SSH_APPROVAL_TIMEOUT
  • 完全 → 直接执行;命中危险规则时先给出可取消的告知窗口(dangerGraceMs,默认 5s)

其它约定:你按键收回输入权时,AI 的调用以 waitReason=revoked 结束(这是正常打断,不是错误); 远端在等凭据时 AI 的写入被拒绝(SSH_AUTH_REQUIRED,AI 不得代填口令)。

ssh_exec 的退出码用哨兵标记解析,属于启发式:命令自己后台化、或把 shell 换掉时可能拿不到真实退出码; 且哨兵依赖 POSIX printf,所以远端需要是 POSIX shell(远端是 cmd/PowerShell 时请用 ssh_send + ssh_read)。

日志字段

ai.command / user.input 的 command 全量保留;output / ai.result 的 text 超过 outputLogTruncateChars(默认 200)会被截断,并带 truncated / fullBytes / lines。 口令提示后输入的行记 text:"[redacted]" + sensitive:true。

日志按一次交互合并,规则是"看换行,不只看时间":终端每敲一个键就重画整行,纯按时间合并会把 ls 和 -a 切成两条,所以 ——

  • 缓冲区已有换行(命令提交了 / 输出成行了)→ 安静 outputFlushQuietMs(默认 400ms)就落一条;
  • 缓冲区还没有换行且你最近按过键(正在编辑这一行,方向键、翻历史、补全都算)→ 完全不落盘, 直到你回车。纯时间窗口治不了这件事:你停 10 秒也只是一次停顿,不代表"这行打完了";
  • 既没有换行、也没人在打字(裸提示符、进度条之类)→ 等 outputIdleFlushMs(默认 5000ms)再落;
  • 回车时先把这一行的回显落成一条,命令结果另成一条(结果因此有独立的 200 字符额度);
  • 还有:超过 outputFlushMaxBytes 立即落一条,会话结束/关闭时也会冲刷。

实时流不受影响:浏览器始终逐字节收到输出,被合并的只有落盘。

日志的生命周期

  • 一个 SSH 会话一个文件:logs/-_.jsonl,会话结束即定稿 (所以同一台主机的多次连接是多个文件,重连会产生新文件);
  • 自动归档:超过 logArchiveAfterDays(默认 7 天)的日志被 gzip 成同目录 archive/.jsonl.gz —— 内容一字不差,体积约 1/10;正在写日志的活跃会话永不被移动;
  • 阈值可以精确到小时:logArchiveAfterDays: 0.5(小数天 = 12 小时)或直接写时长 logArchiveAfterDays: '12h' / '90m' / '30s'。归档扫描在启动后 5 秒、每次打开会话、 以及每 10 分钟各跑一次,所以小时级阈值不需要有人操作也会生效;
  • 0 是"关闭"而不是"立即":logArchiveAfterDays: 0 表示不归档(写错成 0 不会把日志一次性全压掉); 想激进归档就写一个真实时长(如 '30s');
  • 保留期:只有把 logRetentionDays 设为大于 0(默认 0 = 永久保留),超过该时长的归档才会被删除; 当前日志永远不会被自动删除;
  • 界面里能看历史:控制台页底部的「历史日志」列出全部文件(含归档,标注日期/会话/主机/大小/是否归档), 点「查看」只读打开,默认取末尾 300 行;解压后超过读取上限的归档会明确拒绝并报出实际大小,而不是卡死;
  • 文件名经过校验(../ 之类直接拒绝),插件只读自己的日志目录。

配置

配置写在 profile 的 patch 文件里:~/.dsh/profiles/ /cordis.patch.yml(Harness 的插件配置统一在这里, 宿主没有图形化的插件配置页)。按行 id 覆盖,只写你要改的键即可,其余键取默认值: