@rjfan/dsh-presence 0.18.0 → 0.19.0

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.
package/README.md CHANGED
@@ -52,12 +52,50 @@ connection per host**. The remote side is still plain `sshd`: SFTP, exec and a P
52
52
  - **Job notifications** — a toast (and desktop notification) when a background job finishes, plus an optional local `notifyCommand` for daemons and phones.
53
53
  - **Two shells, one kernel** — the DSH plugin and a standalone MCP server (`presence-mcp`, 20 core tools) share the same kernel.
54
54
 
55
+ ## Screenshots
56
+
57
+ <table>
58
+ <tr>
59
+ <td width="50%"><img src="docs/social/screenshot/setting.png" alt="Settings — Presence hosts and workspaces" /></td>
60
+ <td width="50%"><img src="docs/social/screenshot/explorer.png" alt="Remote workspace: file tree and editor" /></td>
61
+ </tr>
62
+ <tr>
63
+ <td align="center"><sub><b>Settings</b> — every host and folder is a local tab</sub></td>
64
+ <td align="center"><sub><b>Remote workspace</b> — the file tree and editor are on the host</sub></td>
65
+ </tr>
66
+ <tr>
67
+ <td width="50%"><img src="docs/social/screenshot/@file.png" alt="@ completes remote file paths" /></td>
68
+ <td width="50%"><img src="docs/social/screenshot/terminal.png" alt="A terminal on the remote host" /></td>
69
+ </tr>
70
+ <tr>
71
+ <td align="center"><sub><b>@ files</b> — completion indexes the remote machine itself</sub></td>
72
+ <td align="center"><sub><b>Terminal</b> — the shell runs on the host, not locally</sub></td>
73
+ </tr>
74
+ </table>
75
+
76
+ ### A real trajectory
77
+
78
+ These three figures are rendered from a recorded 22-call session: train an MNIST
79
+ classifier twice on a remote 64-core host, compare two learning rates, and export
80
+ the result. Every row is one tool call with its measured duration; red rows are
81
+ real failures kept verbatim.
82
+
83
+ <p align="center"><img src="docs/social/figures/figure-1.png" width="100%" alt="Trajectory 1/3: staging the data and launching the first run" /></p>
84
+
85
+ <p align="center"><img src="docs/social/figures/figure-2.png" width="100%" alt="Trajectory 2/3: comparing the two runs and archiving" /></p>
86
+
87
+ <p align="center"><img src="docs/social/figures/figure-3.png" width="100%" alt="Time composition: Presence vs bare SSH" /></p>
88
+
89
+ <sub>Figure 3 stacks end-to-end time into think / wait / tool. The same task through
90
+ bare <code>ssh</code>/<code>scp</code> takes 452.5 s versus 272.8 s, with a 10.4×
91
+ higher median interaction latency.</sub>
92
+
55
93
  ## Quick start
56
94
 
57
95
  ### 1. Install the DSH plugin
58
96
 
59
97
  ```sh
60
- dsh plugin add @rjfan/dsh-presence@0.18.0
98
+ dsh plugin add @rjfan/dsh-presence@0.19.0
61
99
  ```
62
100
 
63
101
  From a source checkout:
@@ -147,17 +185,19 @@ Measured on a Linux host:
147
185
  ## Tools
148
186
 
149
187
  The DSH plugin exposes 24 tools. Relative paths resolve against the active remote
150
- workspace; `host` is optional inside a workspace.
188
+ workspace; `host` is optional inside a workspace. Argument aliases are accepted at
189
+ execute time (`path`/`file_path`/`filePath`, `old`/`oldString`, `new`/`newString`,
190
+ `cwd`/`workdir`, …), so an agent's habitual spelling still works.
151
191
 
152
192
  <details>
153
193
  <summary><b>Show all tools</b></summary>
154
194
 
155
195
  | Tool | What it does |
156
196
  | --- | --- |
157
- | `presence_hosts` | List SSH hosts, connection state and default host (`format:"json"` supported). |
197
+ | `presence_hosts` | List SSH hosts, connection state and default host (`format:"json"` supported); `workspace:true` marks this session's machine and `degraded` rows carry the last error. |
158
198
  | `presence_connect` | Open/reuse a host connection and report the remote identity. |
159
- | `presence_exec` | Run a remote command; `pipefail`, `background`, `waitForPort`, structured JSON, `script`, `saveStdout`; destructive commands dry-run first. |
160
- | `presence_jobs` | Manage background jobs: `list`, `status`, `wait` (with `timedOut`), `stop` (with `alive`/`verified`/`portReleased`), `purge`. |
199
+ | `presence_exec` | Run a remote command; `pipefail`, `background`, `waitForPort`, structured JSON, `saveStdout`; timeout defaults to 300 s; `progress` normalizes `\r` progress bars; long output spills to a remote file whose path is returned (`tailLines`); destructive commands dry-run first — `confirm:true` runs the whole command, `confirm:"partial"` runs only the safe segments. |
200
+ | `presence_jobs` | Manage background jobs: `list`, `status`, `wait` (with `timedOut`), `stop` (with `alive`/`verified`/`portReleased`). |
161
201
  | `presence_read` | Read a remote file with line numbers, `offset`/`limit`, or `raw:true` for verbatim content. |
162
202
  | `presence_read_many` | Read 1–50 remote files in one call, with per-file byte/line caps. |
163
203
  | `presence_write` | Atomic write; preserves mode; returns real mode, size and full sha256; `ifMatchSha256` optimistic lock. |
@@ -182,13 +222,46 @@ workspace; `host` is optional inside a workspace.
182
222
 
183
223
  </details>
184
224
 
225
+ ## Which tool for which path
226
+
227
+ | Where the path lives | Use | Why |
228
+ | --- | --- | --- |
229
+ | under the workspace mount (`~/.dsh/presence/mounts/...`) | built-in `read` / `write` / `edit` / `grep` / `glob` | they reach the remote machine; `grep`/`glob` are routed to it automatically |
230
+ | remote absolute path outside the workspace (`/data`, `/scratch`, GPFS) | `presence_read` / `write` / `edit` / `ls` / `stat` / `search` / `glob` | the built-in tools only see the mount, not the rest of the machine |
231
+ | local file, for preview or Office | `presence_export` first, then `present` / native open | the mount path is virtual; only a real local file can be opened natively |
232
+
233
+ ## Long-running tasks
234
+
235
+ `presence_exec` waits for the command, so it is the wrong tool for a training run, a
236
+ build or a server. The recipe:
237
+
238
+ ```text
239
+ 1. presence_exec { command: "python train.py --lr 3e-4", background: true, label: "train-3e-4" }
240
+ → { id: "j7", pid: 4242, logPath: "/tmp/presence-jobs/....log" }
241
+ 2. presence_jobs { action: "wait", jobId: "j7" } # blocks; no sleep+poll loop
242
+ 3. presence_tail { path: "/tmp/presence-jobs/....log", follow: true } # watch progress
243
+ ```
244
+
245
+ - `presence_jobs { action: "status" }` gives a snapshot; `stop` terminates the process
246
+ group and verifies the port was released.
247
+ - Interactive work: `presence_terminal { action: "open", session: "train" }` — the
248
+ tmux-backed session survives a reconnect.
249
+ - **Do not** start `nohup`, `tmux new-session` or a trailing `&` inside a foreground
250
+ `presence_exec` for long jobs: when the login-node session ends, the cluster may clean
251
+ up what it left behind. `background: true` already detaches through `setsid`; on
252
+ scheduler-managed clusters start the job with the cluster's own launcher
253
+ (`rlaunch -d …`, `salloc`/`sbatch`) so the scheduler owns it.
254
+
185
255
  ## Safety and recovery
186
256
 
187
257
  - **Destructive commands never run unconfirmed.** `rm -r/-f`, `git clean -fdx`,
188
258
  `find -delete`, `shred`, `truncate` and `dd of=` are first replayed with
189
259
  no-op shims to expand their real targets; unset variables and protected paths
190
260
  (workspace root, home, `/`, `.git`) are refused. A second call with
191
- `confirm:true` is required to execute.
261
+ `confirm:true` is required to execute. A rejected mixed command also lists the
262
+ segments that are safe on their own (`Safe segments you can run separately:`);
263
+ `confirm:"partial"` runs only those. Removing one file with a literal
264
+ `rm -f <path>` inside the workspace is exempt — it cannot remove a tree.
192
265
  - **Prefer `presence_trash`** for anything under a workspace: it moves the path to
193
266
  `~/.dsh/presence-trash/<timestamp>/` and returns a restore hint.
194
267
  - **Writes are atomic** and keep the original file mode; `presence_edit` refuses
@@ -221,11 +294,11 @@ npm install --prefix .dev --ignore-scripts rollup @rollup/plugin-node-resolve \
221
294
  @rollup/plugin-commonjs @rollup/plugin-terser codemirror @codemirror/language-data \
222
295
  @codemirror/theme-one-dark
223
296
  node scripts/build-client.mjs
224
- powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package.ps1 -Version 0.18.0
225
- # → dist/presence-plugin-0.18.0.zip
297
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package.ps1 -Version 0.19.0
298
+ # → dist/presence-plugin-0.19.0.zip
226
299
  ```
227
300
 
228
- Tests require one SSH-reachable host:
301
+ Tests need one SSH-reachable host unless noted:
229
302
 
230
303
  ```sh
231
304
  node scripts/regression.mjs server # transport, symlinks, timeouts, binary
@@ -233,6 +306,7 @@ node scripts/job-probe.mjs server # jobs + port forwarding
233
306
  node scripts/feature-probe.mjs server # wait, sessions, pgid, readiness, search
234
307
  node scripts/import-probe.mjs server # local→remote binary, mode, sha256
235
308
  node scripts/safety-probe.mjs server # destructive guard + observation store
309
+ node scripts/p0-guard-probe.mjs # guard split/partial/single-rm (no host needed)
236
310
  ```
237
311
 
238
312
  ## Known limits
@@ -240,6 +314,17 @@ node scripts/safety-probe.mjs server # destructive guard + observation store
240
314
  - The mount path (`~/.dsh/presence/mounts/...`) is virtual: it works through
241
315
  `ctx.fs` / agent tools, but native apps (Explorer, Office, host Python) cannot
242
316
  see it — use `presence_export` first.
317
+ - `presence_exec` defaults to a 300 s timeout; longer work must use `background: true`
318
+ (foreground output over the cap is spilled to a remote file, and `tailLines` keeps the
319
+ end of a long report). Background jobs are tracked per session, not persisted across a
320
+ DSH restart.
321
+ - A login node may clean up processes left behind when a session closes: do not rely on
322
+ `nohup` / `tmux new-session` / `&` started inside a foreground `presence_exec` for long
323
+ jobs. Use `background: true` (detached with `setsid`) or `presence_terminal` (tmux),
324
+ and let the cluster scheduler own the job where required (`rlaunch -d`, `salloc`/`sbatch`).
325
+ - Argument aliases are accepted (`path`/`file_path`/`filePath`, `old`/`oldString`,
326
+ `new`/`newString`, `replaceAll`/`replace_all`, `dryRun`/`dry_run`, `cwd`/`workdir`);
327
+ the canonical name is what the tool schema documents.
243
328
  - SSH `MaxSessions` (default 10) bounds concurrent channels; the pool uses at most 8.
244
329
  - PTY terminals follow the connection; cross-reconnect shells need `tmux` on the target
245
330
  (used automatically by `presence_terminal { session }`).
package/README.zh-CN.md CHANGED
@@ -56,7 +56,7 @@ Presence 让 agent 留在本机,**通过每台机器一条常驻 SSH 连接把
56
56
  ### 1. 安装 DSH 插件
57
57
 
58
58
  ```sh
59
- dsh plugin add @rjfan/dsh-presence@0.18.0
59
+ dsh plugin add @rjfan/dsh-presence@0.19.0
60
60
  ```
61
61
 
62
62
  从源码安装:
@@ -143,17 +143,19 @@ Linux 主机上的实测:
143
143
 
144
144
  ## 工具
145
145
 
146
- DSH 插件提供 24 个工具。相对路径相对当前远程工作区;在工作区里 `host` 可省略。
146
+ DSH 插件提供 24 个工具。相对路径相对当前远程工作区;在工作区里 `host` 可省略。执行时接受
147
+ 参数别名(`path`/`file_path`/`filePath`、`old`/`oldString`、`new`/`newString`、
148
+ `cwd`/`workdir` 等),习惯写法都能用。
147
149
 
148
150
  <details>
149
151
  <summary><b>展开全部工具</b></summary>
150
152
 
151
153
  | 工具 | 说明 |
152
154
  | --- | --- |
153
- | `presence_hosts` | 列出 SSH 主机、连接状态和默认主机(支持 `format:"json"`)。 |
155
+ | `presence_hosts` | 列出 SSH 主机、连接状态和默认主机(支持 `format:"json"`);`workspace:true` 标记当前会话的机器,`degraded` 行带最后一次错误。 |
154
156
  | `presence_connect` | 建立/复用连接并返回远端身份。 |
155
- | `presence_exec` | 执行远端命令;`pipefail`、`background`、`waitForPort`、结构化 JSON、`script`、`saveStdout`;危险命令先 dry-run。 |
156
- | `presence_jobs` | 后台任务:`list`、`status`、`wait`(含 `timedOut`)、`stop`(`alive`/`verified`/`portReleased`)、`purge`。 |
157
+ | `presence_exec` | 执行远端命令;`pipefail`、`background`、`waitForPort`、结构化 JSON、`saveStdout`;超时默认 300 秒;`progress` 归一化 `\r` 进度条;超长输出落盘到远端文件并返回路径(`tailLines`);危险命令先 dry-run —— `confirm:true` 执行整条命令,`confirm:"partial"` 只执行安全分段。 |
158
+ | `presence_jobs` | 后台任务:`list`、`status`、`wait`(含 `timedOut`)、`stop`(`alive`/`verified`/`portReleased`)。 |
157
159
  | `presence_read` | 读远端文件,带行号、`offset`/`limit`,`raw:true` 返回逐字节原样内容。 |
158
160
  | `presence_read_many` | 一次读 1–50 个文件,支持每文件字节/行数上限。 |
159
161
  | `presence_write` | 原子写;保留 mode;返回真实 mode、字节数和完整 sha256;`ifMatchSha256` 乐观锁。 |
@@ -178,11 +180,39 @@ DSH 插件提供 24 个工具。相对路径相对当前远程工作区;在工
178
180
 
179
181
  </details>
180
182
 
183
+ ## 路径与工具怎么选
184
+
185
+ | 路径在哪 | 用什么 | 说明 |
186
+ | --- | --- | --- |
187
+ | 工作区挂载目录下(`~/.dsh/presence/mounts/...`) | 内置 `read` / `write` / `edit` / `grep` / `glob` | 都直达远端机器;`grep`/`glob` 会自动路由过去 |
188
+ | 工作区之外的远端绝对路径(`/data`、`/scratch`、GPFS) | `presence_read` / `write` / `edit` / `ls` / `stat` / `search` / `glob` | 内置工具只看得到挂载目录,看不到整台机器 |
189
+ | 本地文件(预览或 Office) | 先 `presence_export`,再 `present` / 原生打开 | 挂载路径是虚拟的,只有真实本地文件能被原生程序打开 |
190
+
191
+ ## 长时间任务
192
+
193
+ `presence_exec` 会等命令结束,所以训练、构建、起服务都不该直接用它。推荐流程:
194
+
195
+ ```text
196
+ 1. presence_exec { command: "python train.py --lr 3e-4", background: true, label: "train-3e-4" }
197
+ → { id: "j7", pid: 4242, logPath: "/tmp/presence-jobs/....log" }
198
+ 2. presence_jobs { action: "wait", jobId: "j7" } # 阻塞等待,不要 sleep+poll
199
+ 3. presence_tail { path: "/tmp/presence-jobs/....log", follow: true } # 看进度
200
+ ```
201
+
202
+ - `presence_jobs { action: "status" }` 取快照;`stop` 结束进程组并验证端口已释放。
203
+ - 交互式任务:`presence_terminal { action: "open", session: "train" }`,tmux 会话跨重连存活。
204
+ - **不要**在前台 `presence_exec` 里用 `nohup`、`tmux new-session` 或结尾 `&` 跑长任务:登录节点
205
+ 会话结束时,集群可能清理残留进程。`background: true` 本身用 `setsid` 脱离;调度器管理的集群
206
+ 请用集群自己的启动器(`rlaunch -d …`、`salloc`/`sbatch`),让调度器持有任务。
207
+
181
208
  ## 安全与恢复
182
209
 
183
210
  - **危险命令不会直接执行。** `rm -r/-f`、`git clean -fdx`、`find -delete`、`shred`、
184
211
  `truncate`、`dd of=` 会先用 no-op shim 重放以展开真实目标;未设置变量和受保护路径
185
- (工作区根、家目录、`/`、`.git`)直接拒绝。必须再带 `confirm:true` 才会执行。
212
+ (工作区根、家目录、`/`、`.git`)直接拒绝。必须再带 `confirm:true` 才会执行。被拒绝的
213
+ 混合命令还会列出可单独执行的安全分段(`Safe segments you can run separately:`),
214
+ `confirm:"partial"` 只执行这些分段;工作区内对单个文件的字面量 `rm -f <路径>` 免检
215
+ (它不可能删掉一棵树)。
186
216
  - **工作区内删除优先 `presence_trash`**:移动到 `~/.dsh/presence-trash/<时间戳>/` 并返回恢复提示。
187
217
  - **写入是原子的**,保留原文件 mode;`presence_edit` 锚点不唯一时报错而不是猜。
188
218
  - **读后写守卫**:内置 `read`/`write`/`edit` 共享读后写守卫;`presence_write` 也会记账,两套 API 可混用。
@@ -212,11 +242,11 @@ npm install --prefix .dev --ignore-scripts rollup @rollup/plugin-node-resolve \
212
242
  @rollup/plugin-commonjs @rollup/plugin-terser codemirror @codemirror/language-data \
213
243
  @codemirror/theme-one-dark
214
244
  node scripts/build-client.mjs
215
- powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package.ps1 -Version 0.18.0
216
- # → dist/presence-plugin-0.18.0.zip
245
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package.ps1 -Version 0.19.0
246
+ # → dist/presence-plugin-0.19.0.zip
217
247
  ```
218
248
 
219
- 验证脚本需要一台可 SSH 的主机:
249
+ 验证脚本需要一台可 SSH 的主机(标注“不需要真机”的除外):
220
250
 
221
251
  ```sh
222
252
  node scripts/regression.mjs server # 传输层、符号链接、超时、二进制
@@ -224,12 +254,20 @@ node scripts/job-probe.mjs server # 后台任务 + 端口转发
224
254
  node scripts/feature-probe.mjs server # wait、会话、pgid、就绪、搜索
225
255
  node scripts/import-probe.mjs server # 本地→远端二进制、mode、sha256
226
256
  node scripts/safety-probe.mjs server # 危险命令护栏 + 观测状态
257
+ node scripts/p0-guard-probe.mjs # 危险命令分段/单文件豁免(不需要真机)
227
258
  ```
228
259
 
229
260
  ## 已知边界
230
261
 
231
262
  - 挂载路径(`~/.dsh/presence/mounts/...`)是虚拟的:对 `ctx.fs` / agent 工具有效,
232
263
  但 Explorer、Office、宿主 Python 看不到 —— 需要先用 `presence_export`。
264
+ - `presence_exec` 超时默认 300 秒;更长的任务必须用 `background: true`(前台输出超限会
265
+ 落盘到远端文件,`tailLines` 保留长报告末尾)。后台任务按会话跟踪,不跨 DSH 重启持久化。
266
+ - 登录节点可能在会话结束时清理残留进程:不要在前台 `presence_exec` 里用 `nohup` /
267
+ `tmux new-session` / `&` 跑长任务。用 `background: true`(`setsid` 脱离)或
268
+ `presence_terminal`(tmux);集群上让调度器持有任务(`rlaunch -d`、`salloc`/`sbatch`)。
269
+ - 参数别名可用(`path`/`file_path`/`filePath`、`old`/`oldString`、`new`/`newString`、
270
+ `replaceAll`/`replace_all`、`dryRun`/`dry_run`、`cwd`/`workdir`);schema 里记录的是规范名。
233
271
  - sshd 的 `MaxSessions`(默认 10)限制并发通道;连接池最多用 8 个。
234
272
  - PTY 终端跟随连接存活;跨重连的 shell 需要目标机有 `tmux`(`presence_terminal { session }` 自动使用)。
235
273
  - ProxyJump / ProxyCommand 会被解析并提示,但目前不用于实际拨号。