dsh-remote-agent 0.8.1 → 0.9.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
@@ -16,7 +16,7 @@
16
16
  ┌─────────────── 主电脑(hub)────────────────┐
17
17
  │ DSH · 模型 · 会话日志 · 全部凭证 │
18
18
  │ ┌─ dsh-remote-agent ─────────────────────┐ │
19
- │ │ 7 个 remote_* 工具 + Web 面板 │ │
19
+ │ │ 12 个 remote_* 工具 + Web 面板 │ │
20
20
  │ └────────────────────────────────────────┘ │
21
21
  └──────────────────▲──────────────────────────┘
22
22
  │ 节点主动拨入(direct / relay)
@@ -30,14 +30,20 @@
30
30
 
31
31
  | | |
32
32
  |---|---|
33
- | **状态** | 0.4.0 —— 主机侧端到端可用;真实运行时拨入已实测(独立进程 worker → 正在运行的 DSH 桌面 hub,见 [KNOWN-ISSUES §8](KNOWN-ISSUES.md));`exec` 与 `agent` 双链路均已在两台真机上验证通过 |
33
+ | **状态** | 0.8.1 —— 主机 + 副机端到端可用;`exec` 与 `agent` 双链路均已在两台真机上验证通过;CI 覆盖 Windows / macOS / Linux |
34
34
  | **DSH** | 桌面版 / CLI,0.2.0-rc.2 验证 |
35
35
  | **平台** | Windows(全量验证)· Linux / macOS(CI 覆盖)|
36
36
  | **依赖** | **零外部 npm 依赖** —— 只用 `node:` 内置模块 |
37
37
  | **Node** | `^22.19.0 \|\| >=24.0.0` |
38
+ | **测试** | 938 项(937 pass / 1 skip)· 117 条文档漂移断言 · eslint 严格档 |
38
39
 
39
40
  **Web 面板**(侧栏「远程同事」):节点列表(含 CPU/内存徽章)、派发任务、
40
- 任务状态、结果摘要、工作区变更、成品清单、日志尾巴。
41
+ 任务状态、结果摘要、工作区变更、成品清单、日志尾巴、**环境体检**、
42
+ **工作区共享(SMB)**、模型下拉。
43
+
44
+ > **开箱即用**:装完重启 DSH 就能用。包内 patch 默认 `role: auto`(没配
45
+ > `hubUrl` 就是主机),并把 `webserver` 绑到 `0.0.0.0:19387`(否则副机连不上)。
46
+ > 详见下方「安装」。
41
47
 
42
48
  <p align="center">
43
49
  <img src="docs/screenshots/panel-main.png" width="720" alt="Web 面板:节点、派发表单、任务列表">
@@ -93,6 +99,88 @@ node bin/dsh-remote-setup.js --doctor 你的配置.yml
93
99
 
94
100
  ---
95
101
 
102
+ ## 功能
103
+
104
+ ### 一、把任务派给副机
105
+
106
+ | 功能 | 说明 |
107
+ |---|---|
108
+ | **两种任务模式** | `exec` 直接跑命令(`D:\work` 里 `npm run build`、`git pull`、批量重命名);`agent` 让副机起**独立 Agent 会话**,它自己规划、调工具、写文件 |
109
+ | **两阶段审批** | `exec` 默认挂起等确认(`approval: 'manual'`)——等不到人不执行。防止 AI 误派危险命令 |
110
+ | **dry-run 预览** | 派发前先看"会路由到哪台、用哪个模型、在哪个目录跑、预计什么产物",不真派 |
111
+ | **仓库感知调度** | 副机上报它 `roots` 下的 git 仓库(名字 + 远端 url),`remote_task({repo: 'my-project'})` 自动找有那个仓库的副机 |
112
+ | **负载感知路由** | 副机每 30s 上报 CPU/内存/负载;不指定节点时按负载挑一台 |
113
+ | **模型选择** | 面板从副机自述的**可用模型清单**里下拉选(不用猜 provider/model 名);headless 后端不支持指定模型时**派发前就拦**,不用等跑到副机才失败 |
114
+ | **超时按模式区分** | `exec` 默认 10 分钟(失控命令不该占机一小时);`agent` 默认 1 小时 |
115
+ | **并发上限** | 副机 `maxConcurrent`(默认 4)限制同时跑几个任务;超出的排队,`remote_status` 能看到排队数与最久等待 |
116
+
117
+ ### 二、拿结果,但不搬整个工作区
118
+
119
+ | 功能 | 说明 |
120
+ |---|---|
121
+ | **handoff 结果卡** | 任务结束回一张卡:结果摘要 + 工作区变更 + 成品清单 + 退出码。**不是把整个目录传回来** |
122
+ | **产物三分类** | 工作区文件按 `final` / `intermediate` / `other` 分类,**只有 `final` 能拉**。日志、缓存、`node_modules` 全留在副机 |
123
+ | **显式拉取** | 要哪个文件用 `remote_pull` 单独拉,支持 glob 过滤 |
124
+ | **断点续传** | 拉到一半掉线不从头重传:已收字节保留,重试从中段续(`startOffset` + `prefixSha256` 校验)|
125
+ | **变更 diff 视图** | 任务前后对比工作区,看清它到底改了哪些文件、增删多少字节 |
126
+
127
+ ### 三、直接翻副机的文件(不占任务队列)
128
+
129
+ 四个**只读**工具,走独立的 `fs/req`↔`fs/res` 帧 —— **不进任务队列、不占并发槽**。
130
+ 副机正跑长任务堵着队列时,这些依然秒回:
131
+
132
+ | 工具 | 用途 |
133
+ |---|---|
134
+ | `remote_ls` | 列目录(单层、分页) |
135
+ | `remote_read` | 读文件(文本按行分段;二进制只回元数据) |
136
+ | `remote_grep` | 副机**本地**全文搜索(纯 JS,不 spawn 进程) |
137
+ | `remote_tree` | 目录树摘要(扫描在副机跑完,只回结构) |
138
+
139
+ 安全边界:路径必须在该节点 `roots` 内、`denyPaths` 生效、符号链接只列条目不解引用。
140
+
141
+ ### 四、用本地 IDE 打开副机的文件(SMB 共享)
142
+
143
+ 面板上「工作区共享」区:把副机工作区通过 SMB 共享出去,主机
144
+ `net use X: \\副机\dsh-work-...` 映射成盘符 —— 可以用本地 VS Code 直接打开。
145
+
146
+ > **⚠ 这会绕过本插件的安全边界**(`roots` / `denyPaths` 失效、中间产物与密钥
147
+ > 对主机可见、新增凭证面)。需要**二次确认**,默认**只读**。
148
+ > 完整风险与用法见 [docs/workspace-share.md](docs/workspace-share.md)。
149
+
150
+ ### 五、出问题能自己诊断
151
+
152
+ | 功能 | 说明 |
153
+ |---|---|
154
+ | **环境体检** | 面板一键体检 8 项:角色配置、`inject` 依赖、**Web 服务是否绑在可达地址**、端口、防火墙、`roots` 是否存在、Tailscale、副机在线。**每条都给具体修法**,能自动修的标出来 |
155
+ | **可达性自检** | 启动时检查 `webServer` 是否绑在 loopback —— 绑了副机永远连不上,且主机侧毫无异常。绑了会打明确 warn + 面板红色横幅 |
156
+ | **队列健康快照** | 副机随资源帧上报 `queued` / `running` / `maxConcurrent` / 最久等待;堵死时面板与 `remote_nodes` 都会警告 |
157
+ | **面板「运行中」分组** | 运行中的任务置顶,显示**已跑多久**(判断"是不是卡住了"的唯一线索)|
158
+ | **取消真打断** | `remote_cancel` 真的中止 agent 循环(不只是置个标志等它自己跑完)|
159
+ | **审计日志** | 派发/取消/拉取/审批全部留痕(`~/.dsh-remote-agent/audit.jsonl`,可轮转)|
160
+
161
+ ### 六、断连、跨网、安全
162
+
163
+ | 功能 | 说明 |
164
+ |---|---|
165
+ | **断连不终止任务** | 中继挂了、主机重启了,副机照跑;重连后 `task/sync` 自动对账,**已完成的不重跑** |
166
+ | **两种传输** | `direct` 直连(同一局域网/Tailscale);`relay` 中继(双方都在 NAT 后,需要一台 VPS)|
167
+ | **端到端加密** | relay 模式业务帧 X25519 + AES-256-GCM;中继只看得到路由信封 |
168
+ | **主机身份验证** | 副机验主机:hub 持久 ed25519 身份 + 配对码携带指纹,首连 TOFU 钉住 |
169
+ | **凭证不离开副机** | 协议里不带任何模型 key;副机用自己的模型配置跑自己的循环 |
170
+ | **零 npm 依赖** | 只用 `node:` 内置模块,无构建步骤,`lib/` 就是产物 |
171
+
172
+ ### 工具速查(共 12 个)
173
+
174
+ | 类别 | 工具 |
175
+ |---|---|
176
+ | **节点** | `remote_nodes`(列表 + 负载 + 队列 + 模型清单)|
177
+ | **任务** | `remote_task`(派发)· `remote_status`(查进度)· `remote_cancel`(取消)|
178
+ | **审批** | `remote_task` 的 `approval: 'manual'` 会挂起 → 面板或工具批准/拒绝 |
179
+ | **结果** | `remote_handoff`(结果卡)· `remote_manifest`(产物清单)· `remote_pull`(拉成品)· `remote_purge`(清理)|
180
+ | **文件** | `remote_ls` · `remote_read` · `remote_grep` · `remote_tree` |
181
+
182
+ ---
183
+
96
184
  ## 核心不变式
97
185
 
98
186
  这几条是这个包的设计前提,改动前请先读:
@@ -110,7 +198,7 @@ node bin/dsh-remote-setup.js --doctor 你的配置.yml
110
198
 
111
199
  ## 安装
112
200
 
113
- ### 主机(hub)
201
+ ### 主机(hub)—— 装完即用
114
202
 
115
203
  ```bash
116
204
  # 通过 DSH 插件安装器(GitHub 源)
@@ -122,42 +210,58 @@ npm install -g dsh-remote-agent
122
210
 
123
211
  或在 DSH 的插件对话框里填 `github:zbzbhs/dsh-remote-agent`。
124
212
 
125
- 装完在 `cordis.patch.yml` 里确认这一行存在(包内 patch 会自动插入):
213
+ **装完重启 DSH 就能用** —— 不需要改任何配置:
126
214
 
127
- ```yaml
128
- - id: remote-agent
129
- name: dsh-remote-agent
130
- inject: ['webServer', 'tools', 'systemPrompt']
131
- config:
132
- role: hub
133
- ```
215
+ - 包内 patch 默认 `role: auto`。**没配 `hubUrl` 就是主机**,自动生效。
216
+ - 包内 patch 把 `webserver` 绑到 `0.0.0.0:19387`,**副机才能连上**
217
+ (DSH 默认只绑 `127.0.0.1`,那样副机永远连不上且主机侧毫无异常)。
218
+ - 打开 Web 面板「远程同事」,点「副机接入」生成配对码,发给副机。
219
+
220
+ > **安全披露**:绑 `0.0.0.0` 意味着 **DSH 的整个 Web GUI 也能被局域网访问**
221
+ > (不只是本插件的节点端点)。DSH 自带 Host/Origin 围栏 + 浏览器 cookie 鉴权,
222
+ > 但暴露面确实变大了。如果你不需要跨机访问,可以在 profile 的
223
+ > `cordis.patch.yml` 里覆盖回 `127.0.0.1` —— 但那样副机就连不上,
224
+ > 请改用 [中继模式](profiles/README.md)。
225
+ >
226
+ > 面板的「体检」按钮会明确告诉你当前绑在哪(面板加载时也会自动体检一次)。
134
227
 
135
- ### 副机(worker)
228
+ ### 副机(worker)—— 贴个配对码就行
136
229
 
137
- **同一份包**,但配置不同 —— 关键是**必须加 `inject: []`**:
230
+ **同一份包**,装完重启 DSH,然后:
231
+
232
+ 1. 打开 Web 面板「远程同事」
233
+ 2. 把主机生成的 `dshra1://…` 整行**粘进「配对码」框**(自动解析地址与 token)
234
+ 3. 工作区已预填(Windows `D:\work`,其他 `~/work`),**目录不存在会自动创建**
235
+ 4. 点「确认,写入配置」→ 重启 DSH
236
+
237
+ **为什么贴完就自动变副机**:`role` 默认 `auto`,判据是"配了 `hubUrl` 就是副机"。
238
+ 配对码展开后必然产出 `hubUrl`,所以贴码 = 自动切到 worker。
239
+
240
+ **节点 ID 不用填** —— 默认取主机名。想自定义就点「默认用主机名(点此自定义)」展开。
241
+
242
+ 如果你想手工写 YAML(不推荐,但可以):
138
243
 
139
244
  ```yaml
140
245
  - id: remote-agent
141
246
  name: dsh-remote-agent
142
- inject: [] # ← 副机必加,否则永不激活(见下方说明)
247
+ inject: ['tools', 'agentLoop', 'sessions'] # ← 副机必须用这三个
143
248
  config:
144
249
  role: worker
145
- nodeId: slave-pc
146
- transportMode: direct
147
- hubUrl: 'ws://<主机地址>:19387/api/remote-agent/node'
148
- token: '<与主机 nodeToken 相同,或留空>'
149
- roots: ['D:\work'] # ← 改成副机真实存在的目录
250
+ pair: 'dshra1://eyJ2IjoxLC…' # ← 主机面板输出的整行
251
+ roots: ['D:\work'] # ← 副机真实存在的目录(会自动创建)
150
252
  ```
151
253
 
152
- **嫌抄地址容易错?用配对码** —— 主机向导会输出一行 `dshra1://…`(内含
153
- 地址与 token),副机配置里 `transportMode/hubUrl/token` 三行换成一行:
154
-
155
- ```yaml
156
- config:
157
- role: worker
158
- pair: 'dshra1://eyJ2IjoxLC…' # ← 主机向导输出的整行
159
- roots: ['D:\work']
160
- ```
254
+ > **为什么副机的 `inject` 和主机不同**
255
+ >
256
+ > `inject` 决定**这个插件能看见哪些 DSH 服务**,而 agent 的 ctx 继承插件
257
+ > 的依赖声明 —— 少了 `tools`,副机的 agent 就**没有任何 shell/文件工具**,
258
+ > 任务会"跑完了但什么都没干"。
259
+ >
260
+ > - hub 需要 `webServer`(注册路由)+ `tools` + `systemPrompt`
261
+ > - worker 需要 `tools` + `agentLoop` + `sessions`(起 agent 会话)
262
+ >
263
+ > 面板的「切换角色」会自动写对这一个字段,**不用自己记**。
264
+ > (历史坑:早期文档教用户写 `inject: []`,那是错的 —— 会导致 agent 无工具。)
161
265
 
162
266
  > **配对码的两条规矩**
163
267
  >
@@ -176,14 +280,6 @@ npm install -g dsh-remote-agent
176
280
  > 停止重连(确认主动换 hub 后删 `hub-pin.json` 重启即可重新钉住)。
177
281
  > 详见 [SECURITY.md](SECURITY.md) 检查清单第 7 条。
178
282
 
179
- > **为什么副机要写 `inject: []`**
180
- >
181
- > 包内 `cordis.patch.yml` 给这一行声明了
182
- > `inject: ['webServer','tools','systemPrompt']` —— 那是 **hub 需要的服务**。
183
- > 副机 profile 里**没有**这些服务,不清空的话这一行会一直停在 **PENDING**
184
- > **永远不激活**,副机也就永远不拨入。loader 按 id 覆盖,所以写 `inject: []`
185
- > 就覆盖掉了包内的声明。
186
-
187
283
  **接入路径**(双方都在 NAT 后 / 一方有公网 / 隧道)见
188
284
  **[profiles/README.md](profiles/README.md)** —— 那里有每条路径的配置片段与验证命令,
189
285
  以及一条**降级链**:能直连走 direct,不通就降级到中继 VPS 或 cloudflared 隧道;
@@ -279,6 +375,16 @@ ack 自动按明文兼容旧中继继续),安全边界与已知局限见
279
375
 
280
376
  ---
281
377
 
378
+
379
+ ### 想用本地 IDE 打开副机的文件?(SMB 共享)
380
+
381
+ 面板上有「工作区共享(SMB)」区:把副机的工作区共享出去,主机
382
+ `net use X: \\副机\dsh-work-...` 映射成盘符,就能用本地 VS Code 直接打开。
383
+
384
+ > **⚠ 这会绕过本插件的安全边界**(`roots` / `denyPaths` 失效、中间产物与
385
+ > 密钥对主机可见、新增凭证面)。需要**二次确认**,且默认**只读**。
386
+ > 完整风险与用法见 **[docs/workspace-share.md](docs/workspace-share.md)**。
387
+
282
388
  ## 安全
283
389
 
284
390
  **这个插件让主机能在另一台电脑上读写文件、执行命令。** 部署前请读
@@ -295,10 +401,11 @@ ack 自动按明文兼容旧中继继续),安全边界与已知局限见
295
401
 
296
402
  | 路径 | 作用 |
297
403
  |---|---|
298
- | `lib/index.js` | 插件入口(角色分发器:`role: hub` / `worker` / `both`) |
299
- | `lib/hub/**` | 主机侧:节点注册表、7 个工具、subagent provider、UI 路由 |
404
+ | `lib/index.js` | 插件入口(角色分发器:`role: auto` / `hub` / `worker` / `both`) |
405
+ | `lib/hub/**` | 主机侧:节点注册表、**12 个工具**、subagent provider、UI 路由、环境体检 |
300
406
  | `lib/hub/views.js` | **投影共享层** —— 工具与 UI 用同一份字段白名单 |
301
- | `lib/node/**` | 副机侧:拨入、任务引擎、产物服务、exec/agent 双后端 |
407
+ | `lib/node/**` | 副机侧:拨入、任务引擎、产物服务、exec/agent 双后端、SMB 共享 |
408
+ | `lib/env-check.js` | 环境体检(命令行与面板共用一份逻辑) |
302
409
  | `lib/client.js` | 浏览器半边(Web 面板) |
303
410
  | `lib/protocol.js` | 线协议(帧类型、错误码、`PROTOCOL_VERSION`) |
304
411
  | `lib/e2e.js` | 中继链路端到端加密(X25519 握手 + ed25519 认证 + AES-256-GCM 帧封) |
package/cordis.patch.yml CHANGED
@@ -84,4 +84,51 @@
84
84
  name: dsh-remote-agent
85
85
  inject: ['webServer', 'tools', 'systemPrompt']
86
86
  config:
87
- role: ''
87
+ # 默认 auto:**按配置推断角色**(2026-10-17,"开箱即用"的落点)。
88
+ #
89
+ # · 没配 hubUrl → hub(主机)→ 主机装完零操作 ✅
90
+ # · 配了 hubUrl → worker(副机)→ 副机贴完配对码零操作 ✅
91
+ #
92
+ # 为什么不用写死的 hub:副机装完会以 hub 身份启动,注册一个 nodeToken
93
+ # 为空、不校验凭证的直连端点 —— 那是实打实的攻击面(v0.7.0 之前的老问题)。
94
+ #
95
+ # 为什么不用写死的 '':两台机器都要操作一次,不是开箱即用;而且升级时
96
+ # 包内默认容易把 profile 里没显式写的 role 冲成空(实测踩过)。
97
+ #
98
+ # 判据见 lib/index.js 的 resolveRoleDetail。
99
+ role: auto
100
+
101
+ # ═══════════════════════════════════════════════════════════════════════════
102
+ # 让 hub 真的能被外部访问(2026-10-17,**这是"开箱即用"的关键一条**)
103
+ # ═══════════════════════════════════════════════════════════════════════════
104
+ #
105
+ # ── 为什么必须改 webserver ──────────────────────────────────────────────
106
+ #
107
+ # 本插件的副机要主动连主机:`ws://<主机IP>:19387/api/remote-agent/node`。
108
+ # 而那个 WebSocket upgrade 是注册在 DSH 的 `ctx.webServer` 上的
109
+ # (`ctx.webServer.registerUpgrade(...)`)—— **没有独立端口选项**。
110
+ #
111
+ # 而 DSH 的 `@deepseek-ai/dsh-host-webserver` 的 `host` 是必填项、无默认值,
112
+ # 且它面向的默认场景是"本机浏览器访问自己",所以多数 profile 绑 127.0.0.1。
113
+ #
114
+ # 结果:不覆盖这一行,hub 只监听 loopback → **副机永远连不上**,而主机侧
115
+ # 毫无异常(healthz 200、面板能开、工具都在)。用户只能猜"地址错了?
116
+ # token 错了?防火墙?" —— 2026-10-17 实测踩到,代价是好几轮排查。
117
+ #
118
+ # ── 安全披露(必须让用户知道)──────────────────────────────────────────
119
+ #
120
+ # 绑 0.0.0.0 意味着 **DSH 的整个 Web GUI 也能被局域网访问**(不只是我们的
121
+ # 节点端点)。DSH 自带 Host/Origin 围栏 + 浏览器 cookie 鉴权,但暴露面确实
122
+ # 变大了。所以:
123
+ # · README 与面板都有明确提示
124
+ # · 环境体检把这条列出来
125
+ # · 用户可在 profile 里覆盖回 127.0.0.1(此时请改用 relay 模式,否则副机连不上)
126
+ #
127
+ # ── 优先级 ──────────────────────────────────────────────────────────────
128
+ #
129
+ # `cordis.yml` 头注释:应用顺序是 bundles → cordis.patch.yml → --patch overlays。
130
+ # 所以**用户 profile 里的同名行会覆盖这里**(用户优先,符合预期)。
131
+ - id: webserver
132
+ config:
133
+ host: '0.0.0.0'
134
+ port: 19387
@@ -0,0 +1,141 @@
1
+ # 工作区共享(SMB)—— 用本地 IDE 打开副机的文件
2
+
3
+ > **一句话**:把副机的一个工作区目录共享出去,主机映射成盘符 `X:`,就能用
4
+ > 本地 VS Code / 资源管理器直接打开副机的项目。
5
+
6
+ ---
7
+
8
+ ## ⚠️ 先读这段:这会绕过本插件的安全边界
9
+
10
+ 本插件其他所有功能都遵守三条不变式:
11
+
12
+ | 不变式 | 正常情况 | **开启共享后** |
13
+ |---|---|---|
14
+ | `roots` 是安全边界 | 所有文件访问先 realpath 展开,再与 roots 前缀比对 | **失效** —— Windows 内核直接读写,插件拦不住 |
15
+ | `denyPaths` 生效 | 敏感路径在枚举与读取时被拒 | **失效** —— 同上 |
16
+ | 清单先于字节 | 只有 `role:final` 的产物能过河 | **失效** —— 中间产物、日志、`.env`、`.git` 里的密钥全部对主机可见 |
17
+ | 凭证不离开副机 | 协议里不带模型 key | **新增凭证面** —— 主机需要副机的 Windows 账号 |
18
+
19
+ **所以请只共享工作区目录,不要共享整个盘。**
20
+
21
+ 共享是 **Windows SMB** —— 意味着:
22
+
23
+ - 走 SMB 协议(**不是**本插件的 E2E 加密通道)
24
+ - 仅建议在**可信局域网**或 **Tailscale** 上使用
25
+ - 跨公网直接用 = 把目录暴露给互联网,**不要这么做**
26
+
27
+ ---
28
+
29
+ ## 怎么用
30
+
31
+ ### 1. 面板上开启
32
+
33
+ 1. 打开「远程同事」面板 → 找到「工作区共享(SMB)」区
34
+ 2. 选副机
35
+ 3. 选权限:
36
+ - **只读**(推荐)—— 主机能看、不能改
37
+ - **读写** —— 主机能看能改(会多一次警告)
38
+ 4. 勾选「我知道上面的风险,确认开启共享」
39
+ 5. 点「生成共享命令」
40
+
41
+ ### 2. 在**副机**上执行(需要管理员)
42
+
43
+ 插件**不会**代你执行 —— 因为 `New-SmbShare` 需要管理员权限,而 DSH 通常
44
+ 以普通用户运行。Node 里提权的失败表现(静默失败 / 被安全软件拦 / 权限
45
+ 半生效)比让你手动跑一条命令难排查得多。
46
+
47
+ **在副机上开一个管理员 PowerShell**,粘贴面板给的命令:
48
+
49
+ ```powershell
50
+ New-SmbShare -Name 'dsh-work-a1b2c3d4' -Path 'D:\work' -ReadAccess "$env:USERNAME"
51
+ ```
52
+
53
+ - `-ReadAccess` = 只读
54
+ - `-ChangeAccess` = 读写
55
+ - 共享给当前用户(`$env:USERNAME`)
56
+
57
+ ### 3. 在**主机**上映射
58
+
59
+ 普通 PowerShell 即可:
60
+
61
+ ```powershell
62
+ net use X: \\<副机IP>\dsh-work-a1b2c3d4 /persistent:yes
63
+ ```
64
+
65
+ **副机 IP 怎么拿**:
66
+
67
+ - 同一局域网 → `ipconfig` 看局域网 IP
68
+ - 跨网(Tailscale)→ 副机上跑 `tailscale ip -4`
69
+
70
+ 执行后资源管理器里出现 `X:` 盘,就是副机的 `D:\work`。
71
+
72
+ ---
73
+
74
+ ## 关闭共享
75
+
76
+ 面板上生成卸载命令,在**副机**管理员 PowerShell 执行:
77
+
78
+ ```powershell
79
+ Remove-SmbShare -Name 'dsh-work-a1b2c3d4' -Force
80
+ ```
81
+
82
+ 主机侧取消映射:
83
+
84
+ ```powershell
85
+ net use X: /delete
86
+ ```
87
+
88
+ ---
89
+
90
+ ## 常见问题
91
+
92
+ ### 主机连不上 `\\副机IP\共享名`
93
+
94
+ 按顺序查:
95
+
96
+ 1. **副机防火墙** —— 放行 445 端口:
97
+ ```powershell
98
+ netsh advfirewall firewall add rule name="SMB" dir=in action=allow protocol=TCP localport=445
99
+ ```
100
+ 2. **两台机器能互通** —— `ping <副机IP>`
101
+ 3. **凭证** —— 主机用副机的 Windows 账号密码(或在副机建一个专用共享账号)
102
+ 4. **SMB 服务在跑** —— 副机上 `Get-Service LanmanServer` 应为 `Running`
103
+
104
+ ### 共享名为什么是 `dsh-work-a1b2c3d4` 这种怪名字
105
+
106
+ 共享名由工作区路径**稳定哈希**生成,这样:
107
+
108
+ - 同一个目录反复开关共享 → 共享名不变(不会留孤儿共享)
109
+ - 不同目录 → 不同共享名(可以同时共享多个)
110
+ - 固定 `dsh-work-` 前缀 → 便于识别与清理(不会误删你的其它共享)
111
+
112
+ ### 能不能共享整个盘
113
+
114
+ **技术上可以**(`New-SmbShare -Path 'D:\'`),但**强烈不建议**:
115
+
116
+ - 整个盘包含用户配置、浏览器数据、SSH 私钥、`.env` 文件
117
+ - 主机一旦被入侵,副机整盘暴露
118
+
119
+ 插件通过面板生成的命令只会用 `roots` 里的目录 —— 那是你配的安全边界。
120
+
121
+ ### 为什么插件不自己执行 `New-SmbShare`
122
+
123
+ 三个理由:
124
+
125
+ 1. **需要管理员** —— DSH 通常不是管理员
126
+ 2. **Node 提权不可靠** —— 弹 UAC 需要额外依赖,且被安全软件拦时表现为静默失败
127
+ 3. **失败难排查** —— 手动跑一条命令失败时你能看到真实错误
128
+
129
+ ---
130
+
131
+ ## 与 relay 模式的关系
132
+
133
+ **互不影响**:
134
+
135
+ - relay 模式解决"两台机器都在 NAT 后怎么连上"
136
+ - SMB 共享解决"连上之后怎么用本地 IDE 打开文件"
137
+
138
+ 但注意 **SMB 不走 relay** —— 它是 Windows 直连。所以:
139
+
140
+ - 跨网用 Tailscale + SMB → 可以(Tailscale 提供网络层)
141
+ - 只有 relay 没有 Tailscale → **SMB 用不了**(relay 只转发本插件的协议帧)