@twinsearth/w2m-dsh-plugin 1.4.2 → 1.4.3
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 +17 -0
- package/codex/mcp-servers.toml +1 -1
- package/docs/acceptance/1.4.3/BRIDGE-LIVE.json +22 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/src/agent/agent.mjs +6 -1
- package/src/chatops/ilink-fleet-bridge.mjs +19 -0
- package/src/plugin/tools.mjs +1 -1
|
@@ -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.4.
|
|
8
|
+
"version": "1.4.3"
|
|
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.4.
|
|
15
|
+
"version": "1.4.3",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TwinsEarth"
|
|
18
18
|
},
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
|
-
"version": "1.4.
|
|
29
|
+
"version": "1.4.3"
|
|
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.4.
|
|
4
|
+
"version": "1.4.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TwinsEarth"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@
|
|
|
7
7
|
|
|
8
8
|
All notable changes to this project are documented here.
|
|
9
9
|
|
|
10
|
+
## [1.4.3] — 2026-10-22
|
|
11
|
+
|
|
12
|
+
**微信编队桥接:回复可靠性修复(不回复/静默问题)。**
|
|
13
|
+
|
|
14
|
+
- 每条消息独立 try/catch:任何处理异常不再让进程退出,改为回复
|
|
15
|
+
`处理失败(阶段,错误码):原因 + /help 提示`(新增 `renderBridgeError`)。
|
|
16
|
+
- 桥接启动调用 `ilink/bot/msg/notifystart`:修复"token 有效、进程在跑、但 iLink 不推送消息"的静默状态;
|
|
17
|
+
日志记录 `notifystart : ok`,失败也记录原因。
|
|
18
|
+
- 非文本、空消息、未以 `/run` 开头、权限不足、派发失败/超时等路径均保证有回复与下一步提示。
|
|
19
|
+
- 运行方式支持 launchd 常驻(KeepAlive 自动拉起;macOS plist 由运维部署,不进入安装包)。
|
|
20
|
+
|
|
21
|
+
- **授权优先级修复**:exec-capability 只在任务未携带 grant 时兜底;grant 被拒(replay / 过期 / 机器不符)
|
|
22
|
+
原样上报,不再被能力分支覆盖成路径匹配错误,`GRANT_REPLAY` 语义恢复。
|
|
23
|
+
|
|
24
|
+
真机验收(macmini):`notifystart ok`、错误回复代码在位、launchd state=running、bridge 进程存活;
|
|
25
|
+
证据 `docs/acceptance/1.4.3/BRIDGE-LIVE.json`。测试:chatops-ilink-fleet-bridge 36/36。
|
|
26
|
+
|
|
10
27
|
## [1.4.2] — 2026-10-22
|
|
11
28
|
|
|
12
29
|
**Node 代码执行权限成为 UI 可激活的子插件能力(命中真机验收)。**
|
package/codex/mcp-servers.toml
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.4.3",
|
|
3
|
+
"case": "BRIDGE-LIVE",
|
|
4
|
+
"kind": "positive",
|
|
5
|
+
"machines": [
|
|
6
|
+
"71f0dd07-e957-4889-bf82-319a0d6b434a"
|
|
7
|
+
],
|
|
8
|
+
"executed_at": "2026-10-22T07:08:13.838Z",
|
|
9
|
+
"executor": "lead",
|
|
10
|
+
"requirement": "the deployed WeChat bridge enables iLink message push (notifystart ok), answers failures through the renderBridgeError path, and runs under launchd KeepAlive",
|
|
11
|
+
"task_id": "01M5BRIDGEACCMVJ78GU0",
|
|
12
|
+
"result": {
|
|
13
|
+
"outcome": "ok",
|
|
14
|
+
"assertions": {
|
|
15
|
+
"notifystart": true,
|
|
16
|
+
"error_reply_code": true,
|
|
17
|
+
"launchd_state": "state = running",
|
|
18
|
+
"bridge_processes": 3
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"note": "hotfixed bridge files were verified live on macmini before packaging; the same files are what v1.4.3 ships"
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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": {
|
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.4.
|
|
4
|
+
"version": "1.4.3",
|
|
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/agent/agent.mjs
CHANGED
|
@@ -2988,8 +2988,10 @@ export function createAgent(options) {
|
|
|
2988
2988
|
let allowed = matchAllowedCommand(commandArgv, allowedCommands);
|
|
2989
2989
|
let grantAuthorized = false;
|
|
2990
2990
|
let capabilityAuthorized = false;
|
|
2991
|
+
let grantAttempted = false;
|
|
2991
2992
|
const capabilityStatus = readExecCapability(execCapabilityFile);
|
|
2992
2993
|
if (allowed.allowed !== true && offer && typeof offer.grant === 'object' && offer.grant !== null) {
|
|
2994
|
+
grantAttempted = true;
|
|
2993
2995
|
const gatePaths = maintenancePaths();
|
|
2994
2996
|
const gate = await authorizeWithGrant({
|
|
2995
2997
|
offer,
|
|
@@ -3000,7 +3002,10 @@ export function createAgent(options) {
|
|
|
3000
3002
|
if (gate.ok === true) { allowed = { allowed: true, ok: true, grant_id: gate.grantId }; grantAuthorized = true; }
|
|
3001
3003
|
else allowed = { ok: false, reason_code: gate.code ?? 'GRANT_REJECTED', detail: gate.message };
|
|
3002
3004
|
}
|
|
3003
|
-
|
|
3005
|
+
// A capability is a machine-local standing authorization, so it may rescue an allow-list miss -- but never an
|
|
3006
|
+
// explicit grant refusal (replay, expiry, wrong machine). Masking GRANT_REPLAY with a capability would execute a
|
|
3007
|
+
// token the operator already spent, and the envelope would report a reason that names neither decision.
|
|
3008
|
+
if (allowed.allowed !== true && grantAttempted !== true && capabilityStatus.effective === true) {
|
|
3004
3009
|
allowed = matchAllowedCommand(commandArgv, effectiveExecCapabilityPrefixes(execCapabilityFile));
|
|
3005
3010
|
if (allowed.allowed === true) { capabilityAuthorized = true; warnings.push('EXEC_CAPABILITY: ' + EXEC_CAPABILITY_WARNING); }
|
|
3006
3011
|
}
|
|
@@ -372,6 +372,25 @@ export function renderTaskOutcome({ taskId, body }) {
|
|
|
372
372
|
return `${renderVerdict({ taskId, verdict, machines: rows }).text}`;
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Render a per-message handling failure as something the operator can act on.
|
|
377
|
+
*
|
|
378
|
+
* The rule this exists for: **every message gets an answer, including the ones that broke something.**
|
|
379
|
+
* Before this, an exception anywhere in the per-message path escaped the loop, killed the process, and the
|
|
380
|
+
* chat window went silent with no indication why. A failure is now a reply with the phase, a stable code and
|
|
381
|
+
* the next step, and the loop continues.
|
|
382
|
+
*
|
|
383
|
+
* @param {unknown} error
|
|
384
|
+
* @param {string} [phase] where it failed: message | parse | reply | dispatch
|
|
385
|
+
* @returns {string}
|
|
386
|
+
*/
|
|
387
|
+
export function renderBridgeError(error, phase = 'message') {
|
|
388
|
+
const code = error?.code ?? error?.name ?? 'ERR_UNKNOWN';
|
|
389
|
+
const message = error?.message ?? String(error ?? '未知错误');
|
|
390
|
+
return `处理失败(${phase},${code}):${String(message).slice(0, 300)}\n`
|
|
391
|
+
+ '可以发送 /help 查看用法,或稍后重试这条指令。';
|
|
392
|
+
}
|
|
393
|
+
|
|
375
394
|
/** Render a dispatch that never became a task. */
|
|
376
395
|
export function renderFailure(error) {
|
|
377
396
|
return renderDispatchFailure({
|
package/src/plugin/tools.mjs
CHANGED
|
@@ -276,7 +276,7 @@ const MAX_STAGES = 16;
|
|
|
276
276
|
* `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
|
|
277
277
|
* overrides it at runtime for tests and packagers.
|
|
278
278
|
*/
|
|
279
|
-
const PLUGIN_VERSION = '1.4.
|
|
279
|
+
const PLUGIN_VERSION = '1.4.3';
|
|
280
280
|
|
|
281
281
|
/** How long any single HTTP call may take before it is aborted, in milliseconds. */
|
|
282
282
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|