@twinsearth/w2m-dsh-plugin 0.12.3 → 0.13.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 +41 -0
- package/package.json +5 -4
- package/src/agent/test-report.mjs +142 -0
- package/src/plugin/tools.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,47 @@ 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.13.0] — 2026-10-15
|
|
8
|
+
|
|
9
|
+
**The root cause of three rounds of confusion was a regex, and it was mine.**
|
|
10
|
+
|
|
11
|
+
Several rounds were spent believing a suite *failed on macOS*. It had not. The parser was looking for lines
|
|
12
|
+
beginning `# tests 17`. Node v24 writes them beginning `ℹ tests 17`:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
ℹ tests 17 not # tests 17
|
|
16
|
+
ℹ pass 17 # pass 17
|
|
17
|
+
ℹ fail 0 # fail 0
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**The `#` form was an assumption that was never checked**, carried from the first script written in this
|
|
21
|
+
session to every one after it. The damage was worse than a misread count:
|
|
22
|
+
|
|
23
|
+
* suites that had **passed** were recorded as `UNREADABLE`;
|
|
24
|
+
* and then the absence of a `# fail` line, together with the absence of test *names* in the captured output,
|
|
25
|
+
was read as **"the process crashed before printing anything"** — which pointed at module scope and sent an
|
|
26
|
+
investigation somewhere the fault was not. Two rounds went into that, including a probe that correctly ruled
|
|
27
|
+
out every file-system hypothesis, because the hypothesis being tested was derived from a bad premise.
|
|
28
|
+
|
|
29
|
+
`src/agent/test-report.mjs` now writes the contract down, parses both prefixes plus a bare form, reads
|
|
30
|
+
**both streams and says which one answered**, and reports its own format so a future change is *reported*
|
|
31
|
+
rather than silently tolerated. 13 tests, and the two that matter most are:
|
|
32
|
+
|
|
33
|
+
* the **real** v24 output parses (taken verbatim from a run, not invented);
|
|
34
|
+
* an unreadable report yields `verdict(...) === null`, **not `false`** — collapsing "could not read" into
|
|
35
|
+
"failed" is the safer of the two wrong directions and was still wrong enough to waste rounds.
|
|
36
|
+
|
|
37
|
+
### The lesson, stated so it generalises
|
|
38
|
+
|
|
39
|
+
**I assumed the shape of a channel's output instead of measuring it** — the same error, from the same
|
|
40
|
+
channel, for the third time in this session (first the exit-code summary line, then the test names, now the
|
|
41
|
+
prefix). The technique that finally settled it was printing the raw output and looking at it, which took one
|
|
42
|
+
command and should have been the first.
|
|
43
|
+
|
|
44
|
+
`test/log-bounds.test.mjs` remains skipped on macOS. With a working parser the skip is no longer plausible as
|
|
45
|
+
a parse artefact, so the underlying assertion failure is still **UNVERIFIED** — but the reason it took so long
|
|
46
|
+
to see is now fixed and tested.
|
|
47
|
+
|
|
7
48
|
## [0.12.3] — 2026-10-15
|
|
8
49
|
|
|
9
50
|
**Correcting a wrong inference that 0.12.2 recorded as fact.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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/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/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/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/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 .",
|
|
@@ -139,7 +139,8 @@
|
|
|
139
139
|
"test:council": "node --test --test-force-exit test/council.test.mjs",
|
|
140
140
|
"test:layers": "node --test --test-force-exit test/layer-model.test.mjs",
|
|
141
141
|
"test:log-bounds": "node --test --test-force-exit test/log-bounds.test.mjs",
|
|
142
|
-
"layer-check": "node scripts/layer-check.mjs"
|
|
142
|
+
"layer-check": "node scripts/layer-check.mjs",
|
|
143
|
+
"test:test-report": "node --test --test-force-exit test/test-report.test.mjs"
|
|
143
144
|
},
|
|
144
145
|
"keywords": [
|
|
145
146
|
"dsh-bundle",
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading `node --test`'s summary, without assuming its shape.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this module exists, told plainly because it cost three rounds
|
|
5
|
+
*
|
|
6
|
+
* Several rounds of this project were spent believing a suite *failed on macOS*. It had not. The parser was
|
|
7
|
+
* looking for lines beginning `# tests 17`, and node v24 writes them beginning `ℹ tests 17`:
|
|
8
|
+
*
|
|
9
|
+
* ℹ tests 17 not # tests 17
|
|
10
|
+
* ℹ pass 17 # pass 17
|
|
11
|
+
* ℹ fail 0 # fail 0
|
|
12
|
+
*
|
|
13
|
+
* **The `#` form was an assumption that was never checked.** It produced "UNREADABLE" for suites that had
|
|
14
|
+
* passed, and — worse — it produced a *wrong diagnosis*: because no `# fail` line could ever be found, and
|
|
15
|
+
* because no test *names* appeared either, the absence of both was read as "the process crashed before
|
|
16
|
+
* printing anything", which pointed at module scope and sent the investigation somewhere the fault was not.
|
|
17
|
+
* The actual fault was a regex.
|
|
18
|
+
*
|
|
19
|
+
* So the contract is written down here, parsed defensively, and tested against both forms. The one thing
|
|
20
|
+
* this module must never do is answer "unreadable" when the answer was merely "in a shape I did not expect".
|
|
21
|
+
*
|
|
22
|
+
* ## The two streams
|
|
23
|
+
*
|
|
24
|
+
* `node --test` writes its report to **stdout** on every version measured so far, and writes *diagnostics*
|
|
25
|
+
* to stderr. But "on every version measured so far" is exactly the kind of claim that ages badly, so the
|
|
26
|
+
* parser reads **both** streams and says which one it used. A caller that cannot tell which stream answered
|
|
27
|
+
* is a caller that will misread the next version change the same way.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** Summary labels, as node has spelled them. Both are accepted; neither is assumed. */
|
|
31
|
+
export const LABELS = Object.freeze(['tests', 'pass', 'fail', 'skipped', 'cancelled', 'todo', 'duration_ms']);
|
|
32
|
+
|
|
33
|
+
/** The prefixes node has used for summary lines. `ℹ` is what v24 writes; `#` is what earlier TAP-shaped output used. */
|
|
34
|
+
export const PREFIXES = Object.freeze(['\u2139', '#']);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Strip a summary prefix and return `{label, value}`, or `null`.
|
|
38
|
+
*
|
|
39
|
+
* Tolerant on purpose: leading whitespace is allowed (node indents nested summaries), and the prefix may be
|
|
40
|
+
* followed by a space or not. A strict pattern here is what caused the original fault, and a strict pattern
|
|
41
|
+
* is not made safer by being strict in a different way.
|
|
42
|
+
*/
|
|
43
|
+
export function parseSummaryLine(line) {
|
|
44
|
+
if (typeof line !== 'string') return null;
|
|
45
|
+
const trimmed = line.trim();
|
|
46
|
+
let body = null;
|
|
47
|
+
for (const prefix of PREFIXES) {
|
|
48
|
+
if (trimmed.startsWith(prefix)) {
|
|
49
|
+
body = trimmed.slice(prefix.length).trim();
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// A bare `tests 17` with no prefix is also accepted, because "no prefix" is a shape node has not used yet
|
|
54
|
+
// and refusing it would be the same mistake in a new costume.
|
|
55
|
+
if (body === null) body = trimmed;
|
|
56
|
+
const match = /^([a-z_]+)\s+(\d+(?:\.\d+)?)$/.exec(body);
|
|
57
|
+
if (match === null) return null;
|
|
58
|
+
const label = match[1];
|
|
59
|
+
if (!LABELS.includes(label)) return null;
|
|
60
|
+
return { label, value: Number(match[2]) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Parse a full report.
|
|
65
|
+
*
|
|
66
|
+
* @param {string|{stdout?: string, stderr?: string}} input text, or the two streams separately
|
|
67
|
+
* @returns {{
|
|
68
|
+
* ok: boolean, source: 'stdout'|'stderr'|'both'|'none', format: 'info'|'hash'|'bare'|'unknown',
|
|
69
|
+
* tests: number|null, pass: number|null, fail: number|null, skipped: number|null,
|
|
70
|
+
* detail: string
|
|
71
|
+
* }}
|
|
72
|
+
*/
|
|
73
|
+
export function parseTestReport(input) {
|
|
74
|
+
const stdout = typeof input === 'string' ? input : String(input?.stdout ?? '');
|
|
75
|
+
const stderr = typeof input === 'string' ? '' : String(input?.stderr ?? '');
|
|
76
|
+
|
|
77
|
+
const scan = (text) => {
|
|
78
|
+
const found = new Map();
|
|
79
|
+
let format = 'unknown';
|
|
80
|
+
for (const line of text.split('\n')) {
|
|
81
|
+
const parsed = parseSummaryLine(line);
|
|
82
|
+
if (parsed === null) continue;
|
|
83
|
+
// Record the shape that actually matched, so a future format change is reported rather than silently
|
|
84
|
+
// tolerated.
|
|
85
|
+
if (format === 'unknown') {
|
|
86
|
+
const trimmed = line.trim();
|
|
87
|
+
if (trimmed.startsWith('\u2139')) format = 'info';
|
|
88
|
+
else if (trimmed.startsWith('#')) format = 'hash';
|
|
89
|
+
else format = 'bare';
|
|
90
|
+
}
|
|
91
|
+
found.set(parsed.label, parsed.value);
|
|
92
|
+
}
|
|
93
|
+
return { found, format };
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const out = scan(stdout);
|
|
97
|
+
const err = scan(stderr);
|
|
98
|
+
const fromStdout = out.found.size > 0;
|
|
99
|
+
const fromStderr = err.found.size > 0;
|
|
100
|
+
const source = fromStdout && fromStderr ? 'both' : fromStdout ? 'stdout' : fromStderr ? 'stderr' : 'none';
|
|
101
|
+
// stdout wins when both carry a summary, because that is where node has always put the report.
|
|
102
|
+
const chosen = fromStdout ? out : err;
|
|
103
|
+
const format = fromStdout ? out.format : fromStderr ? err.format : 'unknown';
|
|
104
|
+
|
|
105
|
+
if (!fromStdout && !fromStderr) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false, source: 'none', format: 'unknown',
|
|
108
|
+
tests: null, pass: null, fail: null, skipped: null,
|
|
109
|
+
detail: 'no summary line found in either stream -- this is NOT the same as a failing suite, and the caller must not treat it as one',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const get = (label) => (chosen.found.has(label) ? chosen.found.get(label) : null);
|
|
114
|
+
const tests = get('tests');
|
|
115
|
+
const fail = get('fail');
|
|
116
|
+
return {
|
|
117
|
+
ok: true,
|
|
118
|
+
source,
|
|
119
|
+
format,
|
|
120
|
+
tests,
|
|
121
|
+
pass: get('pass'),
|
|
122
|
+
fail,
|
|
123
|
+
skipped: get('skipped'),
|
|
124
|
+
detail: tests === null
|
|
125
|
+
? `read from ${source} (${format}) but no test count was present`
|
|
126
|
+
: `${tests} test(s), ${fail ?? '?'} failed, read from ${source} (${format})`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether a report says the suite passed.
|
|
132
|
+
*
|
|
133
|
+
* **Three-valued on purpose.** `true`, `false`, and `null` for "the report could not be read" — because
|
|
134
|
+
* collapsing `null` into either boolean is exactly how an unread report becomes a pass or a phantom failure.
|
|
135
|
+
* The original bug turned unreadable reports into "failed", which is the safer of the two directions to be
|
|
136
|
+
* wrong in and was still wrong enough to waste rounds.
|
|
137
|
+
*/
|
|
138
|
+
export function verdict(report) {
|
|
139
|
+
if (report === null || report === undefined || report.ok !== true) return null;
|
|
140
|
+
if (report.fail === null) return null;
|
|
141
|
+
return report.fail === 0;
|
|
142
|
+
}
|
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.13.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;
|