@twinsearth/w2m-dsh-plugin 0.8.9 → 0.8.11
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 +122 -0
- package/bin/w2m-localside.mjs +325 -325
- package/bin/w2m-rabbit.mjs +0 -0
- package/bin/w2m-stun.mjs +0 -0
- package/package.json +20 -19
- package/src/chatops/account.mjs +430 -399
- package/src/chatops/index.mjs +440 -440
- package/src/plugin/auto-update.mjs +31 -2
- package/src/plugin/tools.mjs +5207 -4878
- package/src/relay/state.mjs +2277 -2277
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,128 @@ 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.11] — 2026-10-14
|
|
8
|
+
|
|
9
|
+
**The in-process join threw before it could pair — and a missing fingerprint took the fleet key with it.**
|
|
10
|
+
|
|
11
|
+
### The defect that mattered
|
|
12
|
+
|
|
13
|
+
`startLocalStack` built `identityOptions`, loaded the identity, and then passed
|
|
14
|
+
`identityOptions.identity` to `createAgent` — but nothing ever assigned that property.
|
|
15
|
+
`loadOrCreateIdentity()` returns `{ identity, path, created }` and does not mutate the options it was
|
|
16
|
+
given, so `createAgent` received `undefined` and rejected it by contract:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
createAgent: identity.machine_id is required
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**The in-process join therefore threw on every start**, after `loadOrCreateIdentity` had already
|
|
23
|
+
created `device.json` and before anything could pair.
|
|
24
|
+
|
|
25
|
+
That single line accounts for the whole reported symptom, and every earlier explanation was reading
|
|
26
|
+
the wreckage:
|
|
27
|
+
|
|
28
|
+
| observed | why |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `device.json` exists, `device_token: null` | the file is created one line above the throw |
|
|
31
|
+
| its mtime never moves across restarts | execution never reaches the pairing path |
|
|
32
|
+
| `w2m_devices` answers `401` | no token was ever obtained |
|
|
33
|
+
| `P2P_ANNOUNCE_FAILED: HTTP 401` | same token, same absence |
|
|
34
|
+
|
|
35
|
+
The evidence that finally settled it was the mtime, exactly as v0.8.9's own note said it would be —
|
|
36
|
+
but read the other way round: not "the write is missing" but "**nothing after the throw runs at all**".
|
|
37
|
+
|
|
38
|
+
### The second gate: a missing fingerprint discarded a derived key
|
|
39
|
+
|
|
40
|
+
`resolve()` mints a serial number from a hardware fingerprint and the fleet key from the account's
|
|
41
|
+
`userId`. The key does not depend on the fingerprint — but it was only returned *after* the
|
|
42
|
+
fingerprint succeeded:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const hardware = await fingerprint();
|
|
46
|
+
if (!hardware.ok) return hardware; // accountFields -- and the fleet key -- thrown away
|
|
47
|
+
const sn = buildSn({ accountCode: account.accountCode, ... });
|
|
48
|
+
return { ok: true, sn, ...accountFields, ... };
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
On the machine this was reported from, `readHardwareValues({ platform: 'win32' })` returns **zero**
|
|
52
|
+
usable values — both `MachineGuid` (via `reg query`) and the SMBIOS UUID (via PowerShell) come back
|
|
53
|
+
empty — so `fingerprint()` always failed and the fleet key was always discarded after being computed.
|
|
54
|
+
|
|
55
|
+
No amount of retrying could fix that, which is why v0.8.9's backoff did not help: the account *was*
|
|
56
|
+
readable, and the key *was* derived. The failure is now reported as what it is (`no_hardware_source`,
|
|
57
|
+
no SN), while `accountFields` still travels, so a caller that only needs to join a fleet is not held
|
|
58
|
+
hostage by a display identifier.
|
|
59
|
+
|
|
60
|
+
### The join is no longer a one-shot prologue
|
|
61
|
+
|
|
62
|
+
Two changes to the shape, both consequences of the above:
|
|
63
|
+
|
|
64
|
+
- **The mount path is fast.** It spends two derivation attempts (~2 s), not six (~62 s):
|
|
65
|
+
`agent.start()` waits behind it, and any tool that asked to join waited behind that. The patient
|
|
66
|
+
retrying moved into a background loop beside the running agent, where nothing is blocked by it.
|
|
67
|
+
- **A tool call can finish the join.** A tool call is proof the account profile exists — the session
|
|
68
|
+
cannot answer a model request without it — so `w2m_status`, `w2m_devices` and `w2m_run` ask the
|
|
69
|
+
in-process agent to join if it has not already.
|
|
70
|
+
|
|
71
|
+
The rate limit is respected by a **cooldown, not by permanence**: a successful join is memoised for
|
|
72
|
+
the life of the process, while a *failed* one is retried after ten seconds. Memoising the failure
|
|
73
|
+
would have reproduced the defect being fixed — a machine holding a rejected promise for ever, which
|
|
74
|
+
is the "intermittent miss becomes permanent" trap v0.8.7 fell into.
|
|
75
|
+
|
|
76
|
+
### Reporting
|
|
77
|
+
|
|
78
|
+
A machine that cannot join says so once, at warn, naming the reason — and says it again only if the
|
|
79
|
+
state changes. The previous wording guessed *"the profile may not be loaded yet"* for every
|
|
80
|
+
`ok: true` case, which after sixty seconds of backoff was usually the wrong explanation.
|
|
81
|
+
|
|
82
|
+
### Verified
|
|
83
|
+
|
|
84
|
+
`node scripts/verify-join.mjs` — **16 checks, all passing.** It drives the join against a stub agent
|
|
85
|
+
(the real `start()` talks to the public relay for the life of the process, which is why nothing
|
|
86
|
+
exercised this path before) and asserts:
|
|
87
|
+
|
|
88
|
+
- the agent is constructed with a real `identity.machine_id` — the assertion that catches the defect
|
|
89
|
+
above, since `undefined` is what was being passed;
|
|
90
|
+
- nothing is paired while the account cannot be read, and no pairing is attempted without a key;
|
|
91
|
+
- the machine joins once the account answers, **and the token reaches `device.json`**;
|
|
92
|
+
- the pairing presents a 43-character derived fleet key and names the identity on disk;
|
|
93
|
+
- a machine holding a token never re-pairs, however many tools ask (`pair_rate_limit: 5`);
|
|
94
|
+
- a fingerprint failure still yields the fleet key, with `no_hardware_source` reported and no SN
|
|
95
|
+
invented.
|
|
96
|
+
|
|
97
|
+
Two of those checks found real problems while being written, which is the point of having them. The
|
|
98
|
+
first draft of the cooldown rate-limited the *keyless* case as well, so the mount attempt failed on a
|
|
99
|
+
cold account and the very next tool call silently did nothing — the reported symptom, reproduced by
|
|
100
|
+
its own fix. And the mount budget, which had been ~62 s since v0.8.9 with `agent.start()` and every
|
|
101
|
+
tool call waiting behind it, is now ~2 s.
|
|
102
|
+
|
|
103
|
+
The script reads and writes a throwaway identity (`DSH_DEVICE_FILE`), so it never touches the real
|
|
104
|
+
`~/.dsh/xclient/device.json`.
|
|
105
|
+
|
|
106
|
+
Measured on the Windows host this was reported from:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
late-account join
|
|
110
|
+
ok the agent is constructed
|
|
111
|
+
ok the agent receives a real identity (createAgent requires identity.machine_id)
|
|
112
|
+
ok nothing is paired while the account cannot be read
|
|
113
|
+
ok no pairing is attempted without a fleet key
|
|
114
|
+
ok the machine says it is in no fleet rather than staying silent
|
|
115
|
+
ok w2m_status is registered
|
|
116
|
+
ok the machine joins once the account answers
|
|
117
|
+
ok the token is persisted, not just held in memory
|
|
118
|
+
ok the pairing presents a derived fleet key
|
|
119
|
+
ok the pairing names the identity on disk
|
|
120
|
+
ok a machine holding a token never re-pairs
|
|
121
|
+
fleet key without a hardware fingerprint
|
|
122
|
+
ok no fingerprint means no serial number, and that is reported
|
|
123
|
+
ok no serial number is invented
|
|
124
|
+
ok the fleet key survives the fingerprint failure
|
|
125
|
+
ok the account code travels with it
|
|
126
|
+
all checks passed
|
|
127
|
+
```
|
|
128
|
+
|
|
7
129
|
## [0.8.9] — 2026-10-14
|
|
8
130
|
|
|
9
131
|
**The account answers lazily, and we asked once.**
|