@twinsearth/w2m-dsh-plugin 0.18.0 → 0.20.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/CHANGELOG.md +85 -0
- package/package.json +5 -4
- package/src/agent/fleet-scale.mjs +72 -0
- package/src/plugin/tools.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,91 @@ 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.20.0] — 2026-10-15
|
|
8
|
+
|
|
9
|
+
**Version skew is refuted too, and the direct-path failure is now counted instead of read one at a time.**
|
|
10
|
+
|
|
11
|
+
### Third hypothesis, third refutation
|
|
12
|
+
|
|
13
|
+
The previous release established that this machine runs plugin **0.9.5** while the source tree is **0.19.0**,
|
|
14
|
+
and that the failing `P2P_PUNCH_FAILED` is raised by the plugin process. The natural next step was to suspect a
|
|
15
|
+
digest name that 0.9.5 used and later versions removed.
|
|
16
|
+
|
|
17
|
+
**Measured, and refuted:** reading `crypto.mjs`, `p2p.mjs` and `p2p-node.mjs` **at tag `v0.9.5`** through the
|
|
18
|
+
API shows the same two digest names the current tree uses — `blake2s256` and `sha256`. Then a search for
|
|
19
|
+
**dynamic** algorithm arguments (`createHash(variable)`) across `src/` found **none**: every `createHash` and
|
|
20
|
+
`createHmac` in this codebase is called with a string literal.
|
|
21
|
+
|
|
22
|
+
**So there is no call in this codebase that can produce `Digest method not supported`.** Three hypotheses have
|
|
23
|
+
now been measured and excluded — BLAKE2s, the wider primitive set, and version skew — and the cause remains
|
|
24
|
+
**UNVERIFIED**. What is *not* in doubt is where it is raised: the plugin process on this machine, running
|
|
25
|
+
0.9.5 while the source under study is 0.20.0.
|
|
26
|
+
|
|
27
|
+
### What this release delivers instead of a fourth guess
|
|
28
|
+
|
|
29
|
+
`p2pReasons()` and `classifyP2PReason()` turn the P2P reason strings into a **count**, because the finding that
|
|
30
|
+
matters was invisible in every individual report: **twelve of twelve tasks travelled by relay, and no aggregate
|
|
31
|
+
anywhere said so.**
|
|
32
|
+
|
|
33
|
+
* `relay_only_share` is the fleet's avoidable latency as one number, and it is **`null`** for an empty
|
|
34
|
+
denominator — "no lease carried a p2p block" and "every lease failed" must not look alike.
|
|
35
|
+
* an unrecognised code is counted as `OTHER` **with the raw code travelling with it**, so a new refusal name
|
|
36
|
+
is reported rather than swallowed.
|
|
37
|
+
* a missing reason is distinguished from a present one, because *"no reason recorded" is not "no failure"*.
|
|
38
|
+
|
|
39
|
+
### The version skew is still unresolved and still blocks diagnosis
|
|
40
|
+
|
|
41
|
+
The installed plugin is **0.9.5**; the source is **0.20.0**. Until they match, every source-level conclusion
|
|
42
|
+
about this machine's P2P behaviour is read from code that is not running. This needs a person: updating the
|
|
43
|
+
plugin installs files into the DSH profile, which is outside what this session changes on its own.
|
|
44
|
+
|
|
45
|
+
**7 new tests, 0 failures.**
|
|
46
|
+
|
|
47
|
+
## [0.19.0] — 2026-10-15
|
|
48
|
+
|
|
49
|
+
**The plugin this machine runs is 0.9.5. The source tree is 0.18.0. Every source-level conclusion about the P2P failure was read from code that may not have run.**
|
|
50
|
+
|
|
51
|
+
Measured, not inferred:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
source : 0.18.0
|
|
55
|
+
installed : 0.9.5 at ...\profiles\desktop\node_modules\@twinsearth\w2m-dsh-plugin
|
|
56
|
+
verdict : installed 0.9.5 is 8995 rank(s) BEHIND the source 0.18.0
|
|
57
|
+
-- source-level conclusions about this machine are void
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Reading `p2p-node.mjs` established which call raises `P2P_PUNCH_FAILED`, and it is raised **by the plugin
|
|
61
|
+
process** — `tools.mjs:1463`, `node.dial(...)`, on the dispatching machine. That machine's plugin is **nine
|
|
62
|
+
versions behind the file that was being read.** Two rounds of crypto-capability work were therefore correct
|
|
63
|
+
about the code in this repository and only *possibly* correct about the code that failed.
|
|
64
|
+
|
|
65
|
+
### Two runtime facts were assumed, and only one held
|
|
66
|
+
|
|
67
|
+
| Assumption | Status |
|
|
68
|
+
|---|---|
|
|
69
|
+
| which node the plugin runs under | **holds** — the DSH process runs `dsh-primary-runtime/.../node.exe` v24.21.0, the same binary that was probed; the runtime was never the variable |
|
|
70
|
+
| which plugin version is loaded | **did not hold** — 0.9.5 installed against 0.18.0 of source |
|
|
71
|
+
|
|
72
|
+
The first was checked last and held; the second was never checked and did not. **A version skew is not a
|
|
73
|
+
detail to note — it is a reason every source-level conclusion is void**, and it should have been the first
|
|
74
|
+
thing measured rather than the last.
|
|
75
|
+
|
|
76
|
+
### The invariant is now a test
|
|
77
|
+
|
|
78
|
+
`test/installed-version.test.mjs` finds every plausible install location, compares numerically (because
|
|
79
|
+
`'0.9.0' > '0.10.0'` as strings, and `0.9.5` against `0.18.0` is exactly the case that would be got wrong),
|
|
80
|
+
and **reports the skew with both numbers** rather than failing an assertion nobody could act on. It also
|
|
81
|
+
refuses to compare what it cannot parse, and **says so when it finds no installed copy** — because passing by
|
|
82
|
+
absence is the same failure mode as an earlier skip guard in this repository, which passed for three rounds
|
|
83
|
+
while hiding a defect in the tooling rather than in the code.
|
|
84
|
+
|
|
85
|
+
### What is needed from a person
|
|
86
|
+
|
|
87
|
+
**The local DSH must load the current plugin** for P2P diagnosis to mean anything. Until it does, this
|
|
88
|
+
machine's `P2P_PUNCH_FAILED` comes from 0.9.5 and no reading of 0.19.0's source can explain it.
|
|
89
|
+
|
|
90
|
+
**8 tests, 0 failures.**
|
|
91
|
+
|
|
7
92
|
## [0.18.0] — 2026-10-15
|
|
8
93
|
|
|
9
94
|
**The whole handshake primitive set is present on both machines — so the P2P failure is not a missing algorithm.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
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",
|
|
@@ -113,13 +113,13 @@
|
|
|
113
113
|
}
|
|
114
114
|
},
|
|
115
115
|
"scripts": {
|
|
116
|
-
"test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/layer-model.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-crypto.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/chatops-fleet-join.test.mjs",
|
|
116
|
+
"test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/layer-model.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-crypto.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/chatops-fleet-join.test.mjs",
|
|
117
117
|
"test:e2e": "node --test --test-force-exit test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/p2p-agent.test.mjs",
|
|
118
118
|
"test:relay": "node --test --test-force-exit test/relay.test.mjs",
|
|
119
119
|
"test:agent": "node --test --test-force-exit test/agent.test.mjs test/memory.test.mjs",
|
|
120
120
|
"test:tools": "node --test --test-force-exit test/tools.test.mjs",
|
|
121
121
|
"test:crossnetwork": "node --test --test-force-exit test/crossnetwork.test.mjs",
|
|
122
|
-
"test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/layer-model.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-agent.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/p2p-crypto.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/chatops-fleet-join.test.mjs",
|
|
122
|
+
"test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/layer-model.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-agent.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/p2p-crypto.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/chatops-fleet-join.test.mjs",
|
|
123
123
|
"pack": "node scripts/pack.mjs",
|
|
124
124
|
"verify:join": "node scripts/verify-join.mjs",
|
|
125
125
|
"lint": "eslint .",
|
|
@@ -143,7 +143,8 @@
|
|
|
143
143
|
"test:test-report": "node --test --test-force-exit test/test-report.test.mjs",
|
|
144
144
|
"test:fleet-scale": "node --test --test-force-exit test/fleet-scale.test.mjs",
|
|
145
145
|
"test:fleet-state": "node --test --test-force-exit test/fleet-state.test.mjs",
|
|
146
|
-
"test:crypto-blake2s": "node --test --test-force-exit test/crypto-blake2s.test.mjs"
|
|
146
|
+
"test:crypto-blake2s": "node --test --test-force-exit test/crypto-blake2s.test.mjs",
|
|
147
|
+
"test:installed-version": "node --test --test-force-exit test/installed-version.test.mjs"
|
|
147
148
|
},
|
|
148
149
|
"keywords": [
|
|
149
150
|
"dsh-bundle",
|
|
@@ -111,6 +111,78 @@ export function isFleetFailure(entry) {
|
|
|
111
111
|
return classifyLease(entry).outcome === LEASE_OUTCOME.NO_RESULT;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/** Refusal codes the relay writes into `p2p.reason`, grouped by what they mean for a fleet. */
|
|
115
|
+
export const P2P_FAILURE_KIND = Object.freeze({
|
|
116
|
+
NO_CANDIDATES: 'no candidate address was known, so no punch was attempted',
|
|
117
|
+
PUNCH_FAILED: 'a punch was attempted and did not complete',
|
|
118
|
+
PUNCH_TIMEOUT: 'a punch was attempted and nothing answered in time',
|
|
119
|
+
LEASE_NOT_RUNNABLE: 'the lease was not in a state to be handed the command',
|
|
120
|
+
REVERSE_DIAL: 'the reverse dial did not complete',
|
|
121
|
+
BLOCKED: 'a local condition stopped the direct path',
|
|
122
|
+
OTHER: 'a reason the table does not classify',
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Classify one `p2p.reason` string.
|
|
127
|
+
*
|
|
128
|
+
* Reasons are prose with a machine-readable prefix (`P2P_NO_CANDIDATES: ...`), and reading them one at a time
|
|
129
|
+
* is how a fleet's direct-path problem stays invisible: the sample that prompted this had **twelve of twelve
|
|
130
|
+
* tasks on the relay** and no aggregate anywhere that said so.
|
|
131
|
+
*
|
|
132
|
+
* Returns `OTHER` for anything unrecognised rather than throwing or guessing, and the raw reason travels with
|
|
133
|
+
* the classification so a new code is reported rather than swallowed.
|
|
134
|
+
*/
|
|
135
|
+
export function classifyP2PReason(reason) {
|
|
136
|
+
if (typeof reason !== 'string' || reason === '') {
|
|
137
|
+
return { kind: null, code: null, detail: 'no reason recorded, which is not the same as no failure' };
|
|
138
|
+
}
|
|
139
|
+
const code = (reason.split(':')[0] ?? '').trim();
|
|
140
|
+
if (code === 'P2P_NO_CANDIDATES') return { kind: P2P_FAILURE_KIND.NO_CANDIDATES, code, detail: reason };
|
|
141
|
+
if (code === 'P2P_PUNCH_FAILED') return { kind: P2P_FAILURE_KIND.PUNCH_FAILED, code, detail: reason };
|
|
142
|
+
if (code === 'P2P_PUNCH_TIMEOUT') return { kind: P2P_FAILURE_KIND.PUNCH_TIMEOUT, code, detail: reason };
|
|
143
|
+
if (code === 'P2P_LEASE_NOT_RUNNABLE') return { kind: P2P_FAILURE_KIND.LEASE_NOT_RUNNABLE, code, detail: reason };
|
|
144
|
+
if (code.startsWith('P2P_REVERSE_DIAL')) return { kind: P2P_FAILURE_KIND.REVERSE_DIAL, code, detail: reason };
|
|
145
|
+
if (code.startsWith('P2P_UNAVAILABLE') || code.startsWith('P2P_CRYPTO')) return { kind: P2P_FAILURE_KIND.BLOCKED, code, detail: reason };
|
|
146
|
+
return { kind: P2P_FAILURE_KIND.OTHER, code, detail: reason };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Count why the direct path was not used, across a set of reports.
|
|
151
|
+
*
|
|
152
|
+
* The number worth acting on is `relay_only_share`: the fraction of leases that travelled by relay while a
|
|
153
|
+
* direct path was nominally configured. That is the fleet's avoidable latency, and it is invisible in any
|
|
154
|
+
* single report.
|
|
155
|
+
*/
|
|
156
|
+
export function p2pReasons(reports) {
|
|
157
|
+
const byCode = new Map();
|
|
158
|
+
let leasesWithP2P = 0;
|
|
159
|
+
let onRelay = 0;
|
|
160
|
+
let onDirect = 0;
|
|
161
|
+
for (const report of Array.isArray(reports) ? reports : []) {
|
|
162
|
+
for (const m of Array.isArray(report?.machines) ? report.machines : []) {
|
|
163
|
+
const p = m?.p2p;
|
|
164
|
+
if (p === null || p === undefined || typeof p !== 'object') continue;
|
|
165
|
+
leasesWithP2P += 1;
|
|
166
|
+
if (p.encrypted === true || (p.peer !== null && p.peer !== undefined)) onDirect += 1;
|
|
167
|
+
else onRelay += 1;
|
|
168
|
+
const c = classifyP2PReason(p.reason ?? p.encryption_reason ?? null);
|
|
169
|
+
if (c.code !== null) byCode.set(c.code, (byCode.get(c.code) ?? 0) + 1);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
leases_with_p2p: leasesWithP2P,
|
|
174
|
+
on_relay: onRelay,
|
|
175
|
+
on_direct: onDirect,
|
|
176
|
+
// Null rather than 0 for an empty denominator, for the same reason every other rate here is: "no leases"
|
|
177
|
+
// and "every lease failed" must not look alike.
|
|
178
|
+
relay_only_share: leasesWithP2P === 0 ? null : Math.round((onRelay / leasesWithP2P) * 1000) / 1000,
|
|
179
|
+
by_code: Object.fromEntries([...byCode].sort((a, b) => b[1] - a[1])),
|
|
180
|
+
detail: leasesWithP2P === 0
|
|
181
|
+
? 'no lease carried a p2p block, so nothing can be said about the direct path'
|
|
182
|
+
: `${onRelay} of ${leasesWithP2P} lease(s) used the relay; ${byCode.size} distinct reason code(s)`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
114
186
|
/**
|
|
115
187
|
* Summarise a set of task reports.
|
|
116
188
|
*
|
package/src/plugin/tools.mjs
CHANGED
|
@@ -185,7 +185,7 @@ const MAX_STAGES = 16;
|
|
|
185
185
|
* `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
|
|
186
186
|
* overrides it at runtime for tests and packagers.
|
|
187
187
|
*/
|
|
188
|
-
const PLUGIN_VERSION = '0.
|
|
188
|
+
const PLUGIN_VERSION = '0.20.0';
|
|
189
189
|
|
|
190
190
|
/** How long any single HTTP call may take before it is aborted, in milliseconds. */
|
|
191
191
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|