@twinsearth/w2m-dsh-plugin 0.47.0 → 0.48.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 +55 -0
- package/package.json +1 -1
- package/src/chatops/ilink-fleet-bridge.mjs +41 -0
- package/src/plugin/tools.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,61 @@ 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.48.0] — 2026-10-15
|
|
8
|
+
|
|
9
|
+
**`--dry-run` dispatched. Two dry runs sent a command to five machines, and one made a real task.**
|
|
10
|
+
|
|
11
|
+
### The defect, measured before it was described
|
|
12
|
+
|
|
13
|
+
The bridge guarded exactly one thing under `--dry-run`: the **reply**. The dispatch ran unconditionally.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
[15:13:57] dry run : true
|
|
17
|
+
[15:15:00] dispatched task 01M50231AYJYDD41WSPMS9PRM3
|
|
18
|
+
[15:15:50] verdict for …: partial
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**That is a real task record with five real leases, produced by a run whose flag promised nothing would
|
|
22
|
+
happen.** A rehearsal executed commands on production machines — and it was found by accident, while draining
|
|
23
|
+
a message queue, not by a test.
|
|
24
|
+
|
|
25
|
+
### Why the fix is "skip the dispatch" and not "rename the flag"
|
|
26
|
+
|
|
27
|
+
**This repository already fixes the meaning of the word.** `update-install`'s `dryRun` is asserted to **call no
|
|
28
|
+
package manager and write nothing**, and `auto-update`'s is asserted to **write nothing to the profile**. So a
|
|
29
|
+
second `dry-run` meaning "do the dangerous part anyway" is not a naming preference — **it is two flags with one
|
|
30
|
+
name and opposite meanings, and the bridge held the unsafe one.**
|
|
31
|
+
|
|
32
|
+
**Renaming would have left the flag. Aligning keeps the word and removes the hazard**, and a reader who knows
|
|
33
|
+
`dryRun` from anywhere else in this project is now already right about this one.
|
|
34
|
+
|
|
35
|
+
### Why the guard is a function in `lib`, not an `if` in the script
|
|
36
|
+
|
|
37
|
+
`scripts/` is **not in `package.json#files`**, so nothing there is covered by a test that ships. A safety
|
|
38
|
+
property expressed as one line inside an untested script is a property nobody re-checks. `dryRunPolicy` is
|
|
39
|
+
exported and asserted beside the wire shapes — **a script is not where a guard should live if the guard is the
|
|
40
|
+
point.**
|
|
41
|
+
|
|
42
|
+
The guard sits at the **top of the only function that reaches `fleet.run`**, not at its call site, because a
|
|
43
|
+
guard at the call site is one edit away from being bypassed by a second caller.
|
|
44
|
+
|
|
45
|
+
### A name collision that made my own test wrong
|
|
46
|
+
|
|
47
|
+
I asserted that `dryRun: 'true'` must not dispatch. **It does dispatch — the code was right and the test was
|
|
48
|
+
wrong**, and the cause is two different things sharing one word:
|
|
49
|
+
|
|
50
|
+
| | meaning |
|
|
51
|
+
|---|---|
|
|
52
|
+
| the CLI **flag** `--dry-run` | **present-means-true** (`argv.includes('--dry-run')`) |
|
|
53
|
+
| the **parameter** `dryRun` | **defaults to false**, which is what `update-install` means by it |
|
|
54
|
+
|
|
55
|
+
`dryRunPolicy` takes the parameter, and the script always hands it a boolean. The assertion is now the strict
|
|
56
|
+
one — **only `true` turns the dispatch off, so a future caller cannot open the path with a truthy value.**
|
|
57
|
+
|
|
58
|
+
**5 new tests, 0 failures.** One of them reads the script and asserts the guard appears **before** the only
|
|
59
|
+
call that can reach the fleet — because a test of the pure function alone would pass while the script still
|
|
60
|
+
dispatched, **which is precisely the shape of the original defect.**
|
|
61
|
+
|
|
7
62
|
## [0.47.0] — 2026-10-15
|
|
8
63
|
|
|
9
64
|
**Correcting my own counts: 0.46.0 said 54/32/22, the table says 61/53/8.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.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",
|
|
@@ -25,6 +25,47 @@
|
|
|
25
25
|
|
|
26
26
|
import { parseInvocation, renderVerdict, renderDispatchFailure } from './run-command.mjs';
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* What a run with `--dry-run` set is allowed to do.
|
|
30
|
+
*
|
|
31
|
+
* ## The defect this exists to make unreachable
|
|
32
|
+
*
|
|
33
|
+
* `scripts/ilink-fleet-bridge.mjs` guarded exactly one thing under `--dry-run`: the **reply**. The
|
|
34
|
+
* dispatch itself ran unconditionally, so **two dry runs sent `node--version` to all five machines**
|
|
35
|
+
* — and one of them produced a real task record with five real leases. The flag's name promises that
|
|
36
|
+
* nothing happens, and the promise was false in the direction that matters: **a rehearsal executed
|
|
37
|
+
* commands on production machines.**
|
|
38
|
+
*
|
|
39
|
+
* ## Why "skip the dispatch" rather than "rename the flag"
|
|
40
|
+
*
|
|
41
|
+
* This repository already fixes the meaning of the word. `update-install`'s `dryRun` is asserted to
|
|
42
|
+
* **write nothing and call no package manager**, and `auto-update`'s is asserted to **write nothing to
|
|
43
|
+
* the profile**. So a second `dry-run` that means "do the dangerous part anyway" is not a naming
|
|
44
|
+
* preference — it is two flags with one name and opposite meanings, and the bridge held the unsafe one.
|
|
45
|
+
*
|
|
46
|
+
* **Renaming would have left the flag. Aligning keeps the word and removes the hazard**, and it means
|
|
47
|
+
* a reader who knows `dryRun` from anywhere else in this project is already right about this one.
|
|
48
|
+
*
|
|
49
|
+
* ## Why this is a function rather than an `if` in the script
|
|
50
|
+
*
|
|
51
|
+
* `scripts/` is not in `package.json#files`, so nothing there is covered by a test that ships. A safety
|
|
52
|
+
* property expressed as one line inside an untested script is a property nobody re-checks. As an
|
|
53
|
+
* exported function with a name, it is asserted in `chatops-ilink-fleet-bridge.test.mjs` beside the
|
|
54
|
+
* wire shapes — **a script is not where a guard should live if the guard is the point.**
|
|
55
|
+
*
|
|
56
|
+
* @param {{ dryRun?: boolean }} [options]
|
|
57
|
+
* @returns {{ dispatch: boolean, reply: boolean, reason: string|null }}
|
|
58
|
+
*/
|
|
59
|
+
export function dryRunPolicy(options = {}) {
|
|
60
|
+
if (options.dryRun !== true) return { dispatch: true, reply: true, reason: null };
|
|
61
|
+
return {
|
|
62
|
+
dispatch: false,
|
|
63
|
+
reply: false,
|
|
64
|
+
// Carried so the caller logs *why* nothing happened rather than printing a bare "[dry-run]".
|
|
65
|
+
reason: 'dry run: the fleet was not dispatched to, and no reply was sent',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
28
69
|
/** The header every authenticated call needs; undocumented and mandatory. */
|
|
29
70
|
export const AUTH_TYPE = 'ilink_bot_token';
|
|
30
71
|
export const APP_ID = 'bot';
|
package/src/plugin/tools.mjs
CHANGED
|
@@ -208,7 +208,7 @@ const MAX_STAGES = 16;
|
|
|
208
208
|
* `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
|
|
209
209
|
* overrides it at runtime for tests and packagers.
|
|
210
210
|
*/
|
|
211
|
-
const PLUGIN_VERSION = '0.
|
|
211
|
+
const PLUGIN_VERSION = '0.48.0';
|
|
212
212
|
|
|
213
213
|
/** How long any single HTTP call may take before it is aborted, in milliseconds. */
|
|
214
214
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|