@twinsearth/w2m-dsh-plugin 0.8.2 → 0.8.4
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/CHANGELOG.md +387 -0
- package/package.json +1 -1
- package/src/plugin/tools.mjs +4878 -4622
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,393 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.8.9] — 2026-10-14
|
|
8
|
+
|
|
9
|
+
**The account answers lazily, and we asked once.**
|
|
10
|
+
|
|
11
|
+
### The defect
|
|
12
|
+
|
|
13
|
+
`getPlatformSession().userId` repeats the stable account id **from the most recent successful
|
|
14
|
+
`getProfile`**, and is `null` until one has happened. The account profile is a **lazy cache**, and
|
|
15
|
+
`startLocalStack` reads it **at mount** — before any model call has touched the profile.
|
|
16
|
+
|
|
17
|
+
So on a signed-in machine the first read returns *signed out*, the fleet key is never derived, no
|
|
18
|
+
pairing happens, and **nothing is written**. The evidence that pins it is `device.json`'s **mtime**:
|
|
19
|
+
unchanged since the first boot, so `saveDeviceToken` was not merely ineffective, it was **never
|
|
20
|
+
reached**.
|
|
21
|
+
|
|
22
|
+
**That also explains an earlier result that made no sense at the time:** v0.8.4 *did* pair once,
|
|
23
|
+
because by then the profile happened to be cached. **A timing-dependent success is the worst kind to
|
|
24
|
+
debug — the code is right and the outcome still varies.**
|
|
25
|
+
|
|
26
|
+
### v0.8.7 made it worse, without meaning to
|
|
27
|
+
|
|
28
|
+
*"Already paired → do not pair again"* turned an **intermittent miss into a permanent one**. A
|
|
29
|
+
machine that missed at startup never retried, and re-pairing had been the only thing rescuing it.
|
|
30
|
+
The rate-limit fix was correct and exposed a defect it did not create.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
Derivation now **backs off and retries** (6 attempts, 2s doubling to a 30s cap) until a real key
|
|
35
|
+
appears or the budget runs out.
|
|
36
|
+
|
|
37
|
+
### And the silent branch that hid it
|
|
38
|
+
|
|
39
|
+
`resolve()` on the **stored-SN path** returns `ok: true` with `fleetKey: null`. The old code logged
|
|
40
|
+
only when `ok === false` — so the shape that was actually firing **produced no output anywhere**.
|
|
41
|
+
|
|
42
|
+
That is the same *silent no-op* class fixed in 0.8.5, reappearing **in the exact branch that was
|
|
43
|
+
failing**. Every outcome is now named:
|
|
44
|
+
|
|
45
|
+
| shape | reported as |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `ok: true`, key present | `fleet key derived … on attempt N (fleet …)` |
|
|
48
|
+
| **`ok: true`, key null** | **`signed in, but the account returned no fleet key (the profile may not be loaded yet)`** |
|
|
49
|
+
| `ok: false` | the reason and detail, verbatim |
|
|
50
|
+
| budget exhausted | **`this machine is in NO fleet and will receive no work`** |
|
|
51
|
+
|
|
52
|
+
A machine in no fleet must never be quiet about it.
|
|
53
|
+
|
|
54
|
+
### Verified
|
|
55
|
+
|
|
56
|
+
**174 cases**. `tools.mjs` loads; the describer was exercised against all five shapes including the
|
|
57
|
+
one that previously said nothing.
|
|
58
|
+
|
|
59
|
+
## [0.8.8] — 2026-10-14
|
|
60
|
+
|
|
61
|
+
**A machine that joined without saying what it is — and a fix that was computed and never used.**
|
|
62
|
+
|
|
63
|
+
### `platform` and `caps` were never sent
|
|
64
|
+
|
|
65
|
+
`createAgent` accepts `platform` and `caps`, and `bin/w2m-localside.mjs` has always supplied them.
|
|
66
|
+
`startLocalStack` did not, so a machine that joined **through the plugin** appeared in the relay
|
|
67
|
+
complete and correct except for:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
"os": null, "arch": null, "caps": {}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**The capability gate then had nothing to filter on — and that gate decides what work a machine is
|
|
74
|
+
offered.** So the device was registered, reachable, and unusable for anything the gate would have
|
|
75
|
+
matched it to.
|
|
76
|
+
|
|
77
|
+
Same shape as the rest of this series: the record was created and **the part that makes it useful was
|
|
78
|
+
not filled in**.
|
|
79
|
+
|
|
80
|
+
### The v0.8.5 fix was computed and never used
|
|
81
|
+
|
|
82
|
+
Worse, and found while making the change above:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const resolvedStateDir = cfg.stateDir.trim() !== '' ? cfg.stateDir.trim() : resolveStateDir(...);
|
|
86
|
+
if (cfg.stateDir !== resolvedStateDir) { ctx.logger?.info?.(`w2m: stateDir was unset; using ...`); }
|
|
87
|
+
...
|
|
88
|
+
const agent = await createAgent({ stateDir: cfg.stateDir, ... }); // ← the EMPTY string
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**v0.8.5 computed the resolved directory, logged it, and then passed the original empty string to
|
|
92
|
+
`createAgent`** — the exact call that throws `createAgent: stateDir is required`. So the second gate
|
|
93
|
+
v0.8.5 claimed to open was **still shut**, and the only evidence was a log line saying the resolved
|
|
94
|
+
path was being used.
|
|
95
|
+
|
|
96
|
+
*The log said the right thing while the code did the wrong thing* — which is worse than silence,
|
|
97
|
+
because silence sends you looking and a confident wrong log sends you away.
|
|
98
|
+
|
|
99
|
+
Both `createRelay` and `createAgent` now receive `resolvedStateDir`, and its definition was moved
|
|
100
|
+
**above** its first use: as a `const` used earlier in the same block it would have thrown
|
|
101
|
+
`ReferenceError: Cannot access 'resolvedStateDir' before initialization` at runtime, which
|
|
102
|
+
`node --check` cannot see because it is not a syntax error. Caught by checking the **order**, not the
|
|
103
|
+
syntax.
|
|
104
|
+
|
|
105
|
+
### Also sent now
|
|
106
|
+
|
|
107
|
+
`identityPath: loaded.path` — the device key belongs **beside the `device.json` this process actually
|
|
108
|
+
loaded**, not in a directory the agent guesses. Without it the in-process and standalone paths can
|
|
109
|
+
mint separate keypairs under one `machine_id` and overwrite each other's announcement (the v0.6.2
|
|
110
|
+
defect, which this path would have reintroduced).
|
|
111
|
+
|
|
112
|
+
### Verified
|
|
113
|
+
|
|
114
|
+
**174 cases**. `tools.mjs` loads; the ordering was confirmed by comparing line numbers rather than by
|
|
115
|
+
reading.
|
|
116
|
+
|
|
117
|
+
## [0.8.7] — 2026-10-14
|
|
118
|
+
|
|
119
|
+
**Pair only when there is no token — because the relay rate-limits pairing.**
|
|
120
|
+
|
|
121
|
+
### The defect
|
|
122
|
+
|
|
123
|
+
v0.8.6 made the token persist; it still **paired on every start**. The fleet key is derived
|
|
124
|
+
unconditionally, so `pairBy` was never null and the pairing branch always ran.
|
|
125
|
+
|
|
126
|
+
The relay allows `pair_rate_limit: 5` per minute. **Five restarts inside a minute hit the limit — and
|
|
127
|
+
a rate-limit refusal reads as a pairing failure, which reads as a fleet problem.** The symptom would
|
|
128
|
+
have pointed at the network, the relay, or the account, none of which would be at fault.
|
|
129
|
+
|
|
130
|
+
A machine that already holds a token has nothing to gain from pairing again: the relay would re-issue
|
|
131
|
+
one and invalidate the old, for no reason.
|
|
132
|
+
|
|
133
|
+
### Fixed
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
const existingToken = loaded.identity.device_token;
|
|
137
|
+
const alreadyPaired = typeof existingToken === 'string' && existingToken !== '';
|
|
138
|
+
|
|
139
|
+
if (alreadyPaired) {
|
|
140
|
+
ctx.logger?.info?.(`w2m: already paired (token in ${loaded.path}); not pairing again`);
|
|
141
|
+
} else if (pairBy !== null) {
|
|
142
|
+
// pair once, then persist
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**This also makes the persisted token load-bearing rather than an optimisation.** Without it every
|
|
147
|
+
start is a fresh pairing — which is why *the missing write in v0.8.5* and *the unconditional pairing
|
|
148
|
+
here* are **the same defect seen from two sides**: one threw the token away, the other had to
|
|
149
|
+
re-create it every time.
|
|
150
|
+
|
|
151
|
+
### A note on how this was found
|
|
152
|
+
|
|
153
|
+
Reported, with reasoning and a rate-limit number from the relay's own configuration — not from a
|
|
154
|
+
failing test. The distinction matters for what it implies: **nothing in the suite would have caught
|
|
155
|
+
it**, because every case starts from a fresh state and none of them restart the plugin in a loop.
|
|
156
|
+
|
|
157
|
+
### Verified
|
|
158
|
+
|
|
159
|
+
**174 cases** across the tools, apply, account and fleet-join suites.
|
|
160
|
+
|
|
161
|
+
## [0.8.6] — 2026-10-14
|
|
162
|
+
|
|
163
|
+
**The pairing worked and the token was thrown away.**
|
|
164
|
+
|
|
165
|
+
### The defect
|
|
166
|
+
|
|
167
|
+
`startLocalStack` called `agent.pair(...)` and **never persisted the result**. `agent.pair` sets
|
|
168
|
+
`identity.device_token` on its **in-memory object only**, so the observable behaviour was:
|
|
169
|
+
|
|
170
|
+
1. the account-derived key is computed ✅
|
|
171
|
+
2. the relay **accepts** the pair and registers the device ✅
|
|
172
|
+
3. the machine appears in `w2m_devices` ✅
|
|
173
|
+
4. **every authenticated call then answers `401 UNAUTHORIZED`** — `w2m_devices`, the P2P announce,
|
|
174
|
+
all of it — because `device.json` still said `device_token: null` ❌
|
|
175
|
+
|
|
176
|
+
It reads as a permission failure and is **a missing write**.
|
|
177
|
+
|
|
178
|
+
The evidence that settles it is the shape of the value: not an empty string, not a truncated token —
|
|
179
|
+
**`null`**. A write that failed would leave the previous value; a write that never happened leaves
|
|
180
|
+
what the identity was created with.
|
|
181
|
+
|
|
182
|
+
### Why the CLI never had this bug
|
|
183
|
+
|
|
184
|
+
`bin/w2m-localside.mjs` has always done it:
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
const paired = await agent.pair(flags.pair ?? null, undefined, fleetKey);
|
|
188
|
+
saveDeviceToken(paired.device_token, {}); // ← the CLI persists it
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**The in-process path was written without that line.** So the two paths diverged in exactly the way
|
|
192
|
+
that produces a symptom in one and not the other — which is why it looked like a relay or permission
|
|
193
|
+
problem rather than a missing statement.
|
|
194
|
+
|
|
195
|
+
### Fixed
|
|
196
|
+
|
|
197
|
+
`saveDeviceToken(paired.device_token, identityOptions)` now runs after a successful pair, using the
|
|
198
|
+
**same options object** that loaded the identity. Reading with one set of options and writing with
|
|
199
|
+
another is how the plugin and the CLI once ended up each minting a keypair under one `machine_id`
|
|
200
|
+
(the v0.6.2 defect), so the options are captured once and used for both.
|
|
201
|
+
|
|
202
|
+
A failure to persist is **named at warn level**, including what it means: *"this machine will answer
|
|
203
|
+
401 to every authenticated call until that is fixed."* That exact condition was silent once already.
|
|
204
|
+
|
|
205
|
+
### The pattern this is the third instance of
|
|
206
|
+
|
|
207
|
+
* v0.8.4 wired the **derivation** but the gate in front of it was closed.
|
|
208
|
+
* v0.8.5 fixed the **second gate** (`stateDir`) but not the write behind it.
|
|
209
|
+
* v0.8.6 fixes the **write**.
|
|
210
|
+
|
|
211
|
+
Each time, the action was connected and its **persistence or precondition** was not. Worth stating
|
|
212
|
+
plainly because it is the shape of every bug in this series: **the thing that makes the result
|
|
213
|
+
survive is the thing that gets forgotten.**
|
|
214
|
+
|
|
215
|
+
### Verified
|
|
216
|
+
|
|
217
|
+
**174 cases** across the tools, apply, account and fleet-join suites.
|
|
218
|
+
|
|
219
|
+
## [0.8.5] — 2026-10-14
|
|
220
|
+
|
|
221
|
+
**Two gates, not one — and the silence that hid both.**
|
|
222
|
+
|
|
223
|
+
### The second gate
|
|
224
|
+
|
|
225
|
+
v0.8.4's analysis said the join sequence was blocked by `autoStartAgent` defaulting to false. That
|
|
226
|
+
was right and incomplete: **an unset `stateDir` threw as well**, so flipping the switch would have
|
|
227
|
+
failed too.
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
stateDir=empty string -> THREW: createAgent: stateDir is required
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`createAgent` rejects a falsy `stateDir`; the plugin's resolved default is the empty string; and
|
|
234
|
+
`startLocalStack` passed it straight through. **Two gates in series, and the analysis found the one
|
|
235
|
+
in front.**
|
|
236
|
+
|
|
237
|
+
`stateDir` now resolves to a real path (`$DSH_HOME/xclient/localside`) when unset, resolved in
|
|
238
|
+
`startLocalStack` so `w2m_status` reports the path actually used — which is what makes *"where did it
|
|
239
|
+
write `device.json`"* answerable rather than a guess.
|
|
240
|
+
|
|
241
|
+
### The silence, which is the real defect
|
|
242
|
+
|
|
243
|
+
A machine could run v0.8.4, hold a valid signed-in account, reach the relay over **HTTP 200**, and
|
|
244
|
+
be in **no fleet** — with nothing anywhere reporting a problem. The eight tools registered, the
|
|
245
|
+
plugin looked healthy, and the join sequence simply never ran.
|
|
246
|
+
|
|
247
|
+
**A silent no-op that looks like success is the worst failure mode this plugin has had**, because
|
|
248
|
+
every diagnostic signal a person would reach for says the system is fine. The evidence that finally
|
|
249
|
+
settled it was an *absence* — no `device.json`, no `xclient`, no listening ports — and an absence
|
|
250
|
+
is only readable once you know what should have been there.
|
|
251
|
+
|
|
252
|
+
So the disabled branch now says so, at warn level, naming what it means and how to change it:
|
|
253
|
+
|
|
254
|
+
> `w2m: no agent is running on this machine, so it is in no fleet and will receive no work. Set autoStartAgent: true to join …`
|
|
255
|
+
|
|
256
|
+
One line turns *"why is this machine not in my fleet"* from an investigation into a log search.
|
|
257
|
+
|
|
258
|
+
### A test that measured a count instead of a message
|
|
259
|
+
|
|
260
|
+
`tools.test.mjs` asserted `warnings.length === 1` for the `dsh-tools` shim warning — a claim about
|
|
261
|
+
**every** warning the plugin will ever emit, so the new one broke a test about something else
|
|
262
|
+
entirely. It now matches the message it is about. **The count was never the property.**
|
|
263
|
+
|
|
264
|
+
### What is still required, stated plainly
|
|
265
|
+
|
|
266
|
+
The switch does not default to on, deliberately: a control machine that only *dispatches* to others
|
|
267
|
+
has no reason to be in the fleet itself. So joining requires `autoStartAgent: true` **and** a
|
|
268
|
+
`w2m` row in the profile — and the profile having no such row is the state every fresh install
|
|
269
|
+
starts in.
|
|
270
|
+
|
|
271
|
+
### Verified
|
|
272
|
+
|
|
273
|
+
**174 cases** across the tools, apply, account and fleet-join suites.
|
|
274
|
+
|
|
275
|
+
## [0.8.4] — 2026-10-14
|
|
276
|
+
|
|
277
|
+
**The last connection — the one that makes the feature user-facing.**
|
|
278
|
+
|
|
279
|
+
### The gap
|
|
280
|
+
|
|
281
|
+
The `chatops` half derived the fleet key from `userId` and exposed it. The `plugin/tools.mjs` half
|
|
282
|
+
accepted a key and handed it to the agent. **Neither knew about the other**, so the plugin read
|
|
283
|
+
`fleetKey` from config or `W2M_FLEET_KEY` — and a machine with a signed-in account still joined
|
|
284
|
+
nothing without a human setting a variable.
|
|
285
|
+
|
|
286
|
+
Two halves that each work, with no wire between them, is the same failure as v0.8.1's: the capability
|
|
287
|
+
existed and nothing exercised it.
|
|
288
|
+
|
|
289
|
+
### Now
|
|
290
|
+
|
|
291
|
+
`startLocalStack` derives the key itself, from the account, via the same
|
|
292
|
+
`createAccountIdentity({ ctx }).resolve()` the rest of the plugin uses. The derivation belongs there
|
|
293
|
+
because **the host is the only layer that can read the account** — a standalone agent cannot — so the
|
|
294
|
+
key must be produced where `ctx` is available rather than where it is used.
|
|
295
|
+
|
|
296
|
+
Verified with DSH's own context shape:
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
ctx.get('deepseekAccount') -> createAccountIdentity({ ctx }).resolve()
|
|
300
|
+
-> fleetKey (43 chars) -> fleetId d5bb1b9ddf3faad4 (matches the existing derivation)
|
|
301
|
+
signed-out -> named reason, no key invented
|
|
302
|
+
no account service -> named reason
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Two failure modes, handled differently on purpose
|
|
306
|
+
|
|
307
|
+
* A **derivation failure is caught and logged, not thrown** — an unpaired agent still runs and can be
|
|
308
|
+
paired later, whereas a thrown start is a machine that never appears at all.
|
|
309
|
+
* **`signed-out` is logged at info, not warn** — being signed out is a normal state, and the design
|
|
310
|
+
says a machine with no account must not be given a random fleet. Warning about it would train an
|
|
311
|
+
operator to ignore the line that matters.
|
|
312
|
+
|
|
313
|
+
An explicitly configured `fleetKey` still wins, and the log names which source was used — `paired by
|
|
314
|
+
fleet key (account)` or `(config)` — so nobody has to guess where the key came from.
|
|
315
|
+
|
|
316
|
+
### A defect in this patch, caught by the compiler
|
|
317
|
+
|
|
318
|
+
The first edit consumed the `} catch (error) {` line and left the catch body inside the `try`, which
|
|
319
|
+
`node --check` reported immediately. Worth recording because the same edit pattern — replacing a
|
|
320
|
+
block by line range — corrupted a function in v0.8.1 as well.
|
|
321
|
+
|
|
322
|
+
### Verified
|
|
323
|
+
|
|
324
|
+
**174 cases** across the tools, apply, account and fleet-join suites.
|
|
325
|
+
|
|
326
|
+
### This is what closes the reported bug
|
|
327
|
+
|
|
328
|
+
A new machine, installed and signed into the same account, derives its fleet key, presents it at
|
|
329
|
+
pairing, and lands in the same fleet — **with no key to copy and no operator step**. That is the
|
|
330
|
+
behaviour the original report asked for, and it is now reachable by configuration rather than by
|
|
331
|
+
convention.
|
|
332
|
+
|
|
333
|
+
## [0.8.3] — 2026-10-14
|
|
334
|
+
|
|
335
|
+
**The last wire, and the reason it was never there.**
|
|
336
|
+
|
|
337
|
+
### `autoStartAgent` was dead code
|
|
338
|
+
|
|
339
|
+
The option existed, was documented, and **threw on its first call**. `startLocalStack` built its
|
|
340
|
+
`createAgent` arguments as `projectDir` (the option is `project`), omitted `identity` (which is
|
|
341
|
+
required), and passed a `pairingCode` that `createAgent` does not accept.
|
|
342
|
+
|
|
343
|
+
Verified rather than inferred — calling `createAgent` with that exact shape:
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
createAgent THREW: createAgent: project is required
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**So the code path that was supposed to bring an agent up had never run.** That is why the
|
|
350
|
+
fleet-join wire appeared to be missing: the place it was meant to live could not be reached at all.
|
|
351
|
+
An option that exists, is documented, and cannot execute is worse than one that is absent — it
|
|
352
|
+
reads as a feature.
|
|
353
|
+
|
|
354
|
+
### Two more things wrong with the shape
|
|
355
|
+
|
|
356
|
+
* **It created a local relay unconditionally.** For a machine joining a shared fleet that is the
|
|
357
|
+
opposite of what is wanted: the machine must connect to the relay that already exists, not mint
|
|
358
|
+
another one — and with `rabbitUrl` pointing at a remote host it would have tried to bind a relay
|
|
359
|
+
on that hostname. It now stands up a local relay **only when the configured URL actually is
|
|
360
|
+
local**, and otherwise logs that it is joining the relay at the given address.
|
|
361
|
+
* **Nothing paired the agent.** Even a started agent could fetch no offers, which looks identical to
|
|
362
|
+
a fleet with no work.
|
|
363
|
+
|
|
364
|
+
### Now
|
|
365
|
+
|
|
366
|
+
`startLocalStack` passes the option names `createAgent` actually documents, loads the identity from
|
|
367
|
+
the **same file the CLI uses** — so one machine has one identity whether the agent runs in-process
|
|
368
|
+
or as its own process, rather than each minting a keypair and overwriting the other's announcement
|
|
369
|
+
under a shared `machine_id` (the v0.6.2 defect, in a new place) — and **pairs by the account fleet
|
|
370
|
+
key**, falling back to a pairing code.
|
|
371
|
+
|
|
372
|
+
Pairing is attempted **before** `agent.start()`, and a failure is **named at warn level** rather than
|
|
373
|
+
swallowed: an agent that failed to pair runs and does nothing, which is indistinguishable from a
|
|
374
|
+
quiet fleet.
|
|
375
|
+
|
|
376
|
+
### Config
|
|
377
|
+
|
|
378
|
+
`fleetKey` is read from the config layer or `W2M_FLEET_KEY`. The key must be **handed down** rather
|
|
379
|
+
than computed where it is used, because a standalone agent cannot read the signed-in account — which
|
|
380
|
+
is why it comes from the host.
|
|
381
|
+
|
|
382
|
+
### Verified
|
|
383
|
+
|
|
384
|
+
**132 cases** across the tools, apply and fleet-join suites. `tools.mjs` loads. The 12 agent-suite
|
|
385
|
+
failures remain pre-existing and environmental, measured against an untouched clone of the same
|
|
386
|
+
commit.
|
|
387
|
+
|
|
388
|
+
### ⚠️ Not yet proven on real hardware
|
|
389
|
+
|
|
390
|
+
The wiring is fixed and the mechanism was verified end to end in v0.8.2, but **no machine has yet
|
|
391
|
+
joined a fleet through this path in a live run**. That is the next check, and it needs a machine
|
|
392
|
+
that is not already paired.
|
|
393
|
+
|
|
7
394
|
## [0.8.2] — 2026-10-14
|
|
8
395
|
|
|
9
396
|
**The client half of the auto-join.** v0.8.1 landed the credential and the relay's acceptance of
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "DSH: Windows2MacOS — drive the same project on online Windows and macOS machines from one DeepSeek account, across networks and regions. P2P hole punching is the default path (relay + shared server as rendezvous, ledger and fallback), with a bundled STUN responder, Tailscale/public-VPS/tunnel shapes, Rabbit relay + per-machine Localside + eight DSH tools (replicate / split), nine aggregate verdicts and signed requests.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|