@twinsearth/w2m-dsh-plugin 2.0.3 → 2.0.5

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.
Files changed (72) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +17 -0
  4. package/README.md +187 -462
  5. package/bin/w2m-joint.mjs +69 -7
  6. package/codex/mcp-servers.toml +1 -1
  7. package/docs/acceptance/2.0.4/RELEASE-2040.json +22 -0
  8. package/docs/acceptance/2.0.5/RELEASE-205.json +21 -0
  9. package/docs/acceptance/2.0.5/RELEASE-2050.json +22 -0
  10. package/docs/adr/0001-collaboration-crdt.md +59 -0
  11. package/lib/client.js +2754 -2599
  12. package/package.json +2 -2
  13. package/plugin.json +1 -1
  14. package/src/agent/agent.mjs +97 -36
  15. package/src/agent/artifact-delivery.mjs +89 -0
  16. package/src/agent/caps.mjs +16 -2
  17. package/src/agent/sinks/git-sink.mjs +78 -0
  18. package/src/agent/sinks/ssh-sink.mjs +83 -0
  19. package/src/agent/sse-cursor.mjs +16 -0
  20. package/src/agent/sse-watchdog.mjs +71 -0
  21. package/src/plugin/dataplane-sink-route.mjs +113 -0
  22. package/src/plugin/fleet-chat-route.mjs +11 -0
  23. package/src/plugin/gpw.mjs +99 -15
  24. package/src/plugin/joint-dev/dispatch.mjs +5 -1
  25. package/src/plugin/joint-dev/merge-approval.mjs +115 -0
  26. package/src/plugin/joint-dev/progress.mjs +107 -0
  27. package/src/plugin/joint-dev/relay-dispatch.mjs +52 -22
  28. package/src/plugin/project-stream-route.mjs +58 -0
  29. package/src/plugin/tools-joint.mjs +6 -1
  30. package/src/plugin/tools.mjs +25 -5
  31. package/src/relay/fleet-messages.mjs +48 -24
  32. package/src/relay/project-room.mjs +47 -2
  33. package/src/relay/server.mjs +11 -5
  34. package/src/relay/state.mjs +63 -3
  35. package/src/util/chat-crypto.mjs +70 -1
  36. package/docs/COLLAB/COLLABORATION-MODEL.md +0 -240
  37. package/docs/COLLAB/EXECUTION-AUTH-MODEL.md +0 -361
  38. package/docs/COLLAB/M1-ACCEPTANCE-RUNBOOK.md +0 -70
  39. package/docs/COLLAB/M1-MAINTENANCE.md +0 -313
  40. package/docs/COLLAB/M2-TASK-GRANT.md +0 -231
  41. package/docs/COLLAB/M3-GIT-NATIVE-TASKS.md +0 -291
  42. package/docs/COLLAB/M4-PROJECT-ROOM.md +0 -260
  43. package/docs/COLLAB/M5-MULTI-CLIENT-PRIMITIVES.md +0 -263
  44. package/docs/COLLAB/M6-CROSS-NETWORK-RELIABILITY.md +0 -231
  45. package/docs/COLLAB/M7-METRICS-ACCEPTANCE.md +0 -263
  46. package/docs/COLLAB/TASK-MODEL.md +0 -281
  47. package/docs/acceptance/1.9.10/RELEASE-1910.json +0 -21
  48. package/docs/acceptance/1.9.11/RELEASE-1911.json +0 -20
  49. package/docs/acceptance/1.9.12/RELEASE-1912.json +0 -20
  50. package/docs/acceptance/1.9.13/RELEASE-1913.json +0 -20
  51. package/docs/acceptance/1.9.14/RELEASE-1914.json +0 -20
  52. package/docs/acceptance/1.9.15/RELEASE-1915.json +0 -22
  53. package/docs/acceptance/1.9.16/RELEASE-1916.json +0 -21
  54. package/docs/acceptance/1.9.17/RELEASE-1917.json +0 -20
  55. package/docs/acceptance/1.9.19/RELEASE-1919.json +0 -21
  56. package/docs/acceptance/1.9.20/RELEASE-1920.json +0 -22
  57. package/docs/acceptance/1.9.4/RELEASE-194.json +0 -23
  58. package/docs/acceptance/1.9.5/RELEASE-195.json +0 -23
  59. package/docs/acceptance/1.9.6/RELEASE-196.json +0 -22
  60. package/docs/acceptance/1.9.7/RELEASE-197.json +0 -21
  61. package/docs/acceptance/1.9.8/RELEASE-198.json +0 -21
  62. package/docs/acceptance/1.9.9/RELEASE-199.json +0 -21
  63. package/lib/client.js.bak2 +0 -640
  64. package/src/plugin/dataflow.mjs +0 -72
  65. package/src/plugin/fleet-target.mjs.bak-arch2 +0 -812
  66. package/src/plugin/fleet-target.mjs.bak-archive +0 -812
  67. package/src/plugin/tofuture/report.mjs +0 -44
  68. package/src/plugin/tools.mjs.bak-c7 +0 -7029
  69. package/src/relay/tofuture-whitelist.mjs +0 -46
  70. package/src/util/data-plane.mjs +0 -180
  71. package/src/util/sealed-object.mjs +0 -141
  72. package/src/util/tofuture-hmac.mjs +0 -30
package/README.md CHANGED
@@ -1,462 +1,187 @@
1
- # DSH: Windows2MacOS
2
-
3
- **One DeepSeek account. One instruction. Every machine you own runs the same project.**
4
-
5
- [![CI](https://github.com/TwinsEarth/dsh-windows2macos/actions/workflows/ci.yml/badge.svg)](https://github.com/TwinsEarth/dsh-windows2macos/actions/workflows/ci.yml)
6
- [![npm](https://img.shields.io/npm/v/@twinsearth/w2m-dsh-plugin?label=npm)](https://www.npmjs.com/package/@twinsearth/w2m-dsh-plugin)
7
- [![DSH plugin](https://img.shields.io/badge/DSH-plugin-4c6ef5)](#1-dsh-plugin-recommended--zero-configuration)
8
- [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
9
- [![platforms](https://img.shields.io/badge/CI-windows%20%7C%20macos%20%7C%20linux-4c6ef5)](#verified-and-not-verified)
10
-
11
- English | [中文](#中文说明)
12
-
13
- ---
14
-
15
- ## Install
16
-
17
- Five routes in. They all end with **18 tools registered** in your agent. Pick one.
18
-
19
- ### 1. DSH plugin (recommended — zero configuration)
20
-
21
- ```bash
22
- # 1. install the package into your DSH profile
23
- dsh plugin add @twinsearth/w2m-dsh-plugin
24
-
25
- # 2. name it in the profile's bundle list — a package that is not bundled never loads
26
- # ~/.dsh/profiles/<profile>/dsh.profile.yml
27
- plugins:
28
- - '@twinsearth/w2m-dsh-plugin'
29
-
30
- # 3. restart DSH; joining happens at mount time, so a hot reload is not enough
31
- ```
32
-
33
- Since **1.1.15** the agent starts by itself (`autoStartAgent` defaults to `true`): a freshly installed machine
34
- derives its account identity, joins the account's fleet, and shows up in `w2m_devices` with no pairing step.
35
- To turn that off, patch the **existing** `w2m` row — do not add a second one:
36
-
37
- ```yaml
38
- # cordis.patch.yml
39
- - id: w2m
40
- config:
41
- autoStartAgent: false
42
- ```
43
-
44
- ### 2. npm (MCP server, any client)
45
-
46
- ```bash
47
- npm install -g @twinsearth/w2m-dsh-plugin
48
- w2m-mcp --help
49
- ```
50
-
51
- Point any MCP client at `w2m-mcp`. **Zero runtime dependencies**; the package ships the relay, the agent and the
52
- tools.
53
-
54
- ### 3. Claude Code
55
-
56
- ```bash
57
- /plugin marketplace add TwinsEarth/dsh-windows2macos
58
- /plugin install w2m@twinsearth
59
- ```
60
-
61
- The manifests live in [`.claude-plugin/`](.claude-plugin/) and are version-locked to the release.
62
-
63
- ### 4. Codex and other MCP-capable CLIs
64
-
65
- ```toml
66
- # copy this block into your own codex/mcp-servers.toml
67
- [mcp_servers.w2m]
68
- command = "npx"
69
- args = ["-y", "@twinsearth/w2m-dsh-plugin@1.1.19", "w2m-mcp"]
70
- ```
71
-
72
- ### 5. Verified release tarball (offline)
73
-
74
- From the `v1.1.19` release page download **both** files — `twinsearth-w2m-dsh-plugin-1.1.19.tgz` and `SHA256SUMS` —
75
- then:
76
-
77
- ```bash
78
- curl -LO https://github.com/TwinsEarth/dsh-windows2macos/releases/download/v1.1.19/twinsearth-w2m-dsh-plugin-1.1.19.tgz
79
- sha256sum -c SHA256SUMS # verify before installing, always
80
- npm install -g ./twinsearth-w2m-dsh-plugin-1.1.19.tgz
81
- ```
82
-
83
- Every release carries `SHA256SUMS` for exactly this reason. If a release is missing it, do not install that release.
84
-
85
- ### 6. Beta (prerelease — never on npm)
86
-
87
- A beta is a **GitHub prerelease**, not an npm version. Nothing about it is resolved by `npm install`, and `latest` never
88
- points at one. It exists for a developer who has been granted `beta-access` and accepts that it can break.
89
-
90
- ```bash
91
- # 1. find the newest prerelease (a beta is marked "Pre-release" on the releases page)
92
- # https://github.com/TwinsEarth/dsh-windows2macos/releases -> latest entry labelled Pre-release
93
- # 2. download BOTH files attached to it: twinsearth-w2m-dsh-plugin-<version>.tgz and SHA256SUMS
94
- sha256sum -c SHA256SUMS # verify before installing, always
95
- npm install -g ./twinsearth-w2m-dsh-plugin-<version>.tgz
96
- ```
97
-
98
- - **Access**: the beta workflow waits on the protected `beta-access` environment, so a reviewer must approve each run
99
- before the artifact exists at all.
100
- - **Risk acknowledgment**: install it only if you have read [SECURITY.md](SECURITY.md) and accept that a beta may fail
101
- where a stable release does not.
102
- - **Going back**: `npm install -g @twinsearth/w2m-dsh-plugin@latest` returns you to the stable channel.
103
- - **Why not npm**: there is no `npm publish` line in the beta workflow. A channel cannot leak into the default install if
104
- the publication cannot happen at all.
105
-
106
-
107
- ---
108
-
109
- ## Quick start
110
-
111
- ```text
112
- w2m_status # am I paired? which fleet?
113
- w2m_devices # what is in the fleet?
114
- w2m_run mode="replicate" command_argv=["git","rev-parse","HEAD"]
115
- w2m_wait task_id="..."
116
- ```
117
-
118
- `replicate` runs on every online machine; the verdict is `consistent` only when every machine returned the same
119
- result **and** the same commit anchor. `split` gives each machine a slice, `pipeline` chains stages, `broadcast`
120
- names one executor and reports observers, `compose` merges results.
121
-
122
- ---
123
-
124
- ## What it is
125
-
126
- An installable plugin that turns a set of machines into **one fleet**, keyed by **your account**, not by a
127
- hand-maintained host list.
128
-
129
- | Piece | Responsibility |
130
- |---|---|
131
- | **Relay** | a small HTTP server: task routing, leases, SSE, blob storage. Run it anywhere your machines can reach. |
132
- | **Agent** | runs on each machine: pairs once, executes allow-listed commands, heartbeats. |
133
- | **Plugin** | the tools your agent calls. |
134
- | **Admission** (1.1.14+) | a new machine asks to join; an online member approves, **10 s auto-approves**, and a machine admitted in the last 24 h can be removed immediately. |
135
-
136
- ### Tools
137
-
138
- `w2m_status` · `w2m_devices` · `w2m_run` · `w2m_wait` · `w2m_history` · `w2m_stats` · `w2m_report` ·
139
- `w2m_memory` · `w2m_approvals` · `w2m_cancel` · `w2m_council` · `w2m_chat` · `w2m_update` `w2m_tasks` · `w2m_reduce` · `w2m_joint_plan` · `w2m_joint_status`
140
- - `w2m_intent` — map a structured intent (run-tests, node-version, git-status, git-head, fleet-facts, shard-probe) onto an allow-listed command; unknown intents are refused by name, never guessed.
141
-
142
- ### Environment a dispatched command receives (1.1.15+)
143
-
144
- Values come from the offer, never from argv, so one machine cannot reshape a fleet-wide split.
145
-
146
- | Variable | Meaning |
147
- |---|---|
148
- | `W2M_INDEX` / `W2M_INDEX_TOTAL` | this machine's slice number and how many slices exist |
149
- | `W2M_STAGE_INDEX` | the pipeline stage; deliberately separate from `W2M_INDEX` |
150
- | `W2M_TASK_ID` / `W2M_MODE` | the task and its mode |
151
- | `W2M_MACHINE_ID` / `W2M_MACHINE_NAME` | this machine |
152
-
153
- So `command_argv=["node","-e","process.exit(Number(process.env.W2M_INDEX)+1)"]` over four machines exits 1/2/3/4.
154
- Single-machine behaviour is tested; the four-machine run is listed as **not verified** below.
155
-
156
- ---
157
-
158
- ## Fleet model
159
-
160
- Identity is **the account**, not a list of hosts. A machine derives an Ed25519 identity from its account
161
- material; the relay registers the matching public key when the account's first machine pairs; every later machine
162
- proves the same account with a one-time signed challenge.
163
-
164
- If the old pairing path fails for a **named** reason only — invalid pairing code, unreachable relay, timeout — the
165
- machine falls back to asking for admission. Any other failure needs a human and does **not** change mechanisms. A
166
- rejection is final; the machine does not re-ask.
167
-
168
- The relay is authoritative for every time window and every quorum, because client clocks disagree (measured:
169
- tens of seconds between machines in one fleet):
170
-
171
- - **10 s** without a decision auto-approves, but only while at least one member is **online** — an empty fleet
172
- cannot consent, and that refusal is named (`NO_ONLINE_MEMBER`);
173
- - within **24 h** of admission, any online member may remove the machine immediately;
174
- - after 24 h a single machine may only **open a vote**, whose quorum set is **frozen at open time** so a voter
175
- going offline mid-vote cannot move the bar;
176
- - the quorum is 3+ old members need **≥ 50 %** (rounded up); a two-member fleet needs **one** vote;
177
- - a machine offline for more than **72 hours** leaves the **list** — it is never deleted, its records stay, and it
178
- returns by reporting again.
179
-
180
- ---
181
-
182
- ## Security posture
183
-
184
- - The command allow-list is **argv-prefix** based and rejects argv-reparsing flags (`--import`, appended `-e`) with
185
- `COMMAND_REPARSE_ARGUMENT`. It is a boundary, not a sandbox.
186
- - Dispatching tasks requires an operator token. A device token cannot dispatch.
187
- - Admission approval requires at least one member online.
188
- - The account signing key is derived from the fleet key, **never** from the account code — the account code is
189
- written to logs, and deriving a key from a readable value would hand out the account identity.
190
- - Every release is checksummed.
191
-
192
- ---
193
-
194
- ## Verified and not verified
195
-
196
- A green badge that hides this is worse than no badge.
197
-
198
- **Verified:** 2458 tests in 102 files (2444 pass, 14 skip), 15 pre-flight checks, CI green on Windows, macOS and
199
- Linux, npm + GitHub release + checksums consistent, end-to-end fleet runs on a real Linux/Mac/Windows triple.
200
-
201
- **Not verified / known gaps:**
202
-
203
- | Gap | State |
204
- |---|---|
205
- | Approval **popup** control | the relay enforces the rules and `w2m_approvals` · `w2m_cancel` lists and settles requests, but the 10 s countdown is relay data shown in a list, not a dedicated popup |
206
- | Four-machine `W2M_INDEX` run | single-machine behaviour tested; the fleet-wide 1/2/3/4 run has not been executed |
207
- | Fleet chat / broadcast channel | message store + quota implemented and tested; encryption, sharding, endpoints and the panel are **not built** |
208
- | P2P on some macOS hosts | `P2P_PUNCH_FAILED: Unknown cipher` observed; the task falls back to the relay path |
209
-
210
- ---
211
-
212
- ## Troubleshooting
213
-
214
- | Symptom | Cause | Fix |
215
- |---|---|---|
216
- | installed, but `w2m_devices` answers 401 | the agent never started, so no device token | check `autoStartAgent` is not `false`; restart DSH (not a hot reload) |
217
- | machine missing from the fleet | the plugin is not in `dsh.profile.bundles` | add it — a package that is not bundled never loads |
218
- | `COMMAND_NOT_ALLOWED` | command not in the allow-list | extend the allow-list; argv is matched by prefix |
219
- | verdict always `partial` | a stale lease from a machine that is down | fixed in 1.1.2: a dispatch leases only online, non-silent machines |
220
- | `P2P_PUNCH_FAILED` | NAT, or a host crypto gap | it downgrades to the relay path; results are unaffected |
221
-
222
- ---
223
-
224
- ## Release channels
225
-
226
- Three channels, and only one of them is what \
227
- pm install\ gives you.
228
-
229
- | Channel | Where it comes from | Who should use it |
230
- |---|---|---|
231
- | **stable** | npm \latest\ — a version the developer declared stable | everyone; this is the default install |
232
- | **beta** | a GitHub **prerelease** (tarball + \SHA256SUMS\), **never on npm** | developers granted \eta-access\ who accept that it can fail |
233
- | **latest build** | the newest of the two, whatever its channel | a developer who wants the newest code and knows the above |
234
-
235
- A bump is a **beta by default**. npm receives a version only when the developer says, in so many words, that this one is a
236
- stable release: a beta is published as a prerelease (so neither GitHub nor npm ever resolves it as \latest\), it is
237
- downloadable only after the \eta-access\ environment approves the run, and installing it requires reading
238
- [SECURITY.md](SECURITY.md) and accepting the risk.
239
-
240
- There is no \
241
- pm publish\ line in the beta workflow. That is deliberate — a channel cannot leak to the default install if
242
- the publication cannot happen at all.
243
- ---
244
-
245
- ## Links
246
-
247
- - [CHANGELOG](CHANGELOG.md) — every version, including what it deliberately did **not** ship
248
- - [PLATFORMS](docs/PLATFORMS.md) — Claude Code, Codex and MCP wiring details
249
- - [Releases](https://github.com/TwinsEarth/dsh-windows2macos/releases) — per-release evidence and checksums
250
- - [Issues](https://github.com/TwinsEarth/dsh-windows2macos/issues)
251
-
252
- MIT.
253
-
254
- ---
255
-
256
- ## 中文说明
257
-
258
- **一个 DeepSeek 账号。一句话。你自己的每一台机器跑同一个项目。**
259
-
260
- 以下内容与上方英文**逐节对应**,不是摘要。
261
-
262
- ### 安装
263
-
264
- 五种方式,最终都会在你的 agent 里注册 **18 tools registered**。任选其一。
265
-
266
- #### 1. DSH 插件(推荐,零配置)
267
-
268
- ```bash
269
- # 1. 把包装进你的 DSH profile
270
- dsh plugin add @twinsearth/w2m-dsh-plugin
271
-
272
- # 2. 在 profile 的 bundle 列表里写上包名 —— 没进 bundle 的包永远不会被加载
273
- # ~/.dsh/profiles/<profile>/dsh.profile.yml
274
- plugins:
275
- - '@twinsearth/w2m-dsh-plugin'
276
-
277
- # 3. 重启 DSH。加入编队发生在 mount 时,热重载不够
278
- ```
279
-
280
- 从 **1.1.15** 起 agent 会自动启动(`autoStartAgent` 默认 `true`):新装的机器自行派生账号身份、加入该账号的
281
- 编队,无需配对步骤就会出现在 `w2m_devices` 里。若要关闭,请 patch **已存在的** `w2m` 行,**不要新增第二行**:
282
-
283
- ```yaml
284
- # cordis.patch.yml
285
- - id: w2m
286
- config:
287
- autoStartAgent: false
288
- ```
289
-
290
- #### 2. npm(MCP 服务器,任意客户端)
291
-
292
- ```bash
293
- npm install -g @twinsearth/w2m-dsh-plugin
294
- w2m-mcp --help
295
- ```
296
-
297
- 把任意 MCP 客户端指向 `w2m-mcp`。**零运行时依赖**;包里同时带有 relay、agent 与工具。
298
-
299
- #### 3. Claude Code
300
-
301
- ```bash
302
- /plugin marketplace add TwinsEarth/dsh-windows2macos
303
- /plugin install w2m@twinsearth
304
- ```
305
-
306
- 清单文件位于 [`.claude-plugin/`](.claude-plugin/),版本与发行版锁定。
307
-
308
- #### 4. Codex 及其它支持 MCP 的 CLI
309
-
310
- ```toml
311
- # 把这段复制进你自己的 codex/mcp-servers.toml
312
- [mcp_servers.w2m]
313
- command = "npx"
314
- args = ["-y", "@twinsearth/w2m-dsh-plugin@1.1.19", "w2m-mcp"]
315
- ```
316
-
317
- #### 5. 校验过的发布包(离线)
318
-
319
- 从 `v1.1.19` 的 release 页面下载**两个**文件 —— `twinsearth-w2m-dsh-plugin-1.1.19.tgz` 与 `SHA256SUMS` —— 然后:
320
-
321
- ```bash
322
- curl -LO https://github.com/TwinsEarth/dsh-windows2macos/releases/download/v1.1.19/twinsearth-w2m-dsh-plugin-1.1.19.tgz
323
- sha256sum -c SHA256SUMS # 安装前务必校验
324
- npm install -g ./twinsearth-w2m-dsh-plugin-1.1.19.tgz
325
- ```
326
-
327
- 每个 release 都带 `SHA256SUMS` 正是为了这一步。若某个 release 缺少它,**不要安装该版本**。
328
-
329
- #### 6. Beta(预发布版 —— 不进 npm)
330
-
331
- Beta 是 **GitHub prerelease**,不是 npm 版本:`npm install` 永远拿不到它,`latest` 也不会指向它。它是给**已获
332
- `beta-access` 授权、且接受它可能出问题**的开发者用的。
333
-
334
- ```bash
335
- # 1. 在 releases 页面找标着 Pre-release 的最新条目
336
- # https://github.com/TwinsEarth/dsh-windows2macos/releases
337
- # 2. 下载该条目的两个文件:twinsearth-w2m-dsh-plugin-<版本>.tgz 与 SHA256SUMS
338
- sha256sum -c SHA256SUMS # 安装前务必校验
339
- npm install -g ./twinsearth-w2m-dsh-plugin-<版本>.tgz
340
- ```
341
-
342
- - **权限**:beta 流水线挂在受保护的 `beta-access` 环境上,**每次运行都需要审批者批准**,产物才会存在;
343
- - **风险确认**:安装前请阅读 [SECURITY.md](SECURITY.md),并接受 beta 可能比稳定版更容易失败;
344
- - **回退**:`npm install -g @twinsearth/w2m-dsh-plugin@latest` 即回到稳定版通道;
345
- - **为什么不走 npm**:beta 工作流里**没有 `npm publish`** —— 只要发布动作不可能发生,这个通道就漏不进默认安装。
346
-
347
-
348
- ### 快速开始
349
-
350
- ```text
351
- w2m_status # 我配对了吗?在哪个编队?
352
- w2m_devices # 编队里有什么?
353
- w2m_run mode="replicate" command_argv=["git","rev-parse","HEAD"]
354
- w2m_wait task_id="..."
355
- ```
356
-
357
- `replicate` 在每一台在线机器上执行;只有当所有机器返回**相同结果**且**相同 commit 锚点**时,裁决才是
358
- `consistent`。`split` 给每台机器一个分片,`pipeline` 串联多个阶段,`broadcast` 指定一个执行者并汇报观察者,
359
- `compose` 合并结果。
360
-
361
- ### 它是什么
362
-
363
- 一个可安装插件,把一组机器变成**一个编队**,以**你的账号**为键,而不是靠手工维护的主机清单。
364
-
365
- | 组件 | 职责 |
366
- |---|---|
367
- | **Relay** | 一个小型 HTTP 服务:任务路由、租约、SSE、blob 存储。放在任何机器可达的位置。 |
368
- | **Agent** | 每台机器上运行:配对一次、执行白名单命令、心跳。 |
369
- | **Plugin** | 你的 agent 调用的那些工具。 |
370
- | **Admission**(1.1.14+) | 新机器申请加入;在线成员批准,**10 秒自动同意**;入队 24 小时内的机器可被立即移除。 |
371
-
372
- ### 工具
373
-
374
- `w2m_status` · `w2m_devices` · `w2m_run` · `w2m_wait` · `w2m_history` · `w2m_stats` · `w2m_report` ·
375
- `w2m_memory` · `w2m_approvals` · `w2m_cancel` · `w2m_council` · `w2m_chat` · `w2m_update` `w2m_tasks`
376
-
377
- ### 被派发命令会收到的环境变量(1.1.15+)
378
-
379
- 取值来自 offer,**不来自 argv**,因此单台机器无法改变整个编队的分片:`W2M_INDEX` / `W2M_INDEX_TOTAL` /
380
- `W2M_STAGE_INDEX` / `W2M_TASK_ID` / `W2M_MODE` / `W2M_MACHINE_ID` / `W2M_MACHINE_NAME`。
381
-
382
- 因此 `command_argv=["node","-e","process.exit(Number(process.env.W2M_INDEX)+1)"]` 在四台机器上应分别退出
383
- 1/2/3/4。**单机行为已测;四台机器的联跑尚未验证**(见下方"未验证")。
384
-
385
- ### 编队模型
386
-
387
- 身份是**账号**,不是主机清单。机器从账号材料派生 Ed25519 身份;账号的第一台机器配对时,relay 登记对应公钥;
388
- 此后每台机器用一次性签名挑战自证同一个账号。
389
-
390
- 只有当旧配对路径因**明确原因**失败时 —— 配对码无效、relay 不可达、超时 —— 机器才降级去申请入队。其它失败需要
391
- 人工介入,**不会**切换机制。**拒绝即终态**,机器不会再次申请。
392
-
393
- 所有时间窗与法定人数由 relay 权威判定,因为客户端时钟互不一致(实测:同一编队内相差数十秒):
394
-
395
- - 无决策 **10 秒**后自动同意,但**仅当至少一名成员在线** —— 空编队无法同意,该拒绝有明确错误码
396
- (`NO_ONLINE_MEMBER`);
397
- - 入队后 **24 小时内**,任一在线成员可立即移除该机器;
398
- - 超过 24 小时,单台机器只能**发起表决**,且法定人数集合在**发起时冻结**,投票者中途离线无法改变门槛;
399
- - 法定人数:旧成员 ≥3 人需 **≥50%(向上取整)**;2 人编队的移除只要 **1 票**;
400
- - 离线超过 **72 小时**的机器会离开**列表** —— 记录**永不删除**,它重新上报后会自动回来。
401
-
402
- ### 安全姿态
403
-
404
- - 命令白名单按 **argv 前缀**匹配,并拒绝 argv 重解析标志(`--import`、追加 `-e`),错误码
405
- `COMMAND_REPARSE_ARGUMENT`。这是边界,不是沙箱。
406
- - 派发任务需要 operator token;device token 不能派发。
407
- - 审批入队要求至少一名成员在线。
408
- - 账户签名密钥从 **fleet key** 派生,**绝不**用 account code —— account code 会写进日志,用可读值派生密钥等于
409
- 交出账号身份。
410
- - 每个发行版都有校验和。
411
-
412
- ### 已验证与未验证
413
-
414
- **一句真话胜过一块掩盖问题的绿牌。**
415
-
416
- **已验证:** 102 个文件中的 2458 项测试(2444 通过、14 跳过)、15 项门禁检查、Windows / macOS / Linux 三平台
417
- CI 全绿、npm 与 GitHub 发布及校验和一致、在真实的 Linux/Mac/Windows 三机编队上跑过端到端。
418
-
419
- **未验证 / 已知缺口:**
420
-
421
- | 缺口 | 状态 |
422
- |---|---|
423
- | 审批**弹窗**控件 | relay 强制规则、`w2m_approvals` · `w2m_cancel` 可列出并裁决,但 10 秒倒计时是列表里的 relay 数据,不是独立弹窗 |
424
- | 四台机器的 `W2M_INDEX` 联跑 | 单机行为已测;编队级 1/2/3/4 尚未执行 |
425
- | 编队聊天 / 广播通道 | 消息存储与配额已实现并测试;加密、分片、端点与面板**尚未构建** |
426
- | 部分 macOS 主机的 P2P | 观察到 `P2P_PUNCH_FAILED: Unknown cipher`;任务会降级走 relay 路径 |
427
-
428
- ### 故障排查
429
-
430
- | 症状 | 原因 | 处理 |
431
- |---|---|---|
432
- | 装好了,但 `w2m_devices` 返回 401 | agent 从未启动,因此没有 device token | 确认 `autoStartAgent` 不是 `false`;**重启 DSH**(热重载无效) |
433
- | 编队里看不到这台机器 | 插件没写进 `dsh.profile.bundles` | 加进去 —— 不在 bundle 里的包永远不会加载 |
434
- | `COMMAND_NOT_ALLOWED` | 命令不在白名单 | 扩充白名单;argv 按前缀匹配 |
435
- | 裁决总是 `partial` | 来自已下线机器的陈旧租约 | 1.1.2 已修:派发只对在线且未沉默的机器建租约 |
436
- | `P2P_PUNCH_FAILED` | NAT,或宿主密码学能力缺口 | 会自动降级到 relay 路径,结果不受影响 |
437
-
438
- ### 发布通道(中文)
439
-
440
- 三个通道,其中只有一个是你 \
441
- pm install\ 默认拿到的。
442
-
443
- | 通道 | 来源 | 适合谁 |
444
- |---|---|---|
445
- | **stable** | npm \latest\,开发者明确声明为稳定版 | 所有人;默认安装就是它 |
446
- | **beta** | GitHub **prerelease**(tarball + \SHA256SUMS\),**绝不进 npm** | 被授予 \eta-access\、且接受它可能失败的开发者 |
447
- | **最新构建** | 两者中版本较新的那个,不论通道 | 想要最新代码、并理解上述含义的开发者 |
448
-
449
- **默认的 bump 是 beta**。只有开发者明确说明"这次是稳定版",npm 才会收到该版本:beta 以 prerelease 形式发布(因此
450
- GitHub 与 npm 都不会把它解析成 \latest\),只有在 \eta-access\ 环境审批通过后才能下载,安装前需阅读
451
- [SECURITY.md](SECURITY.md) 并自担风险。
452
-
453
- beta 工作流里**没有 \
454
- pm publish\ 这一行**,这是刻意的 —— 只要发布动作不可能发生,这个通道就不可能泄漏到默认安装。
455
- ### 链接
456
-
457
- - [CHANGELOG](CHANGELOG.md) —— 每个版本,包括它**故意没有**交付的内容
458
- - [PLATFORMS](docs/PLATFORMS.md) —— Claude Code、Codex 与 MCP 的接线细节
459
- - [Releases](https://github.com/TwinsEarth/dsh-windows2macos/releases) —— 每个版本的证据与校验和
460
- - [Issues](https://github.com/TwinsEarth/dsh-windows2macos/issues)
461
-
462
- MIT。
1
+ # DSH: Windows2MacOS
2
+
3
+ **One DeepSeek account. One instruction. Every machine you own runs the same project.**
4
+
5
+ [![CI](https://github.com/TwinsEarth/dsh-windows2macos/actions/workflows/ci.yml/badge.svg)](https://github.com/TwinsEarth/dsh-windows2macos/actions/workflows/ci.yml)
6
+ [![npm](https://img.shields.io/npm/v/@twinsearth/w2m-dsh-plugin?label=npm)](https://www.npmjs.com/package/@twinsearth/w2m-dsh-plugin)
7
+ [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
8
+
9
+ English | [中文说明](#中文说明)
10
+
11
+ ---
12
+
13
+ ## 六大核心功能
14
+
15
+ ### 功能 1 · 同一账号的 PC,登录自动编队入队
16
+ 同一 DeepSeek 账号下的每台机器,登录即自动编队入队,无需手动配对、无需维护主机列表。从 v1.1.15 起 `autoStartAgent` 默认开启:新装的机器自行派生账号身份、加入该账号的编队,直接出现在 `w2m_devices` 中。身份以账号为键,而非手维护的 host 列表;每台机器用 Ed25519 密钥证明同一账号归属。
17
+
18
+ ### 功能 2 · 编队聊天框
19
+ 编队内建 `w2m_chat` 聊天通道,同一编队的机器之间可直接对话、广播消息;composer 内的聊天面板与「编队群组」标签页共享同一份消息记录。消息存储与配额已实现并测试。工具路径可使用编队密钥派生的会话加密;面板端到端加密、分片仍未构建,面板/会话镜像走 relay 时仍是明文。
20
+
21
+ ### 功能 3 · 编队联合开发 & 多机跨机实时协同协作
22
+ 不止“跑同一条命令”。`w2m_joint_plan` / `w2m_joint_status` 负责联合任务规划与状态跟踪,`w2m_run` 提供 replicate(并行对比)、split(分片执行)、pipeline(流水线)、broadcast(单机广播)四种协作模式,`w2m_council` 多机协商、`w2m_tasks` 任务面板把一组异构机器变成同一个开发编队。联合开发按《W2M Relay × ToFuture 外部对接 API v1.2》收敛:Ed25519 人签审批、`target_machines` 显式派发、409 读回、死租约拒绝;数据面投递(artifact sink)与合并 CLI 仍在接线中,relay 部署独立于插件发布。
23
+
24
+ ### 功能 4 · 一键到顶:单击滚动三轮,连续点击三次到顶
25
+ 为长会话和长日志场景设计的滚动浮标:单击一次向上滚动约三轮;1.2 秒内连续点击三次直接回到第一轮(自动展开 DSH 官方「加载更早」)。图标位于会话区右上角,与官方浮动控件同一设计规格,不依赖窗口标题栏。
26
+
27
+ ### 功能 5 · 正文上下文搜索:命中文字绿字黄底高亮
28
+ 在主工作区进行正文上下文搜索,命中的文字以黄色背景、绿色文字高亮显示,当前匹配项自动滚动进入视野;搜索覆盖正文内容与上下文,不局限于标题或元数据。
29
+
30
+ ### 功能 6 · 全局文件夹读写黑白名单:保护系统盘 & 个人/工作文件夹
31
+ DSH 默认的权限模型是“限制 or 全开”,一旦放开就难以精细控制。本插件在官方沙箱之上追加全局文件夹读写黑白名单,为系统盘(`C:\Windows`、`Program Files`、`/etc`、`/usr` 等)和个人/工作文件夹建立明确的读/写边界,避免 AI 或跨机任务误触不该动的路径。这一设计来自实际重装系统后的教训:粗粒度的“全开”代价太高,黑白名单把“绝对不允许”与“经常要放行”的路径变成可配置的硬规则。v2.0.4 起提供两种策略:**小白模式(默认)**只保留黑名单强制拒绝(内置 Windows/Program Files 等默认保护文件夹,路径操作每 3 秒最多自动放行一次),其余全部放行;**高级模式**需手动切换,保留白名单预算、待审批队列与 10 秒无人应答自动放行。
32
+
33
+ ## 安装
34
+
35
+ Every install path below registers 18 tools in your agent.
36
+
37
+ 五种方式,最终都会在你的 agent 里注册 18 个工具。任选其一。
38
+
39
+ ### 1. DSH 插件(推荐,零配置)
40
+
41
+ ```bash
42
+ # 1. 把包装进你的 DSH profile
43
+ dsh plugin add @twinsearth/w2m-dsh-plugin
44
+
45
+ # 2. 在 profile 的 bundle 列表里写上包名 —— 没进 bundle 的包永远不会被加载
46
+ # ~/.dsh/profiles/<profile>/dsh.profile.yml
47
+ plugins:
48
+ - '@twinsearth/w2m-dsh-plugin'
49
+
50
+ # 3. 重启 DSH。加入编队发生在 mount 时,热重载不够
51
+ ```
52
+
53
+ 从 1.1.15 起 agent 会自动启动(`autoStartAgent` 默认 true):新装的机器自行派生账号身份、加入该账号的编队,无需配对步骤就会出现在 `w2m_devices` 里。若要关闭:
54
+
55
+ ```yaml
56
+ # cordis.patch.yml
57
+ - id: w2m
58
+ config:
59
+ autoStartAgent: false
60
+ ```
61
+
62
+ ### 2. npm(MCP 服务器,任意客户端)
63
+
64
+ ```bash
65
+ npm install -g @twinsearth/w2m-dsh-plugin
66
+ w2m-mcp --help
67
+ ```
68
+
69
+ 把任意 MCP 客户端指向 `w2m-mcp`。零运行时依赖;包里同时带有 relay、agent 与工具。
70
+
71
+ ### 3. Claude Code
72
+
73
+ ```bash
74
+ /plugin marketplace add TwinsEarth/dsh-windows2macos
75
+ /plugin install w2m@twinsearth
76
+ ```
77
+
78
+ 清单文件位于 `.claude-plugin/`,版本与发行版锁定。
79
+
80
+ ### 4. Codex 及其他支持 MCP 的 CLI
81
+
82
+ ```toml
83
+ # 复制进你自己的 codex/mcp-servers.toml
84
+ [mcp_servers.w2m]
85
+ command = "npx"
86
+ args = ["-y", "@twinsearth/w2m-dsh-plugin@2.0.3", "w2m-mcp"]
87
+ ```
88
+
89
+ ### 5. 验证过的 release tarball(离线)
90
+
91
+ 从 v2.0.3 release 页下载 `twinsearth-w2m-dsh-plugin-2.0.3.tgz` 与 `SHA256SUMS`,然后:
92
+
93
+ ```bash
94
+ curl -LO https://github.com/TwinsEarth/dsh-windows2macos/releases/download/v2.0.3/twinsearth-w2m-dsh-plugin-2.0.3.tgz
95
+ sha256sum -c SHA256SUMS # 先验证,永远先验证
96
+ npm install -g ./twinsearth-w2m-dsh-plugin-2.0.3.tgz
97
+ ```
98
+
99
+ 每个 release 都附带 `SHA256SUMS`,缺失的 release 不要安装。
100
+
101
+ ## Quick start
102
+
103
+ ```bash
104
+ w2m_status # 我配对了吗?在哪个编队?
105
+ w2m_devices # 编队里有什么机器?
106
+ w2m_run mode="replicate" command_argv=["git","rev-parse","HEAD"]
107
+ w2m_wait task_id="..."
108
+ ```
109
+
110
+ replicate 在每台在线机器上运行,只有所有机器返回相同结果且相同 commit 锚点时,判定才是 consistent。split 给每台机器一个分片,pipeline 串联阶段,broadcast 指定一个执行者并报告观察者,compose 合并结果。
111
+
112
+ ## What it is
113
+
114
+ 一个可安装插件,把一组机器变成一个编队,以你的账号为键,而不是手维护的主机列表。
115
+
116
+ | 组件 | 职责 |
117
+ |---|---|
118
+ | Relay | 小型 HTTP 服务器:任务路由、租约、SSE。不承载数据面对象;运行在机器可达的任何地方 |
119
+ | Agent | 每台机器上运行:配对一次、执行白名单命令、心跳 |
120
+ | Plugin | 你的 agent 调用的工具 |
121
+ | Admission (1.1.14+) | 新机器请求加入;在线成员批准,10 秒自动批准,24 小时内加入的机器可立即移除 |
122
+
123
+ ## Tools
124
+
125
+ `w2m_status` · `w2m_devices` · `w2m_run` · `w2m_wait` · `w2m_history` · `w2m_stats` · `w2m_report` · `w2m_memory` · `w2m_approvals` · `w2m_cancel` · `w2m_council` · `w2m_chat` · `w2m_update` · `w2m_tasks` · `w2m_reduce` · `w2m_joint_plan` · `w2m_joint_status` · `w2m_intent`
126
+
127
+ `w2m_intent` 把结构化意图(run-tests、node-version、git-status、git-head、fleet-facts、shard-probe)映射到白名单命令;未知意图按名字拒绝,绝不猜测。
128
+
129
+ ## Security posture
130
+
131
+ 命令白名单基于 argv 前缀,拒绝 argv 重解析标志(`--import`、附加 `-e`),命中时返回 `COMMAND_REPARSE_ARGUMENT`。它是边界,不是沙箱。
132
+
133
+ 派发任务需要 operator token;设备 token 不能派发。
134
+ 加入批准要求至少一个成员在线。
135
+ 账号签名密钥从编队密钥派生,从不从账号代码派生——账号代码会写入日志,从可读值派生密钥等于交出账号身份。
136
+ 每个 release 都有校验和。
137
+
138
+ ## Verified and not verified
139
+
140
+ 一个隐藏这些事实的绿标比没有标更糟。
141
+
142
+ Verified:preflight 16 项发布门禁通过;CI 在 Windows、macOS、Linux 上通过;npm + GitHub release + 校验和一致;联合开发计划/审批/派发路径有单元测试与本地 relay 测试覆盖。
143
+
144
+ Known gaps:
145
+
146
+ | Gap | State |
147
+ |---|---|
148
+ | 真机 A–G 执行证据 | 仅完成 A/B/C/D 的路由冒烟;E/F/G 与真实执行未执行(目标机命令白名单默认拒绝、三机版本需对齐) |
149
+ | 联合开发数据面与合并 | ArtifactRef/dir-sink 与 merge-engine 已有库与单测,但 agent 投递与合并 CLI 尚未接线 |
150
+ | 进度视图 | relay 主树有 `progress` 视图;线上 relay 仍是 1.9.16,未部署该代码,插件侧消费端也在接线中 |
151
+ | 编队聊天 / 广播通道 | 消息存储 + 配额已实现并测试;工具路径会话加密可用;面板端到端加密、分片尚未构建 |
152
+ | 某些 macOS 主机上的 P2P | `P2P_PUNCH_FAILED`;任务回落到 relay 路径 |
153
+ | relay 部署 | relay 升级独立于插件发布;线上版本以 `/healthz` 为准 |
154
+
155
+ ## 中文说明
156
+
157
+ 一个 DeepSeek 账号。一句话。你自己的每一台机器跑同一个项目。
158
+
159
+ ### 六大核心功能
160
+
161
+ 1. **同一账号登录自动编队入队**:新机器自动派生身份、加入编队,出现在 `w2m_devices`,无需手动配对。
162
+ 2. **编队聊天框**:`w2m_chat` 打通编队内沟通,消息存储与配额已就绪;composer 面板与「编队群组」标签共享记录。
163
+ 3. **编队联合开发 & 多机跨机实时协同**:`w2m_joint_plan` / `w2m_joint_status` + 四种协作模式 + `w2m_council`,把异构机器变成同一个开发编队;审批与派发按 Relay API v1.2 收敛。
164
+ 4. **一键到顶**:单击滚三轮;1.2 秒内连续点击三次直接回到第一轮(自动展开「加载更早」)。
165
+ 5. **正文上下文搜索**:主工作区搜索,命中文字绿字黄底高亮,当前项自动滚入视野。
166
+ 6. **全局文件夹读写黑白名单**:保护系统盘和个人/工作文件夹,把“限制 or 全开”变成可配置的精细边界。
167
+
168
+ ### 安装(DSH 插件)
169
+
170
+ ```bash
171
+ dsh plugin add @twinsearth/w2m-dsh-plugin
172
+ # 在 ~/.dsh/profiles/<profile>/dsh.profile.yml 中加入:
173
+ plugins:
174
+ - '@twinsearth/w2m-dsh-plugin'
175
+ # 重启 DSH
176
+ ```
177
+
178
+ 其余四种安装方式与上方英文一致。
179
+
180
+ ## Links
181
+
182
+ - [CHANGELOG](./CHANGELOG.md) — 每个版本,包括它刻意没有交付的东西
183
+ - [PLATFORMS](./PLATFORMS.md) — Claude Code、Codex 与 MCP 接线细节
184
+ - [Releases](https://github.com/TwinsEarth/dsh-windows2macos/releases) — 每个 release 的证据与校验和
185
+ - [Issues](https://github.com/TwinsEarth/dsh-windows2macos/issues)
186
+
187
+ MIT.