@twinsearth/w2m-dsh-plugin 1.7.3 → 1.7.4
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 +13 -0
- package/codex/mcp-servers.toml +1 -1
- package/docs/acceptance/1.7.4/DESKTOP-HOST-COMPAT.json +32 -0
- package/package.json +3 -3
- package/plugin.json +1 -1
- package/src/plugin/tools.mjs +52 -3
|
@@ -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.7.
|
|
8
|
+
"version": "1.7.4"
|
|
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.7.
|
|
15
|
+
"version": "1.7.4",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TwinsEarth"
|
|
18
18
|
},
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
|
-
"version": "1.7.
|
|
29
|
+
"version": "1.7.4"
|
|
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.7.
|
|
4
|
+
"version": "1.7.4",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TwinsEarth"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
All notable changes to this project are documented here.
|
|
9
9
|
|
|
10
|
+
## [1.7.4] — 2026-10-25
|
|
11
|
+
|
|
12
|
+
**修复两处桌面宿主兼容性问题:`agent.identity` 崩溃、切换 relay 后旧 device token 导致 401 无法入队。**
|
|
13
|
+
|
|
14
|
+
- 崩溃修复:`agent.identity?.()` 在桌面宿主上抛 `TypeError: agent.identity is not a function`,
|
|
15
|
+
且发生在 `agent.start().then(...)` 回调里变成未处理 rejection,把整个 DSH 宿主带崩。
|
|
16
|
+
新增 `agentIdentityOf()` 做类型判断,`agent.start()` 仍为非阻塞并记录失败。
|
|
17
|
+
- 迁移修复:relay 切换到新主 relay 后,`device.json` 里的 token 属于旧 relay,新 relay 回
|
|
18
|
+
`401 UNAUTHORIZED: missing or invalid device_token`;启动逻辑看到已有 token 就跳过配对,
|
|
19
|
+
导致机器停在"无列表"。现在会先用 token 探测配置的 relay:**明确 401/403 才丢弃并重新配对**,
|
|
20
|
+
网络错误保留 token(不会因短暂故障误触发配对)。
|
|
21
|
+
- 新增回归测试:`test/agent-identity-safe.test.mjs`、`test/re-pair-stale-token.test.mjs`。
|
|
22
|
+
- 证据:`docs/acceptance/1.7.4/DESKTOP-HOST-COMPAT.json`(含真实崩溃信息与迁移症状)。
|
|
10
23
|
## [1.7.3] — 2026-10-25
|
|
11
24
|
|
|
12
25
|
**截图提示改为顶部浮层(5 秒自动消失、可关闭、不挤压输入框);全服计数标注来源 relay 并单列本编队。**
|
package/codex/mcp-servers.toml
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.7.4",
|
|
3
|
+
"case": "DESKTOP-HOST-COMPAT",
|
|
4
|
+
"kind": "positive",
|
|
5
|
+
"machines": [
|
|
6
|
+
"b53a37f4-1464-4512-9259-33a7df091e3e"
|
|
7
|
+
],
|
|
8
|
+
"executed_at": "2026-10-25T13:13:25.302Z",
|
|
9
|
+
"executor": "lead",
|
|
10
|
+
"requirement": "the plugin must not crash the DSH desktop host, and a device token issued by one relay must trigger a re-pair when the configured relay no longer accepts it",
|
|
11
|
+
"evidence": {
|
|
12
|
+
"host_crash": {
|
|
13
|
+
"symptom": "dsh: fatal load failure: TypeError: agent.identity is not a function",
|
|
14
|
+
"cause": "agent.identity?.() — the desktop host exposes identity as a property, so the optional CALL threw inside agent.start().then(...) and became an unhandled rejection",
|
|
15
|
+
"fix": "agentIdentityOf() type-checks before calling; agent.start runs with its failure recorded, never awaited as a mount prerequisite"
|
|
16
|
+
},
|
|
17
|
+
"relay_switch": {
|
|
18
|
+
"symptom": "the Rabbit refused GET /v1/devices with HTTP 401 (UNAUTHORIZED: missing or invalid device_token), 无列表",
|
|
19
|
+
"cause": "startup saw a stored token and skipped pairing; the token belonged to the OLD relay after the fleet moved to a new primary",
|
|
20
|
+
"fix": "probe the configured relay with the stored token; an explicit 401/403 drops it and re-pairs via the fleet key, while network errors keep it"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"assertions": {
|
|
24
|
+
"identity_regression": "test/agent-identity-safe.test.mjs pass",
|
|
25
|
+
"re_pair_regression": "test/re-pair-stale-token.test.mjs pass",
|
|
26
|
+
"syntax": "node --check src/plugin/tools.mjs pass"
|
|
27
|
+
},
|
|
28
|
+
"task_id": "local-1.7.4-20261025131554",
|
|
29
|
+
"result": {
|
|
30
|
+
"outcome": "ok"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.4",
|
|
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 test/windows-hidden-task.test.mjs test/relay-failover.test.mjs test/npm-update.test.mjs test/agent-start-nonblocking.test.mjs test/fleet-chat-capture.test.mjs test/capture-route.test.mjs test/update-resolver.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 test/relay-failover.test.mjs test/npm-update.test.mjs test/agent-start-nonblocking.test.mjs test/fleet-chat-capture.test.mjs test/capture-route.test.mjs test/update-resolver.test.mjs test/agent-identity-safe.test.mjs test/re-pair-stale-token.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 test/windows-hidden-task.test.mjs test/relay-failover.test.mjs test/npm-update.test.mjs test/agent-start-nonblocking.test.mjs test/fleet-chat-capture.test.mjs test/capture-route.test.mjs test/update-resolver.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 test/relay-failover.test.mjs test/npm-update.test.mjs test/agent-start-nonblocking.test.mjs test/fleet-chat-capture.test.mjs test/capture-route.test.mjs test/update-resolver.test.mjs test/agent-identity-safe.test.mjs test/re-pair-stale-token.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",
|
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.7.
|
|
4
|
+
"version": "1.7.4",
|
|
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
|
@@ -283,7 +283,7 @@ const MAX_STAGES = 16;
|
|
|
283
283
|
* `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
|
|
284
284
|
* overrides it at runtime for tests and packagers.
|
|
285
285
|
*/
|
|
286
|
-
const PLUGIN_VERSION = '1.7.
|
|
286
|
+
const PLUGIN_VERSION = '1.7.4';
|
|
287
287
|
|
|
288
288
|
/** How long any single HTTP call may take before it is aborted, in milliseconds. */
|
|
289
289
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
|
@@ -7469,6 +7469,25 @@ async function startFailoverStack(ctx, cfg) {
|
|
|
7469
7469
|
await stop();
|
|
7470
7470
|
};
|
|
7471
7471
|
}
|
|
7472
|
+
|
|
7473
|
+
/**
|
|
7474
|
+
* Read an agent's identity without assuming `identity` is a method.
|
|
7475
|
+
*
|
|
7476
|
+
* v1.7.4: calling the optional method form on agent.identity crashed the DESKTOP host with
|
|
7477
|
+
* dsh: fatal load failure: TypeError: agent.identity is not a function
|
|
7478
|
+
* because the desktop host's agent exposes `identity` as a property while the in-process agent
|
|
7479
|
+
* exposes it as a function. Optional CALL only guards null/undefined; a present non-function still
|
|
7480
|
+
* throws, and inside a promise callback that became an unhandled rejection that took the host down.
|
|
7481
|
+
* Reading the value with a type check covers both shapes.
|
|
7482
|
+
*/
|
|
7483
|
+
function agentIdentityOf(agent) {
|
|
7484
|
+
try {
|
|
7485
|
+
if (agent === null || agent === undefined) return null;
|
|
7486
|
+
return typeof agent.identity === 'function' ? agent.identity() : (agent.identity ?? null);
|
|
7487
|
+
} catch {
|
|
7488
|
+
return null;
|
|
7489
|
+
}
|
|
7490
|
+
}
|
|
7472
7491
|
async function startLocalStack(ctx, cfg) {
|
|
7473
7492
|
|
|
7474
7493
|
/**
|
|
@@ -7822,7 +7841,37 @@ async function startLocalStack(ctx, cfg) {
|
|
|
7822
7841
|
* the unconditional pairing here were the same defect seen from two sides.
|
|
7823
7842
|
*/
|
|
7824
7843
|
const existingToken = loaded.identity.device_token;
|
|
7825
|
-
|
|
7844
|
+
let alreadyPaired = typeof existingToken === 'string' && existingToken !== '';
|
|
7845
|
+
|
|
7846
|
+
/**
|
|
7847
|
+
* v1.7.4: a stored token is only "paired" for the relay that issued it.
|
|
7848
|
+
*
|
|
7849
|
+
* Moving the fleet to a new primary relay reuses device.json. The stored token belongs to the OLD
|
|
7850
|
+
* relay, the new one answers `401 UNAUTHORIZED: missing or invalid device_token`, and because the
|
|
7851
|
+
* startup path saw a token it skipped pairing entirely: every machine showed "the Rabbit refused
|
|
7852
|
+
* GET /v1/devices with HTTP 401 ... 无列表" and joined no fleet.
|
|
7853
|
+
*
|
|
7854
|
+
* Probe the configured relay with the stored token. An explicit 401/403 means the token is stale
|
|
7855
|
+
* FOR THIS RELAY, so drop it and pair again through the fleet key. Any other outcome (200, network
|
|
7856
|
+
* error, unexpected status) keeps the token -- a transient outage must not trigger a re-pair.
|
|
7857
|
+
*/
|
|
7858
|
+
if (alreadyPaired) {
|
|
7859
|
+
const relayForToken = relay?.url ?? cfg.rabbitUrl;
|
|
7860
|
+
if (typeof relayForToken === 'string' && relayForToken !== '') {
|
|
7861
|
+
try {
|
|
7862
|
+
const probe = await fetch(relayForToken.replace(/\/+$/, '') + '/v1/devices', {
|
|
7863
|
+
headers: { authorization: 'Bearer ' + existingToken, accept: 'application/json' },
|
|
7864
|
+
signal: typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function' ? AbortSignal.timeout(4000) : undefined,
|
|
7865
|
+
});
|
|
7866
|
+
if (probe.status === 401 || probe.status === 403) {
|
|
7867
|
+
alreadyPaired = false;
|
|
7868
|
+
loaded.identity.device_token = null;
|
|
7869
|
+
if (identityOptions.identity !== undefined && identityOptions.identity !== null) identityOptions.identity.device_token = null;
|
|
7870
|
+
ctx.logger?.warn?.('w2m: stored device token was rejected by ' + relayForToken + ' (HTTP ' + probe.status + '); pairing again with the fleet key');
|
|
7871
|
+
}
|
|
7872
|
+
} catch { /* network error: keep the token, do not force a re-pair */ }
|
|
7873
|
+
}
|
|
7874
|
+
}
|
|
7826
7875
|
|
|
7827
7876
|
/**
|
|
7828
7877
|
* Pair with the key that is in hand, and **persist the token**.
|
|
@@ -8026,7 +8075,7 @@ async function startLocalStack(ctx, cfg) {
|
|
|
8026
8075
|
// the relay, and the value startFailoverStack needs from this function is the cleanup handle. So
|
|
8027
8076
|
// start it in the background and record a failure instead of hanging the whole stack.
|
|
8028
8077
|
agent.start().then(
|
|
8029
|
-
() => ctx.logger?.info?.('w2m: agent started as ' + JSON.stringify(agent
|
|
8078
|
+
() => ctx.logger?.info?.('w2m: agent started as ' + JSON.stringify(agentIdentityOf(agent))),
|
|
8030
8079
|
(error) => ctx.logger?.warn?.('w2m: agent start failed: ' + (error instanceof Error ? error.message : String(error))),
|
|
8031
8080
|
);
|
|
8032
8081
|
|