dsh-ssh-workbench 1.6.4 → 1.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,83 @@
1
+ # 执行模型与设计取舍
2
+
3
+ 本文说明「远程执行如何确定目标」这件事的设计结论,以及为此放弃的方案和原因。实现分层见 [architecture.md](architecture.md);用户用法见 [quickstart.md](quickstart.md)。
4
+
5
+ ## 一句话结论
6
+
7
+ **每个远程工具调用自己带 `server`。** 不存在会话级「当前目标」,也不存在把原生工具透明改道到远端。
8
+
9
+ ## 为什么不要「当前目标」
10
+
11
+ 早期版本用一个会话级 active target 表示「现在操作哪台机器」,`@资源名` 会改写它。它在三个地方同时承担职责:
12
+
13
+ 1. **执行绑定** —— 原生 `bash` 被路由到 active target;
14
+ 2. **上下文状态** —— 每轮把 active target 写进提示;
15
+ 3. **UI 状态** —— composer 下方显示当前目标。
16
+
17
+ 三者耦合带来一组必然问题:
18
+
19
+ - 多目标任务只能逐个 `switch → 执行 → switch`,模型把时间花在切换上,人也看不出「这台到底跑了什么」;
20
+ - 多个 `@` 时 active 被清空,模型既不知道该跑哪台,也拿不到资源清单;
21
+ - 切回本地是状态变更,容易残留成「以为在本机、其实在远端」;
22
+ - 目标会漂移,事后无法从对话里复现「这条命令跑在哪台」。
23
+
24
+ 所以把它整体去掉。现在:
25
+
26
+ | 场景 | 做法 |
27
+ |---|---|
28
+ | 单个目标 | `remote_bash({ server, command })` |
29
+ | 多个目标并行 | `remote_exec({ tasks: [{ server, command }] })` |
30
+ | 本机操作 | 原生 `bash` / `pwsh` / `read` / `write` / … |
31
+ | 远程文件 | `remote_read` / `remote_glob` / `remote_grep`(写需打开「允许远程写入」) |
32
+
33
+ **每个调用的目标都写在参数里**,因此并行安全、可审计、可复现,也不存在「状态残留」。执行工具在两种宿主上都注册,避免宿主自带 shell 平台差异(Windows 宿主没有原生 `bash`)影响可用性。
34
+
35
+ ## `@资源名` 是什么
36
+
37
+ **提示,不是绑定。** 用户 `@web-1` 时:
38
+
39
+ - `mentions.js` 解析出候选名字,只作为本会话的提示存储;
40
+ - `managed-context.js` 每轮把它连同资源清单写进上下文;
41
+ - 模型据此在工具参数里填 `server`。
42
+
43
+ `@` 不改变任何执行状态,所以后续无 `@` 的追问不会把命令静默带到别的机器;用户仍可随时 `@` 另一台或直接说「在本机看」。
44
+
45
+ ## 上下文注入
46
+
47
+ 每轮都注入一份紧凑资源清单(名称 / 方言 / 状态),而不是只在「有 active target」或「多个候选」时才注入。理由是:没有任何提示时,模型只能去读 `~/.dsh/ssh-workbench/servers.json` 猜配置——既慢,又把凭据目录暴露给模型。上下文里同时明确:
48
+
49
+ - 远程操作只能用 `remote_*` 工具,且必须显式写 `server`;
50
+ - 原生 shell / 文件工具只在本机生效;
51
+ - 目标不明确时按清单选择,或在有歧义时先问。
52
+
53
+ ## 执行账本与 pill
54
+
55
+ composer 下方的 pill 是**只读投影**,不是控制器:
56
+
57
+ - 数据来自 `activity.js`(每次远程调用的 `server` / `sessionId` / `durationMs` / `outcome`),由 `execution-stats.js` 按 Session 聚合;
58
+ - 进行中的调用由 `live-executions.js` 在内存里记录,仅用于显示「正在连哪台」,不参与执行;
59
+ - 视觉对齐 DSH 内置的 Token 用量弹窗:标题 + 总次数、分隔线、目标名 → 次数;
60
+ - 不显示成功/失败、用时或模型指标(这些是 DSH 会话的私有数据,插件不伪造),不承担切换目标或打开工作台。
61
+
62
+ ## 状态类批量查询的快路径
63
+
64
+ 「看看几台机器状态」这类请求最大的延迟来源不是 SSH 跑命令,而是**对不可达目标的探测**:方言探测(每次最多一个 `connectTimeoutMs`)+ `timeoutGuard` 能力探测(再一次)+ 真正建连(再一次)。
65
+
66
+ 因此 `remote_exec` 提供两个参数:
67
+
68
+ - `probe:false` —— 跳过方言与 `timeoutGuard` 探测,直接用 `facts.dialect` / 记录平台判断;
69
+ - `connectTimeoutMs` —— 按调用限制建连时间,一路透传到 broker 的 `readyTimeout`。
70
+
71
+ 组合后 6 台目标里 3 台不可达时,整体从接近一分钟降到秒级;不可达任务只让自己失败,不阻塞其他任务。
72
+
73
+ ## 有意的非目标
74
+
75
+ | 不做 | 原因 |
76
+ |---|---|
77
+ | 把 `read` / `write` / `edit` 透明改道到远端 | 会让「改文件」的语义取决于隐式状态,比多做几个工具危险得多;需要远程文件就用显式的 `remote_*` |
78
+ | SSHFS / 远程 IDE 式挂载 | 需要额外守护进程与权限模型,和「零安装依赖」的发布约束冲突 |
79
+ | 命令分级(allow/guarded)与风险解释 | 白名单可被 shell 语法绕过,容易制造错误的安全感;真正的边界是 SSH 权限、host-key 与 DSH 审批 |
80
+ | 插件自有审批清单 | 审批策略属于 DSH 会话预设,插件重复实现会互相矛盾 |
81
+ | 伪造模型指标(TTFT / TPS / 轮次) | 这些数据只存在于 DSH 宿主,插件无法准确获得 |
82
+
83
+ 黑名单 guard 保留为兜底:同步、单调、按每个显式目标的方言逐条检查(`remote_exec` 的每个 task 都过),但它**不是权限边界**——见 [SECURITY.md](../SECURITY.md)。
Binary file
Binary file
Binary file
Binary file
@@ -10,7 +10,9 @@ npm test
10
10
  npm run pack
11
11
  ```
12
12
 
13
- 然后安装 `dist/dsh-ssh-workbench-1.6.4.tgz`。安装后重启/重载插件。
13
+ 然后安装 `dist/dsh-ssh-workbench-1.7.8.tgz`。安装后重启/重载插件。
14
+
15
+ 插件管理 → 远程工作台里可以打开「系统文件浏览使用层级模式」:右侧栏「工作区文件」会换成插件的层级浏览器,范围仅限当前会话目录;默认关闭。
14
16
 
15
17
  发布包自带纯 JS 的 SSH 依赖,安装过程不需要编译(不需要 Xcode Command Line Tools)、也不额外下载 npm 包,离线机器同样可以安装。
16
18
 
@@ -32,7 +34,7 @@ npm run pack
32
34
  @web-1 把 /etc/nginx/nginx.conf 的 worker_connections 改成 2048,然后 nginx -t
33
35
  ```
34
36
 
35
- 模型会调用原生 `bash`(Windows 宿主上是 `remote_bash`),命令实际在 web-1 上执行;POSIX 宿主的结果卡片与本地 bash 一致(退出码、超时、截断)。
37
+ 模型会调用 `remote_bash({ server: "web-1", command })`(Windows 目标是 `remote_pwsh`),命令实际在 web-1 上执行;`@` 只是告诉模型这一轮的目标,不产生全局状态。原生 `bash` / `pwsh` 始终在本机运行,所以想查本机时不需要“切回来”。
36
38
 
37
39
  ## 4. 多目标
38
40
 
@@ -40,16 +42,26 @@ npm run pack
40
42
  @web-1 @db-1 各自看下磁盘
41
43
  ```
42
44
 
43
- 模型会先调用 `ssh_workbench_target({ server: "web-1" })` 再执行,然后切换到 `db-1`。可以随时用 `ssh_workbench_target({ server: "local" })` 回到本机。
45
+ 模型会优先用 `remote_exec` 一次并行执行:
46
+
47
+ ```
48
+ remote_exec({ tasks: [
49
+ { server: "web-1", command: "df -h" },
50
+ { server: "db-1", command: "df -h" }
51
+ ] })
52
+ ```
53
+
54
+ 每个 task 自己指定目标,命令在各自服务器上并发执行,结果按任务顺序返回;没有会话级 active target,所以不存在“切目标”状态,也不会把后续本地命令误发到远程。资源名支持中文/非 ASCII(如 `@测试机`)。
44
55
 
45
- 注意:一次提到多个资源后,当前目标会先被清空;在模型调用 `ssh_workbench_target` 选择之前,直接调用远程 shell 工具(POSIX 宿主上的原生 `bash` 或 `remote_bash`)会返回 `TARGET_NOT_SELECTED` 错误(不会静默在本机执行)。资源名支持中文/非 ASCII(如 `@测试机`)。
56
+ composer 下方的 pill 只读展示本会话各目标的远程执行次数,结构对齐 DSH 的 Token 用量卡(标题 + 总数、分隔线、目标名 → 次数);它不切换目标、不打开工作台。终端/文件从左侧「远程工作台」进入。
46
57
 
47
58
  ## 5. 远程工作台
48
59
 
49
60
  **左侧导航 → 远程工作台** 是一个独立页面:左边终端,右边文件管理,中间分隔条可拖动。
50
61
 
51
62
  - 终端:点「+」新建 **本地终端** 或选择一台远程资源开 **远程 SSH 终端**;每个页签一个会话,关闭页签即结束会话。只有活动页签在后台读取输出,切回其他页签时会从上次位置补齐,不会丢输出。
52
- - 文件:跟着当前终端页签走 —— 本地终端时显示 **本地用户主目录文件**(默认 `$HOME`,可用插件配置 `localRoot` 指定其他绝对目录),远程终端时显示该资源的 **SFTP 文件**(远程默认打开登录用户的 home)。两类文件都支持浏览/**在工作台内直接单击预览**/下载/上传/重命名/删除/新建文件夹,远程文件还有传输记录与续传。
63
+ - 文件:跟着当前终端页签走 —— 本地终端时显示 **本地用户主目录文件**(默认 `$HOME`,可用插件配置 `localRoot` 指定其他绝对目录),远程终端时显示该资源的 **SFTP 文件**(远程默认打开登录用户的 home)。两类文件都支持浏览/**在工作台内直接单击预览**/下载/上传/重命名/删除/新建文件夹,远程文件还有传输记录与续传。文件行的操作菜单走 DSH 官方右键菜单:**右键**、`Shift+F10` 或行尾省略号按钮都能打开同一套菜单(打开、下载、复制文件、复制路径、重命名、删除)。
64
+ - 预览范围:Markdown(可切源码)、代码与配置(行号/换行/复制)、CSV/TSV(表格,可切源码)、图片(含 SVG,SVG 可切源码)、其他纯文本;文本读取上限 1 MB、图片 5 MB,超出时头部显示「已截断」并在底部标「前 N 行」。PDF、Office、压缩包等二进制走下载后本地打开。
53
65
  - 本地文件范围限定在 `localRoot`(默认用户主目录)及其子目录,面包屑显示真实路径;越界路径与指向外部的符号链接会被拒绝,需要操作根目录以外的本地路径时用本地终端。
54
66
  - 页面刷新/关闭会尽力关闭终端会话;进程异常退出时服务端会话会在空闲 30 分钟后回收。
55
67
 
@@ -57,8 +69,8 @@ npm run pack
57
69
 
58
70
  ## 6. 常见问题
59
71
 
60
- **为什么 `read` / `write` / `edit` 读不到远程文件?**
61
- 这些文件工具始终作用于 DSH 主机(POSIX 宿主上的原生 `bash` 或远程 shell 工具都不会改变这一点),误用 `write` 会写到本机。远程文件用对应的远程 shell 工具操作(POSIX 目标:`cat`、`tee`、`sed`;Windows 目标:`Get-Content`、`Set-Content`),或用右侧栏 SFTP 面板。
72
+ **远程文件怎么读写?**
73
+ 原生 `read` / `write` / `edit` / `glob` / `grep` 始终作用于 DSH 主机;远程文件请让模型使用显式的远程文件工具:`remote_read`、`remote_glob`、`remote_grep`。写入默认关闭:在「插件 → 远程工作台 → 允许远程写入」打开开关(或设置 `remoteFileWrite: true`)后才可使用 `remote_write` / `remote_edit`;后者默认要求 oldText 唯一匹配。大文件、二进制和上传/下载仍建议用右侧栏 SFTP 面板。POSIX 目标的写入是「同目录临时文件 + rename」原子替换,并尽量保留原权限位;Windows 目标仍为直接写入;软链目标写穿链接。修改关键配置前仍建议备份。
62
74
 
63
75
  **命令很慢/超时?**
64
76
  远程命令默认 120s,可用远程 shell 工具的 `timeoutMs` 参数(原生 `bash` / `remote_bash` / `remote_pwsh`)或插件配置 `commandTimeoutMs` 调整。连接池会复用连接,第一次会稍慢。超时后如果远端有 `timeout`,整个远端进程组会被终止(TERM 后按 `timeoutKillAfterMs` 再补 KILL);没有 `timeout` 的服务器(如 macOS)只能停止等待,命令可能还在跑,结果 stderr 会提示这一点。**取消(Esc)不会立即杀远端**,远端仍会计时到自己的 deadline。
@@ -67,10 +79,13 @@ npm run pack
67
79
  远程命令的 stdin 在启动后立即关闭(等价 `</dev/null`),读 stdin 的命令会立刻得到 EOF,不会挂到超时。
68
80
 
69
81
  **怎么确认命令跑在哪台机器?**
70
- 目标切换后的第一条远程命令会带有目标提示(原生 bash 卡片显示 stderr 行与 cwd 前缀,如 `[remote] target: web-1 (...)`、`web-1:/var/log`;远程 shell 工具在结果文本里带 target/cwd);composer 下方参数行常驻显示当前目标。也可以点 `ssh_workbench_target` 选 `local` 回到本机。
82
+ 每个远程工具结果都带 server / target / cwd(如 `web-1:/var/log`),工具卡片也会显示执行的资源;composer 下方的 pill 只统计本会话执行次数。原生 `bash` / `pwsh` 的结果永远来自本机。
83
+
84
+ **能不能一次让多个资源并行执行?**
85
+ 可以,让模型调用 `remote_exec`:每个 task 显式带 `server`,默认并发 4、最多 8,单次最多 8 个 task;每个任务独立超时、独立输出、互不污染,结果按输入顺序返回。每个 task 也可单独设置 `run_in_background`,需要长任务时不必切回后台工具。`remote_bash` / `remote_pwsh` 也各自显式带 `server`,不存在需要先切换的单 active target。
71
86
 
72
87
  **怎么跑长时间任务 / 后台服务?**
73
- 让模型带 `run_in_background: true` 调用远程 shell 工具(`remote_bash` / `remote_pwsh`,POSIX 宿主上的原生 `bash` 路由同样支持)。工具会立刻返回远端 pid 和日志路径:
88
+ 让模型带 `run_in_background: true` 调用远程 shell 工具(`remote_bash` / `remote_pwsh`),或在 `remote_exec` 的某个 task 上设置它。工具会立刻返回远端 pid 和日志路径:
74
89
  - POSIX:`tail -f <log>` 看输出,`kill <pid>` 停止;
75
90
  - Windows:`Get-Content -LiteralPath '<log>' -Wait` 看输出,`Stop-Process -Id <pid> -Force` 停止。
76
91
  后台进程与本次工具调用脱离,调用返回后继续运行;`timeoutMs` 只约束启动调用本身。它不是 DSH 本地 job,`job_*` 工具看不到它。