qaz320621/dsh-likemobaxtearm-chumc ↗★ 0
dsh-likemobaxtearm-chumc
SSH 控制台:本机 ssh 跑在真实 PTY 中,用户与 AI 共用同一会话、双向可见、全程审计;含 Windows/ConPTY 适配
설치
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 覆盖,只写你要改的键即可,其余键取默认值: