@rjfan/dsh-presence 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +348 -2
- package/README.zh-CN.md +288 -0
- package/client.js +1 -0
- package/cordis.patch.yml +38 -0
- package/icon.svg +30 -0
- package/index.js +328 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +81 -4
- package/src/api.js +1277 -0
- package/src/core/connection.mjs +1900 -0
- package/src/core/ssh-config.mjs +158 -0
- package/src/custom-hosts.js +311 -0
- package/src/debug.js +21 -0
- package/src/destructive.js +446 -0
- package/src/diff.js +210 -0
- package/src/file-references.js +493 -0
- package/src/fs-provider.js +449 -0
- package/src/fs-selfcheck.js +135 -0
- package/src/intercept.js +133 -0
- package/src/job-notifier.js +193 -0
- package/src/localpaths.js +39 -0
- package/src/mounts.js +131 -0
- package/src/observed-store.js +78 -0
- package/src/path-ops.js +390 -0
- package/src/pool.js +943 -0
- package/src/port-discovery.js +278 -0
- package/src/remote-search.js +426 -0
- package/src/shared.js +14 -0
- package/src/tail.js +190 -0
- package/src/tools.js +3347 -0
- package/src/tunnel-store.js +278 -0
- package/src/workspaces.js +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fanrj3
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,349 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="assets/icon-180.png" width="88" alt="Presence icon" />
|
|
4
|
+
|
|
5
|
+
# Presence
|
|
6
|
+
|
|
7
|
+
**Your SSH hosts, as local tabs.**
|
|
8
|
+
|
|
9
|
+
Persistent remote workspaces, native file editing, search, exec, terminals and port
|
|
10
|
+
forwards for coding agents — with **nothing installed on the remote host**.
|
|
11
|
+
|
|
12
|
+
[English](README.md) · [简体中文](README.zh-CN.md) · [Releases](docs/RELEASE.md) · [MCP server](mcp/README.md)
|
|
13
|
+
|
|
14
|
+
[](https://www.npmjs.com/package/@rjfan/dsh-presence)
|
|
15
|
+
[](https://www.npmjs.com/package/presence-mcp)
|
|
16
|
+
[](LICENSE)
|
|
17
|
+
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Why
|
|
26
|
+
|
|
27
|
+
Running an agent on every remote machine means installing it N times, upgrading it N
|
|
28
|
+
times and configuring proxies per host. Running `ssh host '<command>'` from the local
|
|
29
|
+
agent means a new handshake per command, no native file tools and no continuity.
|
|
30
|
+
|
|
31
|
+
Presence keeps the agent local and **extends its reach through one persistent SSH
|
|
32
|
+
connection per host**. The remote side is still plain `sshd`: SFTP, exec and a PTY.
|
|
33
|
+
|
|
34
|
+
| Approach | Cost |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| Install the agent on every host | N installs, N upgrades, per-host proxying |
|
|
37
|
+
| `ssh host '<command>'` per call | Handshake per command, no file tools, no sessions |
|
|
38
|
+
| **Presence** | One connection per host, native tools, nothing installed remotely |
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Remote zero-install** — uses only `sshd` built-ins: the SFTP subsystem, exec channels and a PTY.
|
|
43
|
+
- **One handshake** — a pool keeps one connection per host; every later call is a request on it.
|
|
44
|
+
- **Native remote tools** — `rg`/`grep` runs on the target, commands run in the target shell; path, permission and encoding semantics are the remote ones.
|
|
45
|
+
- **Local-like edits** — atomic writes, mode preservation, ambiguity refusal, read-before-write guard, `presence_edit` with dry-run diffs.
|
|
46
|
+
- **Real workspaces** — a remote directory becomes a normal DSH workspace with a file tree and terminals that open **on the remote machine**.
|
|
47
|
+
- **Recoverable deletes** — destructive shell commands dry-run first; `presence_trash` moves paths to a remote trash with restore.
|
|
48
|
+
- **`@` completes remote files** — inside a remote workspace, file-reference completion indexes the machine itself (`rg --files`, `find` fallback), not a local mirror.
|
|
49
|
+
- **File operations, not just reads** — `presence_mkdir` / `move` / `copy` / `chmod` plus a right-click menu in the file tree; deletes go to the recoverable trash.
|
|
50
|
+
- **Diff and live logs** — unified diff between two remote files or a remote file and a local one; follow a log file from the panel without keeping a `tail -f` job around.
|
|
51
|
+
- **Ports that come back** — save forwards with auto-restore on reconnect, and let the Ports panel discover newly listening ports and forward them with one click.
|
|
52
|
+
- **Job notifications** — a toast (and desktop notification) when a background job finishes, plus an optional local `notifyCommand` for daemons and phones.
|
|
53
|
+
- **Two shells, one kernel** — the DSH plugin and a standalone MCP server (`presence-mcp`, 20 core tools) share the same kernel.
|
|
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
|
+
|
|
93
|
+
## Quick start
|
|
94
|
+
|
|
95
|
+
### 1. Install the DSH plugin
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
dsh plugin add @rjfan/dsh-presence@0.19.0
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
From a source checkout:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
dsh plugin add /path/to/dsh-presence
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 2. Pick a host and open a workspace
|
|
108
|
+
|
|
109
|
+
Open **Settings → Presence → New**, choose a host from `~/.ssh/config` (or paste a
|
|
110
|
+
manual `ssh` command), then pick a remote folder. It appears as a workspace; the
|
|
111
|
+
left sidebar, file tree, terminal and agent tools all use that remote directory.
|
|
112
|
+
|
|
113
|
+
### 3. Work
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
You: 把远程 8000 端口转发到本地,然后打开看看
|
|
117
|
+
Agent: presence_forward { remotePort: 8000 }
|
|
118
|
+
→ http://127.0.0.1:8000
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## MCP server
|
|
122
|
+
|
|
123
|
+
The same kernel is available to agents that cannot install DSH plugins (Codex CLI,
|
|
124
|
+
Claude Code, OpenCode, Cursor, …).
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
npx -y presence-mcp # 20 tools over stdio
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```jsonc
|
|
131
|
+
// Claude Desktop / OpenCode style
|
|
132
|
+
{
|
|
133
|
+
"mcpServers": {
|
|
134
|
+
"presence": {
|
|
135
|
+
"command": "npx",
|
|
136
|
+
"args": ["-y", "presence-mcp"],
|
|
137
|
+
"env": {
|
|
138
|
+
"PRESENCE_HOST": "server",
|
|
139
|
+
"PRESENCE_WORKSPACE": "server:/srv/app"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
See [mcp/README.md](mcp/README.md) for Codex TOML, Claude Code, OpenCode and
|
|
147
|
+
Cursor/Windsurf examples.
|
|
148
|
+
|
|
149
|
+
## How it works
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
┌───────────────────────────────┐
|
|
153
|
+
│ DSH plugin / MCP server │ two shells, one kernel
|
|
154
|
+
└──────────────┬────────────────┘
|
|
155
|
+
│
|
|
156
|
+
Presence kernel
|
|
157
|
+
├── ssh-config hosts, ports, users, keys, jumps from ~/.ssh/config
|
|
158
|
+
├── connection one persistent connection: SFTP / exec / PTY + pool + cancel
|
|
159
|
+
└── host pool one connection per host, created on demand, idle-collected
|
|
160
|
+
│
|
|
161
|
+
SSH (your existing keys, known_hosts, jumps, proxies)
|
|
162
|
+
│
|
|
163
|
+
target: Linux / macOS, stock sshd
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
| Agent operation | Channel | Remote dependency |
|
|
167
|
+
| --- | --- | --- |
|
|
168
|
+
| read / write / edit / list / stat | SFTP subsystem | `sftp-server` (stock sshd) |
|
|
169
|
+
| exec, search (`rg`/`grep`), git | exec channel | commands already on the host |
|
|
170
|
+
| interactive terminal | shell channel + PTY | the host's login shell |
|
|
171
|
+
|
|
172
|
+
Measured on a Linux host:
|
|
173
|
+
|
|
174
|
+
| Operation | Latency |
|
|
175
|
+
| --- | --- |
|
|
176
|
+
| `ssh host '<cmd>'` (new connection every time) | 520–540 ms |
|
|
177
|
+
| Presence connect (one handshake, key auth) | 145 ms |
|
|
178
|
+
| Presence `sftp stat` | p50 0.6 ms / p95 0.9 ms |
|
|
179
|
+
| Presence `exec true` (new channel + shell) | p50 7.6 ms |
|
|
180
|
+
| 8 concurrent exec channels | 15 ms total |
|
|
181
|
+
|
|
182
|
+
> Keep TCP_NODELAY on: without it, request/response hits delayed ACK and p95 goes
|
|
183
|
+
> from 0.9 ms to 48 ms.
|
|
184
|
+
|
|
185
|
+
## Tools
|
|
186
|
+
|
|
187
|
+
The DSH plugin exposes 24 tools. Relative paths resolve against the active remote
|
|
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.
|
|
191
|
+
|
|
192
|
+
<details>
|
|
193
|
+
<summary><b>Show all tools</b></summary>
|
|
194
|
+
|
|
195
|
+
| Tool | What it does |
|
|
196
|
+
| --- | --- |
|
|
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. |
|
|
198
|
+
| `presence_connect` | Open/reuse a host connection and report the remote identity. |
|
|
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`). |
|
|
201
|
+
| `presence_read` | Read a remote file with line numbers, `offset`/`limit`, or `raw:true` for verbatim content. |
|
|
202
|
+
| `presence_read_many` | Read 1–50 remote files in one call, with per-file byte/line caps. |
|
|
203
|
+
| `presence_write` | Atomic write; preserves mode; returns real mode, size and full sha256; `ifMatchSha256` optimistic lock. |
|
|
204
|
+
| `presence_edit` | Exact-text replacement or line-range replacement, with compact diff, `dryRun`, JSON fields. |
|
|
205
|
+
| `presence_ls` | List a directory; `recursive`, `totalBytes`, JSON output. |
|
|
206
|
+
| `presence_stat` | Batch `stat` for 1–100 paths: type, size, mode, mtime, optional sha256; missing paths return `exists:false`. |
|
|
207
|
+
| `presence_mkdir` | Create a remote directory (recursive by default); idempotent when it already exists. |
|
|
208
|
+
| `presence_move` | Rename or move a path inside the same machine; refuses to replace an existing file unless `overwrite:true`. |
|
|
209
|
+
| `presence_copy` | Copy a remote file (SFTP stream, or `cp -p` for large files) or a directory with `recursive:true`. |
|
|
210
|
+
| `presence_chmod` | Change permissions with an octal mode (`755`, `0644`), optionally recursive. |
|
|
211
|
+
| `presence_diff` | Unified diff of two remote files, or a remote file against a local one; context lines and JSON stats. |
|
|
212
|
+
| `presence_tail` | Read the last N lines of a remote file, or follow it for a bounded time (`follow:true`, `timeoutMs`). |
|
|
213
|
+
| `presence_search` | Remote `rg`/`grep` search, count-only mode, JSON matches. |
|
|
214
|
+
| `presence_glob` | Find remote files by glob with size/mtime. |
|
|
215
|
+
| `presence_import` | Local → remote binary-safe upload with mode and sha256. |
|
|
216
|
+
| `presence_export` | Remote → local real file (content-addressed), for preview or native apps. |
|
|
217
|
+
| `presence_forward` | Forward a remote port to `127.0.0.1`; open/list/close with JSON. |
|
|
218
|
+
| `presence_port_probe` | Check listeners and TCP reachability for up to 20 ports; `expect:open|closed`, polling. |
|
|
219
|
+
| `presence_http_probe` | Wait for port → forward → local HTTP assertions → close → verify the local port was released. |
|
|
220
|
+
| `presence_terminal` | Persistent PTY: `open` with `cwd`/`command`, `write` (`ackOnly`), `read`, `close`; tmux-backed sessions survive reconnects. |
|
|
221
|
+
| `presence_trash` | Recoverable delete: move to remote trash, list/restore; batch `paths[]`; returns `sourceRemoved` and `restoreHint`. |
|
|
222
|
+
|
|
223
|
+
</details>
|
|
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
|
+
|
|
255
|
+
## Safety and recovery
|
|
256
|
+
|
|
257
|
+
- **Destructive commands never run unconfirmed.** `rm -r/-f`, `git clean -fdx`,
|
|
258
|
+
`find -delete`, `shred`, `truncate` and `dd of=` are first replayed with
|
|
259
|
+
no-op shims to expand their real targets; unset variables and protected paths
|
|
260
|
+
(workspace root, home, `/`, `.git`) are refused. A second call with
|
|
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.
|
|
265
|
+
- **Prefer `presence_trash`** for anything under a workspace: it moves the path to
|
|
266
|
+
`~/.dsh/presence-trash/<timestamp>/` and returns a restore hint.
|
|
267
|
+
- **Writes are atomic** and keep the original file mode; `presence_edit` refuses
|
|
268
|
+
ambiguous anchors instead of guessing.
|
|
269
|
+
- **Read before write**: built-in `read`/`write`/`edit` share a read-before-write
|
|
270
|
+
guard; `presence_write` records the observation so both APIs can be mixed.
|
|
271
|
+
|
|
272
|
+
## Configuration
|
|
273
|
+
|
|
274
|
+
The plugin reads these environment variables on the host process:
|
|
275
|
+
|
|
276
|
+
| Variable | Default | Meaning |
|
|
277
|
+
| --- | --- | --- |
|
|
278
|
+
| `PRESENCE_HOST` | — | Default SSH host (alias from `~/.ssh/config` or a manual host). |
|
|
279
|
+
| `PRESENCE_WORKSPACE` | — | `host:/abs/path` to bind a remote workspace. |
|
|
280
|
+
| `PRESENCE_PASSWORD_<ALIAS>` | — | Password for a host; `<ALIAS>` is uppercase with non-alphanumerics replaced by `_`. |
|
|
281
|
+
| `PRESENCE_MAX_CHANNELS` | 8 | Concurrent channels per connection. |
|
|
282
|
+
| `PRESENCE_CONNECT_TIMEOUT_MS` | 25000 | Connection attempt timeout. |
|
|
283
|
+
| `PRESENCE_IDLE_TIMEOUT_MS` | 900000 | Idle connection timeout (connections with terminals are kept). |
|
|
284
|
+
| `PRESENCE_READ_ONLY` | false | Refuse writes/edits; reads, search and exec still work. |
|
|
285
|
+
|
|
286
|
+
Full configuration and the path/permission matrix are in
|
|
287
|
+
[docs/VERIFICATION.md](docs/VERIFICATION.md) and [docs/HANDOFF.md](docs/HANDOFF.md).
|
|
288
|
+
|
|
289
|
+
## Development
|
|
290
|
+
|
|
291
|
+
```sh
|
|
292
|
+
npm install
|
|
293
|
+
npm install --prefix .dev --ignore-scripts rollup @rollup/plugin-node-resolve \
|
|
294
|
+
@rollup/plugin-commonjs @rollup/plugin-terser codemirror @codemirror/language-data \
|
|
295
|
+
@codemirror/theme-one-dark
|
|
296
|
+
node scripts/build-client.mjs
|
|
297
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package.ps1 -Version 0.19.0
|
|
298
|
+
# → dist/presence-plugin-0.19.0.zip
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Tests need one SSH-reachable host unless noted:
|
|
302
|
+
|
|
303
|
+
```sh
|
|
304
|
+
node scripts/regression.mjs server # transport, symlinks, timeouts, binary
|
|
305
|
+
node scripts/job-probe.mjs server # jobs + port forwarding
|
|
306
|
+
node scripts/feature-probe.mjs server # wait, sessions, pgid, readiness, search
|
|
307
|
+
node scripts/import-probe.mjs server # local→remote binary, mode, sha256
|
|
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)
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Known limits
|
|
313
|
+
|
|
314
|
+
- The mount path (`~/.dsh/presence/mounts/...`) is virtual: it works through
|
|
315
|
+
`ctx.fs` / agent tools, but native apps (Explorer, Office, host Python) cannot
|
|
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.
|
|
328
|
+
- SSH `MaxSessions` (default 10) bounds concurrent channels; the pool uses at most 8.
|
|
329
|
+
- PTY terminals follow the connection; cross-reconnect shells need `tmux` on the target
|
|
330
|
+
(used automatically by `presence_terminal { session }`).
|
|
331
|
+
- ProxyJump/ProxyCommand are parsed and warned about, but not used for dialing yet.
|
|
332
|
+
- `@` file completion indexes the remote tree lazily (about 10 s per workspace);
|
|
333
|
+
on a very large tree the first query can take a few seconds.
|
|
334
|
+
- Port auto-restore runs when the connection is re-established; a forward whose
|
|
335
|
+
saved local port is already taken falls back to another free port.
|
|
336
|
+
|
|
337
|
+
## Contributing
|
|
338
|
+
|
|
339
|
+
Issues and PRs are welcome. For a feature, open an issue describing the remote
|
|
340
|
+
workflow you want; for a fix, keep the diff focused and add a probe under
|
|
341
|
+
`scripts/` when behavior changes.
|
|
342
|
+
|
|
343
|
+
## License
|
|
344
|
+
|
|
345
|
+
[MIT](LICENSE)
|
|
346
|
+
|
|
347
|
+
## Star History
|
|
348
|
+
|
|
349
|
+
[](https://star-history.com/#fanrj3/dsh-presence&Date)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/icon-180.png" width="88" alt="Presence icon" />
|
|
4
|
+
|
|
5
|
+
# Presence
|
|
6
|
+
|
|
7
|
+
**把 SSH 机器变成本地标签页。**
|
|
8
|
+
|
|
9
|
+
给 coding agent 用的持久远程工作区、原生文件编辑、搜索、命令执行、终端和端口转发
|
|
10
|
+
—— **远端不需要安装任何东西**。
|
|
11
|
+
|
|
12
|
+
[English](README.md) · [简体中文](README.zh-CN.md) · [版本记录](docs/RELEASE.md) · [MCP server](mcp/README.md)
|
|
13
|
+
|
|
14
|
+
[](https://www.npmjs.com/package/@rjfan/dsh-presence)
|
|
15
|
+
[](https://www.npmjs.com/package/presence-mcp)
|
|
16
|
+
[](LICENSE)
|
|
17
|
+
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 为什么不是「agent 跑在远端」
|
|
26
|
+
|
|
27
|
+
在每台服务器上装 agent,意味着装 N 次、升级 N 次、逐台配代理;让本地 agent 每次
|
|
28
|
+
`ssh host '<command>'`,意味着每条命令一次握手、用不上原生文件工具、上下文不连续。
|
|
29
|
+
|
|
30
|
+
Presence 让 agent 留在本机,**通过每台机器一条常驻 SSH 连接把能力伸到远端**。远端
|
|
31
|
+
仍然只是普通的 `sshd`:SFTP、exec 和一个 PTY。
|
|
32
|
+
|
|
33
|
+
| 方案 | 代价 |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| 每台机器装 agent | N 次安装、N 次升级、逐台代理配置 |
|
|
36
|
+
| 每个操作 `ssh host '<cmd>'` | 每条命令一次握手、无文件工具、无会话 |
|
|
37
|
+
| **Presence** | 每台机器一条连接、原生工具、远端零安装 |
|
|
38
|
+
|
|
39
|
+
## 特性
|
|
40
|
+
|
|
41
|
+
- **远端零安装** —— 只用 `sshd` 自带能力:SFTP 子系统、exec 通道和 PTY。
|
|
42
|
+
- **一次握手** —— 连接池为每台机器保持一条连接,之后的调用都是这条连接上的请求。
|
|
43
|
+
- **用目标机的原生工具** —— `rg`/`grep` 在目标机跑,命令在目标 shell 里执行;路径、权限、编码都是远端的语义。
|
|
44
|
+
- **接近本地的编辑** —— 原子写、保留 mode、锚点歧义拒绝、读后写守卫、`presence_edit` 的 dry-run diff。
|
|
45
|
+
- **真正的远程工作区** —— 远端目录注册成普通 DSH 工作区,文件树、终端都**开在远端机器上**。
|
|
46
|
+
- **可恢复的删除** —— 危险 shell 命令先 dry-run;`presence_trash` 移入远端回收站并可恢复。
|
|
47
|
+
- **`@` 能补全远端文件** —— 远程工作区里,文件引用补全直接索引目标机(`rg --files`,回退 `find`),不是本地镜像。
|
|
48
|
+
- **文件操作不只是读** —— `presence_mkdir` / `move` / `copy` / `chmod`,文件树右键菜单直接操作;删除走可恢复回收站。
|
|
49
|
+
- **Diff 与实时日志** —— 两个远端文件、或远端与本地文件之间的统一 diff;面板里直接跟随日志,不用常驻 `tail -f` 任务。
|
|
50
|
+
- **转发会自己回来** —— 保存转发并自动恢复;端口面板发现新监听端口后可一键转发。
|
|
51
|
+
- **任务完成通知** —— 后台任务结束时弹 toast(及桌面通知),也可配置本地 `notifyCommand` 推送到手机或守护进程。
|
|
52
|
+
- **两种外壳,同一个内核** —— DSH 插件与独立 MCP server(`presence-mcp`,20 个核心工具)共用同一套内核。
|
|
53
|
+
|
|
54
|
+
## 快速开始
|
|
55
|
+
|
|
56
|
+
### 1. 安装 DSH 插件
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
dsh plugin add @rjfan/dsh-presence@0.19.0
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
从源码安装:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
dsh plugin add /path/to/dsh-presence
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. 选一台机器,打开工作区
|
|
69
|
+
|
|
70
|
+
打开 **设置 → Presence → 新建**,从 `~/.ssh/config` 选一台机器(或直接粘贴一条
|
|
71
|
+
`ssh` 命令),再选一个远端目录。它会以工作区出现:左侧文件树、终端和 agent 工具
|
|
72
|
+
都使用这个远端目录。
|
|
73
|
+
|
|
74
|
+
### 3. 开始用
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
你:把远程 8000 端口转发到本地,然后打开看看
|
|
78
|
+
Agent: presence_forward { remotePort: 8000 }
|
|
79
|
+
→ http://127.0.0.1:8000
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## MCP server
|
|
83
|
+
|
|
84
|
+
同样的能力也提供给装不了 DSH 插件的 agent(Codex CLI、Claude Code、OpenCode、Cursor 等)。
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
npx -y presence-mcp # 20 个工具,走 stdio
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```jsonc
|
|
91
|
+
// Claude Desktop / OpenCode 风格
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"presence": {
|
|
95
|
+
"command": "npx",
|
|
96
|
+
"args": ["-y", "presence-mcp"],
|
|
97
|
+
"env": {
|
|
98
|
+
"PRESENCE_HOST": "server",
|
|
99
|
+
"PRESENCE_WORKSPACE": "server:/srv/app"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Codex TOML、Claude Code、OpenCode、Cursor/Windsurf 的完整配置见
|
|
107
|
+
[mcp/README.md](mcp/README.md)。
|
|
108
|
+
|
|
109
|
+
## 工作原理
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
┌───────────────────────────────┐
|
|
113
|
+
│ DSH 插件 / MCP server │ 两种外壳,同一个内核
|
|
114
|
+
└──────────────┬────────────────┘
|
|
115
|
+
│
|
|
116
|
+
Presence 内核
|
|
117
|
+
├── ssh-config 从 ~/.ssh/config 读主机、端口、用户、密钥、跳板
|
|
118
|
+
├── connection 一条常驻连接:SFTP / exec / PTY + 通道池 + 取消
|
|
119
|
+
└── host pool 每台机器一条连接,按需建立、闲置回收
|
|
120
|
+
│
|
|
121
|
+
SSH(沿用你已有的密钥、known_hosts、跳板、代理)
|
|
122
|
+
│
|
|
123
|
+
目标机:Linux / macOS,原生 sshd
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Agent 操作 | 通道 | 远端依赖 |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| 读 / 写 / 编辑 / 列目录 / stat | SFTP 子系统 | sshd 自带 `sftp-server` |
|
|
129
|
+
| 执行命令、搜索(rg/grep)、git | exec 通道 | 目标机已有的命令 |
|
|
130
|
+
| 交互式终端 | shell 通道 + PTY | 目标机的登录 shell |
|
|
131
|
+
|
|
132
|
+
Linux 主机上的实测:
|
|
133
|
+
|
|
134
|
+
| 操作 | 延迟 |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `ssh host '<cmd>'`(每次新连接) | 520–540 ms |
|
|
137
|
+
| Presence 建连(一次握手,含密钥认证) | 145 ms |
|
|
138
|
+
| Presence `sftp stat` | p50 0.6 ms / p95 0.9 ms |
|
|
139
|
+
| Presence `exec true`(新通道 + shell) | p50 7.6 ms |
|
|
140
|
+
| 8 个 exec 通道并发 | 合计 15 ms |
|
|
141
|
+
|
|
142
|
+
> 必须保持 TCP_NODELAY:否则请求/响应会撞上 delayed ACK,p95 从 0.9 ms 劣化到 48 ms。
|
|
143
|
+
|
|
144
|
+
## 工具
|
|
145
|
+
|
|
146
|
+
DSH 插件提供 24 个工具。相对路径相对当前远程工作区;在工作区里 `host` 可省略。执行时接受
|
|
147
|
+
参数别名(`path`/`file_path`/`filePath`、`old`/`oldString`、`new`/`newString`、
|
|
148
|
+
`cwd`/`workdir` 等),习惯写法都能用。
|
|
149
|
+
|
|
150
|
+
<details>
|
|
151
|
+
<summary><b>展开全部工具</b></summary>
|
|
152
|
+
|
|
153
|
+
| 工具 | 说明 |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `presence_hosts` | 列出 SSH 主机、连接状态和默认主机(支持 `format:"json"`);`workspace:true` 标记当前会话的机器,`degraded` 行带最后一次错误。 |
|
|
156
|
+
| `presence_connect` | 建立/复用连接并返回远端身份。 |
|
|
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`)。 |
|
|
159
|
+
| `presence_read` | 读远端文件,带行号、`offset`/`limit`,`raw:true` 返回逐字节原样内容。 |
|
|
160
|
+
| `presence_read_many` | 一次读 1–50 个文件,支持每文件字节/行数上限。 |
|
|
161
|
+
| `presence_write` | 原子写;保留 mode;返回真实 mode、字节数和完整 sha256;`ifMatchSha256` 乐观锁。 |
|
|
162
|
+
| `presence_edit` | 精确文本替换或行号区间替换,带紧凑 diff、`dryRun` 和 JSON 字段。 |
|
|
163
|
+
| `presence_ls` | 列目录;`recursive`、`totalBytes`、JSON 输出。 |
|
|
164
|
+
| `presence_stat` | 批量 `stat` 1–100 个路径:类型、大小、mode、mtime、可选 sha256;缺失路径返回 `exists:false`。 |
|
|
165
|
+
| `presence_mkdir` | 创建远端目录(默认递归);已存在时幂等成功。 |
|
|
166
|
+
| `presence_move` | 同一台机器内重命名/移动;目标已存在时除非 `overwrite:true` 否则拒绝。 |
|
|
167
|
+
| `presence_copy` | 复制远端文件(SFTP 流,大文件走 `cp -p`),目录需 `recursive:true`。 |
|
|
168
|
+
| `presence_chmod` | 按八进制 mode 改权限(`755`/`0644`),可递归。 |
|
|
169
|
+
| `presence_diff` | 两个远端文件、或远端与本地文件的统一 diff;支持上下文行数和 JSON 统计。 |
|
|
170
|
+
| `presence_tail` | 读远端文件最后 N 行,或在限定时间内跟随(`follow:true`、`timeoutMs`)。 |
|
|
171
|
+
| `presence_search` | 远端 `rg`/`grep` 搜索,`countOnly`,JSON matches。 |
|
|
172
|
+
| `presence_glob` | 远端 glob 找文件,带 size/mtime。 |
|
|
173
|
+
| `presence_import` | 本地 → 远端二进制安全上传,可设 mode,返回 sha256。 |
|
|
174
|
+
| `presence_export` | 远端 → 本地真实文件(内容寻址),供预览或原生程序打开。 |
|
|
175
|
+
| `presence_forward` | 远端端口转发到 `127.0.0.1`;open/list/close 支持 JSON。 |
|
|
176
|
+
| `presence_port_probe` | 检查最多 20 个端口的监听与 TCP 可达性;`expect:open|closed` 可轮询。 |
|
|
177
|
+
| `presence_http_probe` | 等端口 → 开转发 → 本地 HTTP 断言 → 关转发 → 验证本地端口已释放。 |
|
|
178
|
+
| `presence_terminal` | 持久 PTY:`open` 可带 `cwd`/`command`,`write`(`ackOnly`)、`read`、`close`;tmux 会话可跨重连。 |
|
|
179
|
+
| `presence_trash` | 可恢复删除:移入远端回收站、list/restore;支持批量 `paths[]`;返回 `sourceRemoved` 和 `restoreHint`。 |
|
|
180
|
+
|
|
181
|
+
</details>
|
|
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
|
+
|
|
208
|
+
## 安全与恢复
|
|
209
|
+
|
|
210
|
+
- **危险命令不会直接执行。** `rm -r/-f`、`git clean -fdx`、`find -delete`、`shred`、
|
|
211
|
+
`truncate`、`dd of=` 会先用 no-op shim 重放以展开真实目标;未设置变量和受保护路径
|
|
212
|
+
(工作区根、家目录、`/`、`.git`)直接拒绝。必须再带 `confirm:true` 才会执行。被拒绝的
|
|
213
|
+
混合命令还会列出可单独执行的安全分段(`Safe segments you can run separately:`),
|
|
214
|
+
`confirm:"partial"` 只执行这些分段;工作区内对单个文件的字面量 `rm -f <路径>` 免检
|
|
215
|
+
(它不可能删掉一棵树)。
|
|
216
|
+
- **工作区内删除优先 `presence_trash`**:移动到 `~/.dsh/presence-trash/<时间戳>/` 并返回恢复提示。
|
|
217
|
+
- **写入是原子的**,保留原文件 mode;`presence_edit` 锚点不唯一时报错而不是猜。
|
|
218
|
+
- **读后写守卫**:内置 `read`/`write`/`edit` 共享读后写守卫;`presence_write` 也会记账,两套 API 可混用。
|
|
219
|
+
|
|
220
|
+
## 配置
|
|
221
|
+
|
|
222
|
+
插件从宿主进程环境变量读取:
|
|
223
|
+
|
|
224
|
+
| 变量 | 默认 | 含义 |
|
|
225
|
+
| --- | --- | --- |
|
|
226
|
+
| `PRESENCE_HOST` | — | 默认 SSH 主机(`~/.ssh/config` 别名或手动主机)。 |
|
|
227
|
+
| `PRESENCE_WORKSPACE` | — | `host:/abs/path`,绑定一个远程工作区。 |
|
|
228
|
+
| `PRESENCE_PASSWORD_<ALIAS>` | — | 某台机器的密码;`<ALIAS>` 大写、非字母数字换成 `_`。 |
|
|
229
|
+
| `PRESENCE_MAX_CHANNELS` | 8 | 每连接并发通道数。 |
|
|
230
|
+
| `PRESENCE_CONNECT_TIMEOUT_MS` | 25000 | 连接尝试超时。 |
|
|
231
|
+
| `PRESENCE_IDLE_TIMEOUT_MS` | 900000 | 空闲连接回收时间(持终端连接不回收)。 |
|
|
232
|
+
| `PRESENCE_READ_ONLY` | false | 拒绝写入/编辑;读取、搜索、执行不受影响。 |
|
|
233
|
+
|
|
234
|
+
完整配置与路径/权限矩阵见 [docs/VERIFICATION.md](docs/VERIFICATION.md) 和
|
|
235
|
+
[docs/HANDOFF.md](docs/HANDOFF.md)。
|
|
236
|
+
|
|
237
|
+
## 开发
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
npm install
|
|
241
|
+
npm install --prefix .dev --ignore-scripts rollup @rollup/plugin-node-resolve \
|
|
242
|
+
@rollup/plugin-commonjs @rollup/plugin-terser codemirror @codemirror/language-data \
|
|
243
|
+
@codemirror/theme-one-dark
|
|
244
|
+
node scripts/build-client.mjs
|
|
245
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package.ps1 -Version 0.19.0
|
|
246
|
+
# → dist/presence-plugin-0.19.0.zip
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
验证脚本需要一台可 SSH 的主机(标注“不需要真机”的除外):
|
|
250
|
+
|
|
251
|
+
```sh
|
|
252
|
+
node scripts/regression.mjs server # 传输层、符号链接、超时、二进制
|
|
253
|
+
node scripts/job-probe.mjs server # 后台任务 + 端口转发
|
|
254
|
+
node scripts/feature-probe.mjs server # wait、会话、pgid、就绪、搜索
|
|
255
|
+
node scripts/import-probe.mjs server # 本地→远端二进制、mode、sha256
|
|
256
|
+
node scripts/safety-probe.mjs server # 危险命令护栏 + 观测状态
|
|
257
|
+
node scripts/p0-guard-probe.mjs # 危险命令分段/单文件豁免(不需要真机)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## 已知边界
|
|
261
|
+
|
|
262
|
+
- 挂载路径(`~/.dsh/presence/mounts/...`)是虚拟的:对 `ctx.fs` / agent 工具有效,
|
|
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 里记录的是规范名。
|
|
271
|
+
- sshd 的 `MaxSessions`(默认 10)限制并发通道;连接池最多用 8 个。
|
|
272
|
+
- PTY 终端跟随连接存活;跨重连的 shell 需要目标机有 `tmux`(`presence_terminal { session }` 自动使用)。
|
|
273
|
+
- ProxyJump / ProxyCommand 会被解析并提示,但目前不用于实际拨号。
|
|
274
|
+
- `@` 文件补全按工作区惰性索引远端树(约 10 秒缓存);超大目录首次查询可能要几秒。
|
|
275
|
+
- 端口自动恢复在连接重建时执行;若保存的本地端口已被占用,会回退到其它空闲端口。
|
|
276
|
+
|
|
277
|
+
## 参与贡献
|
|
278
|
+
|
|
279
|
+
欢迎 issue 和 PR。新功能请先开 issue 描述你的远程工作流;修 bug 请保持 diff 聚焦,
|
|
280
|
+
行为变化时在 `scripts/` 下补一个探针。
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
[MIT](LICENSE)
|
|
285
|
+
|
|
286
|
+
## Star History
|
|
287
|
+
|
|
288
|
+
[](https://star-history.com/#fanrj3/dsh-presence&Date)
|