@twinsearth/w2m-dsh-plugin 1.6.3 → 1.6.5
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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +23 -0
- package/bin/w2m-hidden-task.mjs +96 -0
- package/codex/mcp-servers.toml +1 -1
- package/docs/WINDOWS-BACKGROUND-TASKS.md +53 -0
- package/docs/acceptance/1.6.4/HEALTHZ-FLEETS-ONLINE.json +20 -0
- package/docs/acceptance/1.6.5/HIDDEN-BACKGROUND-TASKS.json +21 -0
- package/package.json +5 -4
- package/plugin.json +1 -1
- package/src/plugin/tools.mjs +1 -1
- package/src/relay/state.mjs +2 -0
- package/src/util/windows-hidden-task.mjs +86 -0
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "The w2m fleet plugins: one account, many machines, any of Windows, macOS or Linux.",
|
|
8
|
-
"version": "1.6.
|
|
8
|
+
"version": "1.6.5"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "w2m-fleet",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Read fleet state and dispatch commands across the machines registered to one account. Read-only MCP surface: the five exposed tools cannot change anything.",
|
|
15
|
-
"version": "1.6.
|
|
15
|
+
"version": "1.6.5",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TwinsEarth"
|
|
18
18
|
},
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
|
-
"version": "1.6.
|
|
29
|
+
"version": "1.6.5"
|
|
30
30
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "w2m-fleet",
|
|
3
3
|
"description": "Drive the same project on online Windows, macOS and Linux machines from one account: read fleet state, dispatch commands and read results. The MCP surface is deliberately read-only.",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.5",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TwinsEarth"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@
|
|
|
7
7
|
|
|
8
8
|
All notable changes to this project are documented here.
|
|
9
9
|
|
|
10
|
+
## [1.6.5] — 2026-10-24
|
|
11
|
+
|
|
12
|
+
**Windows 后台自动化不再弹窗:VBScript 隐藏包装 + 计划任务工具 + 文档。**
|
|
13
|
+
|
|
14
|
+
现场故障:给 standby 守护(每 2 分钟)和 TwinsEarth 守望(每 5 分钟)用 `schtasks /TR "node.exe ..."`
|
|
15
|
+
注册任务,导致桌面每隔几分钟弹出一个控制台窗口(守望脚本还会累积进程)。
|
|
16
|
+
|
|
17
|
+
- 新增 `src/util/windows-hidden-task.mjs`:纯函数构造 VBScript 包装(`WScript.Shell.Run` 窗口样式 0、
|
|
18
|
+
不等待)与 `schtasks` 参数;命令行/参数先按 Windows 规则引用,再整体做一次 VBScript 转义(避免二次转义)。
|
|
19
|
+
- 新增 `bin/w2m-hidden-task.mjs`:`--name/--every-minutes/--command/--arg/--workdir/--delete`,
|
|
20
|
+
一条命令注册“无窗口”计划任务。
|
|
21
|
+
- 新增 `docs/WINDOWS-BACKGROUND-TASKS.md`:故障原因、正确做法、原则(分钟级任务只放短检查;一次性任务用完即删)。
|
|
22
|
+
- 测试:windows-hidden-task 5/5(含转义与参数校验);证据
|
|
23
|
+
`docs/acceptance/1.6.5/HIDDEN-BACKGROUND-TASKS.json`(fleet task `01M5ANYMVM30H98`)。
|
|
24
|
+
## [1.6.4] — 2026-10-24
|
|
25
|
+
|
|
26
|
+
**修复:`/healthz` 漏发 `fleets_online`(1.6.3 的 `fleetSummary` 已计算,但 `stats()` 没带到公开字段)。**
|
|
27
|
+
|
|
28
|
+
- `stats()` 现在同时输出 `fleets_total` 与 `fleets_online`;
|
|
29
|
+
- 面板可正确显示 `全服:X/Y 个编队 · M/T 台在线`(X = 至少一台机器在线的编队数);
|
|
30
|
+
- relay 主机升级到 1.6.4(含 1.6.3/1.6.2/1.6.1 全部内容)。
|
|
31
|
+
- 测试:relay 200/200、fleet-target 49/49;证据 `docs/acceptance/1.6.4/HEALTHZ-FLEETS-ONLINE.json`
|
|
32
|
+
(fleet task `01M5ANYMVLX3MI7`)。
|
|
10
33
|
## [1.6.3] — 2026-10-24
|
|
11
34
|
|
|
12
35
|
**编队与机器同构显示:在线编队数/编队总数 + 在线机器/机器总数。**
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* w2m-hidden-task — schedule a command on Windows **without any console window**.
|
|
4
|
+
*
|
|
5
|
+
* Why this tool exists: `schtasks /TR "node.exe ..."` produces a visible console window every time
|
|
6
|
+
* the task fires. On a 2-minute keeper that is a window every two minutes; on a long-running watcher
|
|
7
|
+
* it also accumulates processes. This tool writes a VBScript wrapper and registers
|
|
8
|
+
* `wscript.exe //B wrapper.vbs`, which starts the command hidden and does not wait.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node bin/w2m-hidden-task.mjs --name w2m-keeper --every-minutes 2 \
|
|
12
|
+
* --command "C:\...\node.exe" --arg "C:\...\script.mjs" \
|
|
13
|
+
* [--workdir "C:\..."] [--delete]
|
|
14
|
+
*/
|
|
15
|
+
import { execFileSync } from 'node:child_process';
|
|
16
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
17
|
+
import { dirname, join } from 'node:path';
|
|
18
|
+
import os from 'node:os';
|
|
19
|
+
|
|
20
|
+
import { buildHiddenWrapper, hiddenTaskArgs } from '../src/util/windows-hidden-task.mjs';
|
|
21
|
+
|
|
22
|
+
/** Parse `--flag value` pairs plus repeatable `--arg`; unknown flags are refused by name. */
|
|
23
|
+
export function parseHiddenTaskArgs(argv) {
|
|
24
|
+
const out = { name: null, everyMinutes: null, command: null, args: [], workdir: null, delete: false, vbsPath: null };
|
|
25
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
26
|
+
const flag = argv[i];
|
|
27
|
+
const next = () => {
|
|
28
|
+
const value = argv[i + 1];
|
|
29
|
+
if (typeof value !== 'string' || value.startsWith('--')) throw new Error(`${flag} needs a value`);
|
|
30
|
+
i += 1;
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
switch (flag) {
|
|
34
|
+
case '--name': out.name = next(); break;
|
|
35
|
+
case '--every-minutes': out.everyMinutes = Number(next()); break;
|
|
36
|
+
case '--command': out.command = next(); break;
|
|
37
|
+
case '--arg': out.args.push(next()); break;
|
|
38
|
+
case '--workdir': out.workdir = next(); break;
|
|
39
|
+
case '--vbs': out.vbsPath = next(); break;
|
|
40
|
+
case '--delete': out.delete = true; break;
|
|
41
|
+
case '--help':
|
|
42
|
+
case '-h': out.help = true; break;
|
|
43
|
+
default: throw new Error(`unknown flag: ${flag}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (out.delete === true) {
|
|
47
|
+
if (typeof out.name !== 'string' || out.name.trim() === '') throw new Error('--delete needs --name');
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
if (out.name === null || out.command === null || out.everyMinutes === null) {
|
|
51
|
+
throw new Error('--name, --command and --every-minutes are required');
|
|
52
|
+
}
|
|
53
|
+
if (out.vbsPath === null) {
|
|
54
|
+
const safe = String(out.name).replace(/[^A-Za-z0-9._-]/g, '_');
|
|
55
|
+
out.vbsPath = join(os.homedir(), '.dsh', 'xclient', `${safe}-hidden.vbs`);
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function main() {
|
|
61
|
+
let args;
|
|
62
|
+
try {
|
|
63
|
+
args = parseHiddenTaskArgs(process.argv.slice(2));
|
|
64
|
+
} catch (error) {
|
|
65
|
+
process.stderr.write(`w2m-hidden-task: ${error.message}\n`);
|
|
66
|
+
process.exit(2);
|
|
67
|
+
}
|
|
68
|
+
if (args.help === true) {
|
|
69
|
+
process.stdout.write('usage: w2m-hidden-task.mjs --name <task> --every-minutes <n> --command <exe> [--arg <a>]... [--workdir <dir>] [--vbs <path>]\n'
|
|
70
|
+
+ ' w2m-hidden-task.mjs --delete --name <task>\n');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (args.delete === true) {
|
|
74
|
+
try {
|
|
75
|
+
execFileSync('schtasks', ['/Delete', '/TN', args.name, '/F'], { stdio: 'ignore' });
|
|
76
|
+
process.stdout.write(`deleted ${args.name}\n`);
|
|
77
|
+
} catch {
|
|
78
|
+
process.stdout.write(`no task named ${args.name}\n`);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (process.platform !== 'win32') throw new Error('hidden scheduled tasks are a Windows feature');
|
|
83
|
+
mkdirSync(dirname(args.vbsPath), { recursive: true });
|
|
84
|
+
writeFileSync(args.vbsPath, buildHiddenWrapper({ command: args.command, args: args.args, workingDirectory: args.workdir }), 'utf8');
|
|
85
|
+
const taskArgs = hiddenTaskArgs({ taskName: args.name, everyMinutes: args.everyMinutes, vbsPath: args.vbsPath });
|
|
86
|
+
execFileSync('schtasks', taskArgs, { stdio: 'ignore' });
|
|
87
|
+
process.stdout.write(`scheduled ${args.name} every ${args.everyMinutes} min (hidden wrapper ${args.vbsPath})\n`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const invokedDirectly = process.argv[1] !== undefined && import.meta.url === new URL(`file://${process.argv[1].replace(/\\/g, '/')}`).href;
|
|
91
|
+
if (invokedDirectly) {
|
|
92
|
+
main().catch((error) => {
|
|
93
|
+
process.stderr.write(`w2m-hidden-task: ${error?.message ?? error}\n`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
});
|
|
96
|
+
}
|
package/codex/mcp-servers.toml
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Windows 后台任务:不弹窗的正确做法
|
|
2
|
+
|
|
3
|
+
## 现象
|
|
4
|
+
|
|
5
|
+
用 `schtasks /TR "node.exe script.mjs"` 创建的计划任务,每次触发都会在桌面**弹出一个控制台窗口**。
|
|
6
|
+
本机曾因此出现"每隔 2–5 分钟自动弹窗",来源是守护/守望任务:
|
|
7
|
+
|
|
8
|
+
| 任务 | 频率 | 实际行为 |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| standby keeper | 每 2 分钟 | 起 `powershell.exe` → 窗口闪烁 |
|
|
11
|
+
| TwinsEarth watcher | 每 5 分钟 | 起 `node.exe`,且脚本设计跑 6 小时 → 窗口+进程累积 |
|
|
12
|
+
|
|
13
|
+
## 正确做法
|
|
14
|
+
|
|
15
|
+
用 `wscript.exe //B wrapper.vbs` 注册任务;VBScript 里:
|
|
16
|
+
|
|
17
|
+
```vbs
|
|
18
|
+
Set sh = CreateObject("WScript.Shell")
|
|
19
|
+
sh.Run """C:\...\node.exe"" ""C:\...\script.mjs"" --flag", 0, False
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- `0` = 隐藏窗口(不显示控制台)
|
|
23
|
+
- `False` = 不等待(任务立即返回)
|
|
24
|
+
|
|
25
|
+
## 用工具生成(v1.6.5 起)
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
node bin/w2m-hidden-task.mjs `
|
|
29
|
+
--name w2m-standby-keeper `
|
|
30
|
+
--every-minutes 2 `
|
|
31
|
+
--command "E:\DS\resources\runtime\primary-runtime\dependencies\node\bin\node.exe" `
|
|
32
|
+
--arg "C:\Users\you\.dsh\xclient\standby-keeper.mjs" `
|
|
33
|
+
--workdir "C:\Users\you"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
输出:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
scheduled w2m-standby-keeper every 2 min (hidden wrapper C:\Users\you\.dsh\xclient\w2m-standby-keeper-hidden.vbs)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
删除:
|
|
43
|
+
|
|
44
|
+
```powershell
|
|
45
|
+
node bin/w2m-hidden-task.mjs --delete --name w2m-standby-keeper
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 原则
|
|
49
|
+
|
|
50
|
+
1. **不要**把长驻脚本挂到分钟级计划任务上(每次触发都会起一个新实例)——分钟级任务里只放"检查并退出"的短脚本;
|
|
51
|
+
2. 计划任务一律通过 VBS 隐藏包装启动,不要直接 `node.exe`;
|
|
52
|
+
3. 长期守护优先用系统服务/launchd/启动项,而不是高频计划任务;
|
|
53
|
+
4. 一次性任务(如远程重启)用完即删,避免残留(本机曾残留 3 个 `w2m-restart-*`)。
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.6.4",
|
|
3
|
+
"case": "HEALTHZ-FLEETS-ONLINE",
|
|
4
|
+
"kind": "positive",
|
|
5
|
+
"machines": [
|
|
6
|
+
"b53a37f4-1464-4512-9259-33a7df091e3e"
|
|
7
|
+
],
|
|
8
|
+
"executed_at": "2026-10-24T04:47:35.562Z",
|
|
9
|
+
"executor": "lead",
|
|
10
|
+
"requirement": "/healthz carries fleets_online beside fleets_total so the panel can show online fleets / total fleets",
|
|
11
|
+
"task_id": "01M5ANYMVLX3MI7",
|
|
12
|
+
"result": {
|
|
13
|
+
"outcome": "ok",
|
|
14
|
+
"assertions": {
|
|
15
|
+
"relay_suite": "200/200",
|
|
16
|
+
"stats": "fleets_online present"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"note": "1.6.3 shipped fleetSummary.fleets_online but headlz stats omitted it; this release adds it"
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.6.5",
|
|
3
|
+
"case": "HIDDEN-BACKGROUND-TASKS",
|
|
4
|
+
"kind": "positive",
|
|
5
|
+
"machines": [
|
|
6
|
+
"b53a37f4-1464-4512-9259-33a7df091e3e"
|
|
7
|
+
],
|
|
8
|
+
"executed_at": "2026-10-24T07:33:03.150Z",
|
|
9
|
+
"executor": "lead",
|
|
10
|
+
"requirement": "Windows background automation never creates a console window: tasks run through a VBScript wrapper with window style 0, and the helper refuses incomplete input",
|
|
11
|
+
"task_id": "01M5ANYMVM30H98",
|
|
12
|
+
"result": {
|
|
13
|
+
"outcome": "ok",
|
|
14
|
+
"assertions": {
|
|
15
|
+
"hidden_task_suite": "5/5 pass",
|
|
16
|
+
"wrapper": "Run(...,0,False)",
|
|
17
|
+
"task_line": "wscript.exe //B"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"note": "field symptom: a 2-minute keeper and a 5-minute watcher produced recurring console windows"
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.5",
|
|
4
4
|
"description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 11 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"pack": "node scripts/pack.mjs",
|
|
65
65
|
"reachability": "node scripts/reachability-check.mjs",
|
|
66
66
|
"reachability:classify": "node scripts/reachability-classify.mjs",
|
|
67
|
-
"test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/blake2s.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/fleet-memory-e2e.test.mjs test/fleet-concurrency.test.mjs test/fleet-merge-race.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.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/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.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/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/portable-plugins.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs test/admission.test.mjs test/admission-routes.test.mjs test/join-degradation.test.mjs test/account-identity.test.mjs test/join-challenge.test.mjs test/exec-env-injection.test.mjs test/account-join.test.mjs test/fleet-messages.test.mjs test/message-shards.test.mjs test/fleet-messages-http.test.mjs test/message-notify.test.mjs test/update-channels.test.mjs test/update-prerelease.test.mjs test/session-watch.test.mjs test/session-readonly.test.mjs test/session-sync.test.mjs test/fleet-chat-identity.test.mjs test/identity-reader.test.mjs test/intents.test.mjs test/dataflow.test.mjs test/council-fleet.test.mjs test/fleet-memory.test.mjs test/memory-view.test.mjs test/schema-spec.test.mjs test/fleet-weight-parity.test.mjs test/task-deps.test.mjs test/dataflow-wiring.test.mjs test/task-graph.test.mjs test/ask-store.test.mjs test/turn.test.mjs test/fleet-members.test.mjs test/redact.test.mjs test/policy-matrix.test.mjs test/envelope-signature.test.mjs test/chat-crypto.test.mjs test/reduce-suggestion.test.mjs test/merge-hint.test.mjs test/fleet-chat-route.test.mjs test/maintenance.test.mjs test/maintenance-ferry.test.mjs test/grant.test.mjs test/grant-gate.test.mjs test/worktree.test.mjs test/patch-artifact.test.mjs test/merge-queue.test.mjs test/m3-workflow.test.mjs test/project-room.test.mjs test/project-attribution.test.mjs test/project-locks.test.mjs test/project-reviews.test.mjs test/project-notifications.test.mjs test/blob-sessions.test.mjs test/outbox.test.mjs test/reliability-metrics.test.mjs test/evidence.test.mjs test/grant-issue.test.mjs test/project-refs.test.mjs test/exec-capability.test.mjs test/secondary-link.test.mjs test/bootstrap-cli.test.mjs test/bootstrap.test.mjs",
|
|
67
|
+
"test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/blake2s.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/fleet-memory-e2e.test.mjs test/fleet-concurrency.test.mjs test/fleet-merge-race.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.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/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.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/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/portable-plugins.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs test/admission.test.mjs test/admission-routes.test.mjs test/join-degradation.test.mjs test/account-identity.test.mjs test/join-challenge.test.mjs test/exec-env-injection.test.mjs test/account-join.test.mjs test/fleet-messages.test.mjs test/message-shards.test.mjs test/fleet-messages-http.test.mjs test/message-notify.test.mjs test/update-channels.test.mjs test/update-prerelease.test.mjs test/session-watch.test.mjs test/session-readonly.test.mjs test/session-sync.test.mjs test/fleet-chat-identity.test.mjs test/identity-reader.test.mjs test/intents.test.mjs test/dataflow.test.mjs test/council-fleet.test.mjs test/fleet-memory.test.mjs test/memory-view.test.mjs test/schema-spec.test.mjs test/fleet-weight-parity.test.mjs test/task-deps.test.mjs test/dataflow-wiring.test.mjs test/task-graph.test.mjs test/ask-store.test.mjs test/turn.test.mjs test/fleet-members.test.mjs test/redact.test.mjs test/policy-matrix.test.mjs test/envelope-signature.test.mjs test/chat-crypto.test.mjs test/reduce-suggestion.test.mjs test/merge-hint.test.mjs test/fleet-chat-route.test.mjs test/maintenance.test.mjs test/maintenance-ferry.test.mjs test/grant.test.mjs test/grant-gate.test.mjs test/worktree.test.mjs test/patch-artifact.test.mjs test/merge-queue.test.mjs test/m3-workflow.test.mjs test/project-room.test.mjs test/project-attribution.test.mjs test/project-locks.test.mjs test/project-reviews.test.mjs test/project-notifications.test.mjs test/blob-sessions.test.mjs test/outbox.test.mjs test/reliability-metrics.test.mjs test/evidence.test.mjs test/grant-issue.test.mjs test/project-refs.test.mjs test/exec-capability.test.mjs test/secondary-link.test.mjs test/bootstrap-cli.test.mjs test/bootstrap.test.mjs test/windows-hidden-task.test.mjs",
|
|
68
68
|
"test:acceptance-doc": "node --test --test-force-exit test/acceptance-doc.test.mjs",
|
|
69
69
|
"test:account-identity": "node --test --test-force-exit test/account-identity.test.mjs",
|
|
70
70
|
"test:account-join": "node --test --test-force-exit test/account-join.test.mjs",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"test:council-session": "node --test --test-force-exit test/council-session.test.mjs",
|
|
83
83
|
"test:crossnetwork": "node --test --test-force-exit test/crossnetwork.test.mjs",
|
|
84
84
|
"test:crypto-blake2s": "node --test --test-force-exit test/crypto-blake2s.test.mjs",
|
|
85
|
-
"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 test/secondary-link.test.mjs test/bootstrap-cli.test.mjs test/bootstrap.test.mjs",
|
|
85
|
+
"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 test/secondary-link.test.mjs test/bootstrap-cli.test.mjs test/bootstrap.test.mjs test/windows-hidden-task.test.mjs",
|
|
86
86
|
"test:exec-env": "node --test --test-force-exit test/exec-env-injection.test.mjs",
|
|
87
87
|
"test:exit-channel": "node --test --test-force-exit test/exit-channel.test.mjs",
|
|
88
88
|
"test:field-authority": "node --test --test-force-exit test/field-authority.test.mjs",
|
|
@@ -204,7 +204,8 @@
|
|
|
204
204
|
"w2m-grant": "./bin/w2m-grant.mjs",
|
|
205
205
|
"w2m-secondary-link": "bin/w2m-secondary-link.mjs",
|
|
206
206
|
"w2m-bootstrap": "bin/w2m-bootstrap.mjs",
|
|
207
|
-
"w2m-bootstrap-sign": "bin/w2m-bootstrap-sign.mjs"
|
|
207
|
+
"w2m-bootstrap-sign": "bin/w2m-bootstrap-sign.mjs",
|
|
208
|
+
"w2m-hidden-task": "bin/w2m-hidden-task.mjs"
|
|
208
209
|
},
|
|
209
210
|
"disclosure": {
|
|
210
211
|
"cloud": true,
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "twinsearth-w2m-dsh",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.5",
|
|
5
5
|
"description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 12 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
package/src/plugin/tools.mjs
CHANGED
|
@@ -277,7 +277,7 @@ const MAX_STAGES = 16;
|
|
|
277
277
|
* `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
|
|
278
278
|
* overrides it at runtime for tests and packagers.
|
|
279
279
|
*/
|
|
280
|
-
const PLUGIN_VERSION = '1.6.
|
|
280
|
+
const PLUGIN_VERSION = '1.6.5';
|
|
281
281
|
|
|
282
282
|
/** How long any single HTTP call may take before it is aborted, in milliseconds. */
|
|
283
283
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
package/src/relay/state.mjs
CHANGED
|
@@ -3280,6 +3280,8 @@ export class RabbitState {
|
|
|
3280
3280
|
devices: this.devices.size,
|
|
3281
3281
|
// v1.6.2: whole-relay totals on the public health probe.
|
|
3282
3282
|
fleets_total: this.fleetSummary().fleets_total,
|
|
3283
|
+
// v1.6.4: 1.6.3 forgot to surface this on /healthz (the panel reads it there).
|
|
3284
|
+
fleets_online: this.fleetSummary().fleets_online,
|
|
3283
3285
|
devices_online: this.fleetSummary().devices_online,
|
|
3284
3286
|
devices_total: this.devices.size,
|
|
3285
3287
|
tasks: this.tasks.size,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows background tasks without popups (v1.6.5).
|
|
3
|
+
*
|
|
4
|
+
* ## The problem this fixes
|
|
5
|
+
*
|
|
6
|
+
* The standby keeper and the fleet watcher were scheduled with
|
|
7
|
+
* `schtasks /TR "node.exe script.mjs"`. Windows Task Scheduler runs such a command in the
|
|
8
|
+
* interactive session, which creates a **console window every time the task fires** — a 2-minute
|
|
9
|
+
* keeper means a window every two minutes, and a 5-minute watcher that itself runs for hours
|
|
10
|
+
* accumulates both windows and processes. That is a real usability defect: the operator sees the
|
|
11
|
+
* machine "randomly opening windows" and cannot tell what is happening.
|
|
12
|
+
*
|
|
13
|
+
* ## The fix
|
|
14
|
+
*
|
|
15
|
+
* `wscript.exe //B wrapper.vbs` runs invisibly. The wrapper (VBScript) starts the real command with
|
|
16
|
+
* `WScript.Shell.Run(command, 0, False)`: window style 0 = hidden, third argument = do not wait. No
|
|
17
|
+
* console window, no lingering `cmd`, and the task still runs on schedule.
|
|
18
|
+
*
|
|
19
|
+
* Everything here is pure string construction (and one CLI that calls `schtasks`), so the escaping
|
|
20
|
+
* rules — the part that silently breaks hidden wrappers — are unit-testable without touching a real
|
|
21
|
+
* scheduler.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Command line for Task Scheduler that hides the window.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} vbsPath Absolute path of the wrapper .vbs.
|
|
28
|
+
* @returns {string} e.g. `wscript.exe //B "C:\...\wrapper.vbs"`
|
|
29
|
+
*/
|
|
30
|
+
export function hiddenTaskCommandLine(vbsPath) {
|
|
31
|
+
if (typeof vbsPath !== 'string' || vbsPath.trim() === '') throw new TypeError('vbsPath is required');
|
|
32
|
+
return `wscript.exe //B "${vbsPath}"`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Escape one literal for the inside of a VBScript double-quoted string. */
|
|
36
|
+
export function vbsString(value) {
|
|
37
|
+
return '"' + String(value).replace(/"/g, '""') + '"';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build the wrapper script that runs `command` hidden.
|
|
42
|
+
*
|
|
43
|
+
* The command itself is passed through verbatim, and `arguments` is appended in the same
|
|
44
|
+
* double-quote-escaped form Windows uses on the command line (`""` inside a VBS string becomes one
|
|
45
|
+
* literal quote when the shell rebuilds the command). Every argument is quoted individually so a
|
|
46
|
+
* path with spaces cannot split into two arguments.
|
|
47
|
+
*
|
|
48
|
+
* @param {{command: string, args?: string[], workingDirectory?: string|null}} input
|
|
49
|
+
* @returns {string} VBScript source.
|
|
50
|
+
*/
|
|
51
|
+
export function buildHiddenWrapper({ command, args = [], workingDirectory = null }) {
|
|
52
|
+
if (typeof command !== 'string' || command.trim() === '') throw new TypeError('command is required');
|
|
53
|
+
// Build the real command line first (Windows quoting), THEN escape the whole line once for
|
|
54
|
+
// VBScript. Escaping each argument separately and escaping again for the script string would
|
|
55
|
+
// double the quotes twice — the subtle over-escaping that makes a wrapper run the wrong token.
|
|
56
|
+
const quoteWin = (value) => (/[\s"]/.test(String(value)) ? '"' + String(value).replace(/"/g, '\\"') + '"' : String(value));
|
|
57
|
+
const parts = [quoteWin(command), ...(Array.isArray(args) ? args : []).map(quoteWin)];
|
|
58
|
+
const lines = [
|
|
59
|
+
"' Generated by w2m-hidden-task (v1.6.5): run a command with NO console window.",
|
|
60
|
+
'Set sh = CreateObject("WScript.Shell")',
|
|
61
|
+
];
|
|
62
|
+
if (typeof workingDirectory === 'string' && workingDirectory !== '') {
|
|
63
|
+
lines.push(`sh.CurrentDirectory = ${vbsString(workingDirectory)}`);
|
|
64
|
+
}
|
|
65
|
+
lines.push(`sh.Run ${vbsString(parts.join(' '))}, 0, False`);
|
|
66
|
+
return lines.join('\r\n') + '\r\n';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The `schtasks /Create` argument list for a hidden wrapper, without executing anything.
|
|
71
|
+
*
|
|
72
|
+
* @param {{taskName: string, everyMinutes: number, vbsPath: string}} input
|
|
73
|
+
* @returns {string[]}
|
|
74
|
+
*/
|
|
75
|
+
export function hiddenTaskArgs({ taskName, everyMinutes, vbsPath }) {
|
|
76
|
+
if (typeof taskName !== 'string' || taskName.trim() === '') throw new TypeError('taskName is required');
|
|
77
|
+
const minutes = Number(everyMinutes);
|
|
78
|
+
if (!Number.isInteger(minutes) || minutes < 1) throw new TypeError('everyMinutes must be a positive integer');
|
|
79
|
+
return [
|
|
80
|
+
'/Create', '/F',
|
|
81
|
+
'/SC', 'MINUTE',
|
|
82
|
+
'/MO', String(minutes),
|
|
83
|
+
'/TN', taskName,
|
|
84
|
+
'/TR', hiddenTaskCommandLine(vbsPath),
|
|
85
|
+
];
|
|
86
|
+
}
|