agent-comms 6.0.0 → 8.0.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +5 -3
- package/dist/bridges/claude-code/channel.d.ts.map +1 -1
- package/dist/bridges/claude-code/channel.js +2 -2
- package/dist/bridges/claude-code/channel.js.map +1 -1
- package/dist/bridges/codex/tool.d.ts.map +1 -1
- package/dist/bridges/codex/tool.js +2 -2
- package/dist/bridges/codex/tool.js.map +1 -1
- package/dist/bridges/mcp/server.d.ts.map +1 -1
- package/dist/bridges/mcp/server.js +2 -2
- package/dist/bridges/mcp/server.js.map +1 -1
- package/dist/bridges/opencode/plugin.js +2 -2
- package/dist/bridges/opencode/plugin.js.map +1 -1
- package/dist/bridges/pi/extension.d.ts.map +1 -1
- package/dist/bridges/pi/extension.js +2 -9
- package/dist/bridges/pi/extension.js.map +1 -1
- package/dist/bridges/user/controller.d.ts +1 -1
- package/dist/bridges/user/controller.d.ts.map +1 -1
- package/dist/bridges/user/controller.js +4 -4
- package/dist/bridges/user/controller.js.map +1 -1
- package/dist/bridges/user/web/server.d.ts +5 -11
- package/dist/bridges/user/web/server.d.ts.map +1 -1
- package/dist/bridges/user/web/server.js +15 -14
- package/dist/bridges/user/web/server.js.map +1 -1
- package/dist/bridges/user/web/test/bridge-web-server.integration.test.d.ts +2 -0
- package/dist/bridges/user/web/test/bridge-web-server.integration.test.d.ts.map +1 -0
- package/dist/bridges/user/web/test/bridge-web-server.integration.test.js +47 -0
- package/dist/bridges/user/web/test/bridge-web-server.integration.test.js.map +1 -0
- package/dist/core/bridge-mesh.d.ts +1 -1
- package/dist/core/bridge-mesh.d.ts.map +1 -1
- package/dist/core/bridge-mesh.js +2 -1
- package/dist/core/bridge-mesh.js.map +1 -1
- package/dist/core/gateway-trust.d.ts +5 -5
- package/dist/core/gateway-trust.d.ts.map +1 -1
- package/dist/core/gateway-trust.js +5 -5
- package/dist/core/gateway-trust.js.map +1 -1
- package/dist/core/gossip-directory.d.ts +1 -1
- package/dist/core/gossip-directory.d.ts.map +1 -1
- package/dist/core/gossip-directory.js +4 -2
- package/dist/core/gossip-directory.js.map +1 -1
- package/dist/core/hub-forwarding.d.ts +4 -18
- package/dist/core/hub-forwarding.d.ts.map +1 -1
- package/dist/core/hub-forwarding.js +2 -41
- package/dist/core/hub-forwarding.js.map +1 -1
- package/dist/core/hub-link.d.ts +58 -0
- package/dist/core/hub-link.d.ts.map +1 -0
- package/dist/core/hub-link.js +118 -0
- package/dist/core/hub-link.js.map +1 -0
- package/dist/core/hub-session.d.ts +14 -15
- package/dist/core/hub-session.d.ts.map +1 -1
- package/dist/core/hub-session.js +30 -51
- package/dist/core/hub-session.js.map +1 -1
- package/dist/core/mesh-graph.d.ts +1 -1
- package/dist/core/mesh-graph.js +1 -1
- package/dist/core/mesh-store.d.ts +20 -5
- package/dist/core/mesh-store.d.ts.map +1 -1
- package/dist/core/mesh-store.js +52 -26
- package/dist/core/mesh-store.js.map +1 -1
- package/dist/core/peer-lifecycle.d.ts +2 -5
- package/dist/core/peer-lifecycle.d.ts.map +1 -1
- package/dist/core/peer-lifecycle.js +0 -1
- package/dist/core/peer-lifecycle.js.map +1 -1
- package/dist/core/receiver-set.d.ts +15 -0
- package/dist/core/receiver-set.d.ts.map +1 -0
- package/dist/core/receiver-set.js +32 -0
- package/dist/core/receiver-set.js.map +1 -0
- package/dist/core/room-router.d.ts +2 -2
- package/dist/core/room-router.d.ts.map +1 -1
- package/dist/core/room-router.js +2 -32
- package/dist/core/room-router.js.map +1 -1
- package/dist/core/shutdown-signals.d.ts +1 -1
- package/dist/core/shutdown-signals.js +1 -1
- package/dist/core/tool.d.ts +1 -1
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +1 -1
- package/dist/core/tool.js.map +1 -1
- package/dist/core/transport.d.ts +7 -7
- package/dist/core/transport.d.ts.map +1 -1
- package/dist/core/wire-mesh-transport.d.ts +7 -3
- package/dist/core/wire-mesh-transport.d.ts.map +1 -1
- package/dist/core/wire-mesh-transport.js +39 -22
- package/dist/core/wire-mesh-transport.js.map +1 -1
- package/dist/test/bridge-mesh.integration.test.js +15 -1
- package/dist/test/bridge-mesh.integration.test.js.map +1 -1
- package/dist/test/chat-controller-mesh-errors.integration.test.js +7 -1
- package/dist/test/chat-controller-mesh-errors.integration.test.js.map +1 -1
- package/dist/test/coordinator-failover-hub.integration.test.d.ts +5 -0
- package/dist/test/coordinator-failover-hub.integration.test.d.ts.map +1 -0
- package/dist/test/coordinator-failover-hub.integration.test.js +78 -0
- package/dist/test/coordinator-failover-hub.integration.test.js.map +1 -0
- package/dist/test/coordinator-failover.integration.test.js +3 -6
- package/dist/test/coordinator-failover.integration.test.js.map +1 -1
- package/dist/test/coordinator-failover.unit.test.js +0 -7
- package/dist/test/coordinator-failover.unit.test.js.map +1 -1
- package/dist/test/gateway-trust.integration.test.d.ts +1 -1
- package/dist/test/gateway-trust.integration.test.js +26 -2
- package/dist/test/gateway-trust.integration.test.js.map +1 -1
- package/dist/test/gateway-trust.unit.test.js +1 -1
- package/dist/test/hub-exposure.integration.test.d.ts +5 -0
- package/dist/test/hub-exposure.integration.test.d.ts.map +1 -0
- package/dist/test/hub-exposure.integration.test.js +121 -0
- package/dist/test/hub-exposure.integration.test.js.map +1 -0
- package/dist/test/hub-helpers.d.ts +20 -3
- package/dist/test/hub-helpers.d.ts.map +1 -1
- package/dist/test/hub-helpers.js +43 -3
- package/dist/test/hub-helpers.js.map +1 -1
- package/dist/test/hub-link.unit.test.d.ts +2 -0
- package/dist/test/hub-link.unit.test.d.ts.map +1 -0
- package/dist/test/hub-link.unit.test.js +210 -0
- package/dist/test/hub-link.unit.test.js.map +1 -0
- package/dist/test/hub-peer-per-store.integration.test.d.ts +9 -0
- package/dist/test/hub-peer-per-store.integration.test.d.ts.map +1 -0
- package/dist/test/hub-peer-per-store.integration.test.js +211 -0
- package/dist/test/hub-peer-per-store.integration.test.js.map +1 -0
- package/dist/test/hub-room-domain-verb-trust-bypass.integration.test.js +2 -2
- package/dist/test/hub-room-domain-verb-trust-bypass.integration.test.js.map +1 -1
- package/dist/test/hub-session-close.integration.test.js +1 -1
- package/dist/test/hub-session-close.integration.test.js.map +1 -1
- package/dist/test/hub-session-dispatch.unit.test.js +1 -66
- package/dist/test/hub-session-dispatch.unit.test.js.map +1 -1
- package/dist/test/mesh-graph-trace.integration.test.js +2 -2
- package/dist/test/mesh-graph-trace.integration.test.js.map +1 -1
- package/dist/test/mesh-smoke.runner.js +1 -3
- package/dist/test/mesh-smoke.runner.js.map +1 -1
- package/dist/test/mesh-store-delivery-listeners.integration.test.d.ts +2 -0
- package/dist/test/mesh-store-delivery-listeners.integration.test.d.ts.map +1 -0
- package/dist/test/mesh-store-delivery-listeners.integration.test.js +160 -0
- package/dist/test/mesh-store-delivery-listeners.integration.test.js.map +1 -0
- package/dist/test/mesh-store-orchestration.unit.test.js +11 -41
- package/dist/test/mesh-store-orchestration.unit.test.js.map +1 -1
- package/dist/test/peer-lifecycle.unit.test.js +0 -8
- package/dist/test/peer-lifecycle.unit.test.js.map +1 -1
- package/dist/test/principal-membership-trust.integration.test.js +8 -1
- package/dist/test/principal-membership-trust.integration.test.js.map +1 -1
- package/dist/test/room-join-by-bare-name.integration.test.js +1 -1
- package/dist/test/room-join-by-bare-name.integration.test.js.map +1 -1
- package/dist/test/test-transport.d.ts +1 -1
- package/dist/test/test-transport.d.ts.map +1 -1
- package/dist/test/test-transport.js +1 -1
- package/dist/test/test-transport.js.map +1 -1
- package/dist/test/wire-mesh-transport-hub.integration.test.d.ts +1 -1
- package/dist/test/wire-mesh-transport-hub.integration.test.js +96 -9
- package/dist/test/wire-mesh-transport-hub.integration.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/core/coordinator-gateway.d.ts +0 -25
- package/dist/core/coordinator-gateway.d.ts.map +0 -1
- package/dist/core/coordinator-gateway.js +0 -34
- package/dist/core/coordinator-gateway.js.map +0 -1
- package/dist/test/coordinator-gateway.unit.test.d.ts +0 -2
- package/dist/test/coordinator-gateway.unit.test.d.ts.map +0 -1
- package/dist/test/coordinator-gateway.unit.test.js +0 -88
- package/dist/test/coordinator-gateway.unit.test.js.map +0 -1
- package/dist/test/gateway-forwarding.integration.test.d.ts +0 -9
- package/dist/test/gateway-forwarding.integration.test.d.ts.map +0 -1
- package/dist/test/gateway-forwarding.integration.test.js +0 -226
- package/dist/test/gateway-forwarding.integration.test.js.map +0 -1
- package/dist/test/hub-forwarding.unit.test.d.ts +0 -2
- package/dist/test/hub-forwarding.unit.test.d.ts.map +0 -1
- package/dist/test/hub-forwarding.unit.test.js +0 -102
- package/dist/test/hub-forwarding.unit.test.js.map +0 -1
- package/dist/test/hub-todevice-routing.integration.test.d.ts +0 -7
- package/dist/test/hub-todevice-routing.integration.test.d.ts.map +0 -1
- package/dist/test/hub-todevice-routing.integration.test.js +0 -121
- package/dist/test/hub-todevice-routing.integration.test.js.map +0 -1
|
@@ -63,7 +63,7 @@ export async function wireTestTransport(store, options) {
|
|
|
63
63
|
const { slot: resolvedSlot } = await wireTransportInternal(store, options);
|
|
64
64
|
return resolvedSlot;
|
|
65
65
|
}
|
|
66
|
-
/** Same wiring as wireTestTransport, but also returns the constructed WireMeshTransport instance itself -- needed by any test that has to reach transport.hub directly (
|
|
66
|
+
/** Same wiring as wireTestTransport, but also returns the constructed WireMeshTransport instance itself -- needed by any test that has to reach transport.hub directly (hub.connect, hub.peers(), hub.isConnected), since MeshStore's own join of the hub goes through the transport's joinHub (agent-comms#192's own hub-mode room-verb-gating tests are the first to need this, alongside the real room verb handlers store.roomVerbHandlers already wires in). */
|
|
67
67
|
export async function wireTestTransportWithHub(store, options) {
|
|
68
68
|
return wireTransportInternal(store, options);
|
|
69
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-transport.js","sourceRoot":"","sources":["../../src/test/test-transport.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"test-transport.js","sourceRoot":"","sources":["../../src/test/test-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WireMeshTransport
|
|
2
|
+
* WireMeshTransport joinHub (agent-comms#154, per store since agent-comms#293) -- split out of wire-mesh-transport.test.ts to stay under this repo's max-lines cap, the same reason wire-mesh-transport-shutdown-unref.test.ts was split. Uses the real relay-hub-over-ws harness (hub-helpers.ts) rather than a mocked HubSession, since the behaviour under test is specifically that joinHub drives the real HubSession instance the transport constructs for itself.
|
|
3
3
|
*/
|
|
4
4
|
export {};
|
|
5
5
|
//# sourceMappingURL=wire-mesh-transport-hub.integration.test.d.ts.map
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WireMeshTransport
|
|
2
|
+
* WireMeshTransport joinHub (agent-comms#154, per store since agent-comms#293) -- split out of wire-mesh-transport.test.ts to stay under this repo's max-lines cap, the same reason wire-mesh-transport-shutdown-unref.test.ts was split. Uses the real relay-hub-over-ws harness (hub-helpers.ts) rather than a mocked HubSession, since the behaviour under test is specifically that joinHub drives the real HubSession instance the transport constructs for itself.
|
|
3
3
|
*/
|
|
4
4
|
import { test, describe, expect } from "vitest";
|
|
5
5
|
import { generateIdentity } from "../core/identity.js";
|
|
6
|
+
import { GatewayTrust } from "../core/gateway-trust.js";
|
|
6
7
|
import { WireMeshTransport } from "../core/wire-mesh-transport.js";
|
|
7
|
-
import { realHubOverWs, waitForCondition } from "./hub-helpers.js";
|
|
8
|
+
import { realHubOverWs, unreachableHubUrl, waitForCondition, } from "./hub-helpers.js";
|
|
9
|
+
/** A device-id is a 64-character lowercase hex SHA-256 digest. */
|
|
10
|
+
const DEVICE_ID_HEX_LENGTH = 64;
|
|
11
|
+
/** Long enough for HubLink's first redial delay plus a real reconnect, but short of the test timeout. */
|
|
12
|
+
const HUB_RETURN_TIMEOUT_MS = 10_000;
|
|
13
|
+
/** Gossip tick short enough for several to land inside GOSSIP_WATCH_MS. */
|
|
14
|
+
const GOSSIP_TICK_MS = 50;
|
|
15
|
+
/** How long gossip is watched after the last redial for a send to a dead session. */
|
|
16
|
+
const GOSSIP_WATCH_MS = 500;
|
|
17
|
+
/** Hub outages the transport must ride out before the watch. */
|
|
18
|
+
const HUB_BOUNCES = 2;
|
|
8
19
|
function noopEvents() {
|
|
9
20
|
return {
|
|
10
21
|
onMessage: () => undefined,
|
|
@@ -20,16 +31,16 @@ function noopEvents() {
|
|
|
20
31
|
onDeviceReachable: () => undefined,
|
|
21
32
|
};
|
|
22
33
|
}
|
|
23
|
-
describe("WireMeshTransport
|
|
24
|
-
test("
|
|
34
|
+
describe("WireMeshTransport joinHub", () => {
|
|
35
|
+
test("joinHub dials the hub in the background and shutdown drops the session, observable on the hub's own connection count", async () => {
|
|
25
36
|
const hub = await realHubOverWs();
|
|
26
37
|
const transport = new WireMeshTransport(noopEvents(), generateIdentity());
|
|
27
38
|
try {
|
|
28
39
|
expect(transport.hub.isConnected).toBe(false);
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
transport.joinHub(hub.url, () => true);
|
|
41
|
+
await waitForCondition(() => transport.hub.isConnected);
|
|
31
42
|
await waitForCondition(() => hub.connectionCount() === 1);
|
|
32
|
-
await transport.
|
|
43
|
+
await transport.shutdown();
|
|
33
44
|
expect(transport.hub.isConnected).toBe(false);
|
|
34
45
|
await waitForCondition(() => hub.connectionCount() === 0);
|
|
35
46
|
}
|
|
@@ -38,14 +49,90 @@ describe("WireMeshTransport connectHub/disconnectHub", () => {
|
|
|
38
49
|
await hub.close();
|
|
39
50
|
}
|
|
40
51
|
});
|
|
41
|
-
test("
|
|
52
|
+
test("joinHub returns before the hub has answered, and an unreachable hub is reported without throwing", async () => {
|
|
53
|
+
const errors = [];
|
|
54
|
+
const transport = new WireMeshTransport({
|
|
55
|
+
...noopEvents(),
|
|
56
|
+
onError: (error) => {
|
|
57
|
+
errors.push(error);
|
|
58
|
+
},
|
|
59
|
+
}, generateIdentity());
|
|
60
|
+
try {
|
|
61
|
+
transport.joinHub(await unreachableHubUrl(), () => true);
|
|
62
|
+
expect(transport.hub.isConnected).toBe(false);
|
|
63
|
+
await waitForCondition(() => errors.length > 0);
|
|
64
|
+
expect(transport.hub.isConnected).toBe(false);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await transport.shutdown();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
test("joinHub dials again once a hub that went away comes back on the same address", async () => {
|
|
71
|
+
const first = await realHubOverWs();
|
|
42
72
|
const transport = new WireMeshTransport(noopEvents(), generateIdentity());
|
|
73
|
+
let second;
|
|
43
74
|
try {
|
|
44
|
-
|
|
75
|
+
transport.joinHub(first.url, () => true);
|
|
76
|
+
await waitForCondition(() => transport.hub.isConnected);
|
|
77
|
+
await first.close();
|
|
78
|
+
await waitForCondition(() => !transport.hub.isConnected);
|
|
79
|
+
second = await realHubOverWs({ port: first.port });
|
|
80
|
+
await waitForCondition(() => transport.hub.isConnected, HUB_RETURN_TIMEOUT_MS);
|
|
81
|
+
await waitForCondition(() => second?.connectionCount() === 1);
|
|
45
82
|
}
|
|
46
83
|
finally {
|
|
47
84
|
await transport.shutdown();
|
|
85
|
+
await second?.close();
|
|
48
86
|
}
|
|
49
87
|
});
|
|
88
|
+
test("a session the hub ended is forgotten, so gossip after a redial reaches only the live session", async () => {
|
|
89
|
+
const errors = [];
|
|
90
|
+
const trust = new GatewayTrust();
|
|
91
|
+
trust.add("f".repeat(DEVICE_ID_HEX_LENGTH));
|
|
92
|
+
const transport = new WireMeshTransport({
|
|
93
|
+
...noopEvents(),
|
|
94
|
+
onError: (error) => {
|
|
95
|
+
errors.push(error);
|
|
96
|
+
},
|
|
97
|
+
}, generateIdentity(), {
|
|
98
|
+
gatewayTrust: trust,
|
|
99
|
+
getCurrentPresence: () => "active",
|
|
100
|
+
getSelfAgentAdvert: () => ({
|
|
101
|
+
name: "gossip-after-redial",
|
|
102
|
+
harness: "test",
|
|
103
|
+
cwd: "/test",
|
|
104
|
+
pid: process.pid,
|
|
105
|
+
startedAt: "2026-01-01T00:00:00.000Z",
|
|
106
|
+
tags: [],
|
|
107
|
+
subscribedRooms: [],
|
|
108
|
+
}),
|
|
109
|
+
presenceReadvertiseIntervalMs: GOSSIP_TICK_MS,
|
|
110
|
+
});
|
|
111
|
+
let hub = await realHubOverWs();
|
|
112
|
+
const { port } = hub;
|
|
113
|
+
try {
|
|
114
|
+
transport.joinHub(hub.url, () => true);
|
|
115
|
+
await waitForCondition(() => transport.hub.isConnected);
|
|
116
|
+
for (let bounce = 0; bounce < HUB_BOUNCES; bounce += 1) {
|
|
117
|
+
await hub.close();
|
|
118
|
+
await waitForCondition(() => !transport.hub.isConnected);
|
|
119
|
+
hub = await realHubOverWs({ port });
|
|
120
|
+
await waitForCondition(() => transport.hub.isConnected, HUB_RETURN_TIMEOUT_MS);
|
|
121
|
+
}
|
|
122
|
+
const errorsBeforeWatch = errors.length;
|
|
123
|
+
await new Promise((resolve) => {
|
|
124
|
+
setTimeout(resolve, GOSSIP_WATCH_MS);
|
|
125
|
+
});
|
|
126
|
+
expect(errors.slice(errorsBeforeWatch)).toEqual([]);
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
await transport.shutdown();
|
|
130
|
+
await hub.close();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
test("shutdown is safe when joinHub was never called", async () => {
|
|
134
|
+
const transport = new WireMeshTransport(noopEvents(), generateIdentity());
|
|
135
|
+
await expect(transport.shutdown()).resolves.toBeUndefined();
|
|
136
|
+
});
|
|
50
137
|
});
|
|
51
138
|
//# sourceMappingURL=wire-mesh-transport-hub.integration.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wire-mesh-transport-hub.integration.test.js","sourceRoot":"","sources":["../../src/test/wire-mesh-transport-hub.integration.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,
|
|
1
|
+
{"version":3,"file":"wire-mesh-transport-hub.integration.test.js","sourceRoot":"","sources":["../../src/test/wire-mesh-transport-hub.integration.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,kEAAkE;AAClE,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC,yGAAyG;AACzG,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAErC,2EAA2E;AAC3E,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B,qFAAqF;AACrF,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B,gEAAgE;AAChE,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB,SAAS,UAAU;IACjB,OAAO;QACL,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS;QAC1B,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS;QAChC,kBAAkB,EAAE,GAAG,EAAE,CAAC,SAAS;QACnC,cAAc,EAAE,GAAG,EAAE,CAAC,SAAS;QAC/B,mBAAmB,EAAE,GAAG,EAAE,CAAC,SAAS;QACpC,UAAU,EAAE,GAAG,EAAE,CAAC,SAAS;QAC3B,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;QAC7B,mBAAmB,EAAE,GAAG,EAAE,CAAC,SAAS;QACpC,oBAAoB,EAAE,GAAG,EAAE,CAAC,SAAS;QACrC,gBAAgB,EAAE,GAAG,EAAE,CAAC,SAAS;QACjC,iBAAiB,EAAE,GAAG,EAAE,CAAC,SAAS;KACnC,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,IAAI,CAAC,sHAAsH,EAAE,KAAK,IAAI,EAAE;QACtI,MAAM,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,UAAU,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC;YACH,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAE9C,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YAEvC,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACxD,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC,CAAC;YAE1D,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;YAE3B,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9C,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC,CAAC;QAC5D,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kGAAkG,EAAE,KAAK,IAAI,EAAE;QAClH,MAAM,MAAM,GAAY,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,iBAAiB,CACrC;YACE,GAAG,UAAU,EAAE;YACf,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;SACF,EACD,gBAAgB,EAAE,CACnB,CAAC;QACF,IAAI,CAAC;YACH,SAAS,CAAC,OAAO,CAAC,MAAM,iBAAiB,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YAEzD,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9C,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC9F,MAAM,KAAK,GAAG,MAAM,aAAa,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,UAAU,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1E,IAAI,MAA6D,CAAC;QAClE,IAAI,CAAC;YACH,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAExD,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;YACpB,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAEzD,MAAM,GAAG,MAAM,aAAa,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACnD,MAAM,gBAAgB,CACpB,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAC/B,qBAAqB,CACtB,CAAC;YACF,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC,CAAC;QAChE,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC3B,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8FAA8F,EAAE,KAAK,IAAI,EAAE;QAC9G,MAAM,MAAM,GAAY,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;QACjC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,IAAI,iBAAiB,CACrC;YACE,GAAG,UAAU,EAAE;YACf,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;SACF,EACD,gBAAgB,EAAE,EAClB;YACE,YAAY,EAAE,KAAK;YACnB,kBAAkB,EAAE,GAAG,EAAE,CAAC,QAAQ;YAClC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;gBACzB,IAAI,EAAE,qBAAqB;gBAC3B,OAAO,EAAE,MAAM;gBACf,GAAG,EAAE,OAAO;gBACZ,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,SAAS,EAAE,0BAA0B;gBACrC,IAAI,EAAE,EAAE;gBACR,eAAe,EAAE,EAAE;aACpB,CAAC;YACF,6BAA6B,EAAE,cAAc;SAC9C,CACF,CAAC;QACF,IAAI,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;QAChC,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;QACrB,IAAI,CAAC;YACH,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAExD,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC;gBACvD,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;gBAClB,MAAM,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBACzD,GAAG,GAAG,MAAM,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpC,MAAM,gBAAgB,CACpB,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAC/B,qBAAqB,CACtB,CAAC;YACJ,CAAC;YACD,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC;YACxC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,UAAU,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;YACvC,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC3B,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,UAAU,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1E,MAAM,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CoordinatorGateway — attaches the cross-machine gateway role to this machine's local coordinator (agent-comms#154, agent-comms#153's first leg). A bridge that becomes the mesh's local coordinator, whether by a fresh bind (MeshStore's own init()) or a takeover (PeerLifecycle's own handleBecomeCoordinator), also becomes the machine's gateway: it dials the hub and holds the connection for as long as it holds the coordinator role. Losing the role, gracefully or by crash, drops the connection; the next coordinator re-dials as part of taking over. Hub-side state is therefore rebuilt from scratch on every takeover -- messages in flight during the gap are lost, the same loss class as a coordinator crash today, now on the data path. Forwarding local agents onto the hub and merging its directory back (agent-comms#155) is deliberately not this class's concern; it owns only the connection lifecycle.
|
|
3
|
-
*/
|
|
4
|
-
export interface CoordinatorGatewayDeps {
|
|
5
|
-
/** The hub URL this machine's gateway dials -- configuration, defaulting to DEFAULT_HUB_URL (mesh-store-shared.ts). */
|
|
6
|
-
hubUrl: string;
|
|
7
|
-
/** Dials the hub. Backed by the transport's own optional connectHub -- a transport with no gateway capability is never asked to redial by anything else in this class. */
|
|
8
|
-
connectHub: (url: string) => Promise<void>;
|
|
9
|
-
/** Drops the held hub connection, if any. Backed by the transport's own optional disconnectHub. */
|
|
10
|
-
disconnectHub: () => Promise<void>;
|
|
11
|
-
/** Reports a hub-dial failure. Never invoked for anything else -- onBecameCoordinator's own guarantee (see its doc comment) is that a hub problem is always reported this way, never thrown, so this is the only signal a caller gets that the gateway role didn't actually connect. */
|
|
12
|
-
onError?: (error: Error) => void;
|
|
13
|
-
}
|
|
14
|
-
export declare class CoordinatorGateway {
|
|
15
|
-
private readonly deps;
|
|
16
|
-
private connected;
|
|
17
|
-
constructor(deps: Readonly<CoordinatorGatewayDeps>);
|
|
18
|
-
/** Whether this side currently holds the gateway role: it has dialled the hub and has not since lost coordinator status. */
|
|
19
|
-
get isConnected(): boolean;
|
|
20
|
-
/** Dials the hub for this machine's gateway role. Idempotent: a call while already connected is a no-op, since nothing in this codebase's own coordinator-election machinery re-fires "became coordinator" without an intervening onLostCoordinator -- this guard is defensive, not a known double-fire path. Never throws: local coordinator election (the whole reason this side is calling this at all) must not depend on the hub being reachable, so a dial failure is reported via deps.onError and swallowed here, leaving isConnected false so a later onBecameCoordinator call retries rather than being blocked by the earlier failure's own idempotency guard. */
|
|
21
|
-
onBecameCoordinator(): Promise<void>;
|
|
22
|
-
/** Drops this machine's held hub connection. A no-op if this side never became the gateway, or already lost the role -- MeshStore.shutdown() calls this unconditionally regardless of coordinator status, so this guard is what makes that safe rather than a redundant extra close. */
|
|
23
|
-
onLostCoordinator(): Promise<void>;
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=coordinator-gateway.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"coordinator-gateway.d.ts","sourceRoot":"","sources":["../../src/core/coordinator-gateway.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,sBAAsB;IACrC,uHAAuH;IACvH,MAAM,EAAE,MAAM,CAAC;IACf,0KAA0K;IAC1K,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,mGAAmG;IACnG,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,wRAAwR;IACxR,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,qBAAa,kBAAkB;IAGjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,SAAS,CAAS;gBAEG,IAAI,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IAEnE,4HAA4H;IAC5H,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,6oBAA6oB;IACvoB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAY1C,wRAAwR;IAClR,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;CAKzC"}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CoordinatorGateway — attaches the cross-machine gateway role to this machine's local coordinator (agent-comms#154, agent-comms#153's first leg). A bridge that becomes the mesh's local coordinator, whether by a fresh bind (MeshStore's own init()) or a takeover (PeerLifecycle's own handleBecomeCoordinator), also becomes the machine's gateway: it dials the hub and holds the connection for as long as it holds the coordinator role. Losing the role, gracefully or by crash, drops the connection; the next coordinator re-dials as part of taking over. Hub-side state is therefore rebuilt from scratch on every takeover -- messages in flight during the gap are lost, the same loss class as a coordinator crash today, now on the data path. Forwarding local agents onto the hub and merging its directory back (agent-comms#155) is deliberately not this class's concern; it owns only the connection lifecycle.
|
|
3
|
-
*/
|
|
4
|
-
export class CoordinatorGateway {
|
|
5
|
-
deps;
|
|
6
|
-
connected = false;
|
|
7
|
-
constructor(deps) {
|
|
8
|
-
this.deps = deps;
|
|
9
|
-
}
|
|
10
|
-
/** Whether this side currently holds the gateway role: it has dialled the hub and has not since lost coordinator status. */
|
|
11
|
-
get isConnected() {
|
|
12
|
-
return this.connected;
|
|
13
|
-
}
|
|
14
|
-
/** Dials the hub for this machine's gateway role. Idempotent: a call while already connected is a no-op, since nothing in this codebase's own coordinator-election machinery re-fires "became coordinator" without an intervening onLostCoordinator -- this guard is defensive, not a known double-fire path. Never throws: local coordinator election (the whole reason this side is calling this at all) must not depend on the hub being reachable, so a dial failure is reported via deps.onError and swallowed here, leaving isConnected false so a later onBecameCoordinator call retries rather than being blocked by the earlier failure's own idempotency guard. */
|
|
15
|
-
async onBecameCoordinator() {
|
|
16
|
-
if (this.connected)
|
|
17
|
-
return;
|
|
18
|
-
try {
|
|
19
|
-
await this.deps.connectHub(this.deps.hubUrl);
|
|
20
|
-
this.connected = true;
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
this.deps.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/** Drops this machine's held hub connection. A no-op if this side never became the gateway, or already lost the role -- MeshStore.shutdown() calls this unconditionally regardless of coordinator status, so this guard is what makes that safe rather than a redundant extra close. */
|
|
27
|
-
async onLostCoordinator() {
|
|
28
|
-
if (!this.connected)
|
|
29
|
-
return;
|
|
30
|
-
this.connected = false;
|
|
31
|
-
await this.deps.disconnectHub();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
//# sourceMappingURL=coordinator-gateway.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"coordinator-gateway.js","sourceRoot":"","sources":["../../src/core/coordinator-gateway.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,MAAM,OAAO,kBAAkB;IAGA;IAFrB,SAAS,GAAG,KAAK,CAAC;IAE1B,YAA6B,IAAsC;QAAtC,SAAI,GAAJ,IAAI,CAAkC;IAAG,CAAC;IAEvE,4HAA4H;IAC5H,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,6oBAA6oB;IAC7oB,KAAK,CAAC,mBAAmB;QACvB,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CACjB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,wRAAwR;IACxR,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAC5B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;IAClC,CAAC;CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"coordinator-gateway.unit.test.d.ts","sourceRoot":"","sources":["../../src/test/coordinator-gateway.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Direct unit tests for CoordinatorGateway -- the connect-hub-on-become-coordinator / disconnect-hub-on-lose-coordinator wiring (agent-comms#154), tested against injected connectHub/disconnectHub closures rather than a real transport or hub socket, mirroring peer-lifecycle.test.ts's own DI-based approach.
|
|
3
|
-
*/
|
|
4
|
-
import { describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { CoordinatorGateway, } from "../core/coordinator-gateway.js";
|
|
6
|
-
const HUB_URL = "wss://mesh.example.test/";
|
|
7
|
-
function makeHarness() {
|
|
8
|
-
const connectHub = vi.fn().mockResolvedValue(undefined);
|
|
9
|
-
const disconnectHub = vi.fn().mockResolvedValue(undefined);
|
|
10
|
-
const onError = vi.fn();
|
|
11
|
-
const deps = {
|
|
12
|
-
hubUrl: HUB_URL,
|
|
13
|
-
connectHub,
|
|
14
|
-
disconnectHub,
|
|
15
|
-
onError,
|
|
16
|
-
};
|
|
17
|
-
return {
|
|
18
|
-
gateway: new CoordinatorGateway(deps),
|
|
19
|
-
connectHub,
|
|
20
|
-
disconnectHub,
|
|
21
|
-
onError,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
describe("CoordinatorGateway — onBecameCoordinator", () => {
|
|
25
|
-
it("dials the configured hub URL", async () => {
|
|
26
|
-
const { gateway, connectHub } = makeHarness();
|
|
27
|
-
await gateway.onBecameCoordinator();
|
|
28
|
-
expect(connectHub).toHaveBeenCalledWith(HUB_URL);
|
|
29
|
-
expect(connectHub).toHaveBeenCalledTimes(1);
|
|
30
|
-
});
|
|
31
|
-
it("marks isConnected true once dialled", async () => {
|
|
32
|
-
const { gateway } = makeHarness();
|
|
33
|
-
expect(gateway.isConnected).toBe(false);
|
|
34
|
-
await gateway.onBecameCoordinator();
|
|
35
|
-
expect(gateway.isConnected).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
it("is idempotent -- a second call while already connected does not redial", async () => {
|
|
38
|
-
const { gateway, connectHub } = makeHarness();
|
|
39
|
-
await gateway.onBecameCoordinator();
|
|
40
|
-
await gateway.onBecameCoordinator();
|
|
41
|
-
expect(connectHub).toHaveBeenCalledTimes(1);
|
|
42
|
-
});
|
|
43
|
-
it("reports a dial failure via onError rather than throwing -- local coordinator election must not depend on hub reachability", async () => {
|
|
44
|
-
const { gateway, connectHub, onError } = makeHarness();
|
|
45
|
-
const dialError = new Error("ECONNREFUSED");
|
|
46
|
-
connectHub.mockRejectedValueOnce(dialError);
|
|
47
|
-
await expect(gateway.onBecameCoordinator()).resolves.toBeUndefined();
|
|
48
|
-
expect(onError).toHaveBeenCalledWith(dialError);
|
|
49
|
-
});
|
|
50
|
-
it("does not mark itself connected after a failed dial, so a later call can retry", async () => {
|
|
51
|
-
const { gateway, connectHub } = makeHarness();
|
|
52
|
-
connectHub.mockRejectedValueOnce(new Error("ECONNREFUSED"));
|
|
53
|
-
await gateway.onBecameCoordinator();
|
|
54
|
-
expect(gateway.isConnected).toBe(false);
|
|
55
|
-
await gateway.onBecameCoordinator();
|
|
56
|
-
expect(gateway.isConnected).toBe(true);
|
|
57
|
-
expect(connectHub).toHaveBeenCalledTimes(2);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
describe("CoordinatorGateway — onLostCoordinator", () => {
|
|
61
|
-
it("drops the hub connection when one is held", async () => {
|
|
62
|
-
const { gateway, disconnectHub } = makeHarness();
|
|
63
|
-
await gateway.onBecameCoordinator();
|
|
64
|
-
await gateway.onLostCoordinator();
|
|
65
|
-
expect(disconnectHub).toHaveBeenCalledTimes(1);
|
|
66
|
-
expect(gateway.isConnected).toBe(false);
|
|
67
|
-
});
|
|
68
|
-
it("is a no-op when this side never became the gateway", async () => {
|
|
69
|
-
const { gateway, disconnectHub } = makeHarness();
|
|
70
|
-
await gateway.onLostCoordinator();
|
|
71
|
-
expect(disconnectHub).not.toHaveBeenCalled();
|
|
72
|
-
});
|
|
73
|
-
it("is a no-op on a second call after already losing the role", async () => {
|
|
74
|
-
const { gateway, disconnectHub } = makeHarness();
|
|
75
|
-
await gateway.onBecameCoordinator();
|
|
76
|
-
await gateway.onLostCoordinator();
|
|
77
|
-
await gateway.onLostCoordinator();
|
|
78
|
-
expect(disconnectHub).toHaveBeenCalledTimes(1);
|
|
79
|
-
});
|
|
80
|
-
it("allows redialling after losing and regaining the role", async () => {
|
|
81
|
-
const { gateway, connectHub } = makeHarness();
|
|
82
|
-
await gateway.onBecameCoordinator();
|
|
83
|
-
await gateway.onLostCoordinator();
|
|
84
|
-
await gateway.onBecameCoordinator();
|
|
85
|
-
expect(connectHub).toHaveBeenCalledTimes(2);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
//# sourceMappingURL=coordinator-gateway.unit.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"coordinator-gateway.unit.test.js","sourceRoot":"","sources":["../../src/test/coordinator-gateway.unit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the gateway actually forwarding between the local mesh and the hub (agent-comms#155), the core leg of the cross-machine mesh epic (#153) on top of #154's own connection-lifecycle-only gateway. Two independent local meshes ("machine A" and "machine B"), each its own coordinator dialling a shared real relay hub (createRelayHub, the same domain logic the production mesh.exadev.io Durable Object runs, served over local WebSockets via hub-helpers.ts) -- neither mesh is ever directly connected to the other, so anything either side learns about the other can only have arrived via the hub.
|
|
3
|
-
*
|
|
4
|
-
* Machine A's coordinator (a1) additionally has a second, ordinary local peer (a2) -- proving outbound advertisement and the remote-directory merge work for a local peer that is NOT itself the gateway, not just for the gateway's own agent (which #154 already exercised end-to-end before this issue).
|
|
5
|
-
*
|
|
6
|
-
* Every gateway here explicitly trusts the specific remote device-ids it needs to (agent-comms#156's own gateway allowlist, added after these tests first landed): the allowlist is deny-all by default, so every test below establishes trust before expecting forwarding/merge/routing to happen -- see gateway-trust.integration.test.ts for the deny-by-default behaviour itself (untrusted forwarding/merge/routing all failing) rather than re-proving it per test here.
|
|
7
|
-
*/
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=gateway-forwarding.integration.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-forwarding.integration.test.d.ts","sourceRoot":"","sources":["../../src/test/gateway-forwarding.integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the gateway actually forwarding between the local mesh and the hub (agent-comms#155), the core leg of the cross-machine mesh epic (#153) on top of #154's own connection-lifecycle-only gateway. Two independent local meshes ("machine A" and "machine B"), each its own coordinator dialling a shared real relay hub (createRelayHub, the same domain logic the production mesh.exadev.io Durable Object runs, served over local WebSockets via hub-helpers.ts) -- neither mesh is ever directly connected to the other, so anything either side learns about the other can only have arrived via the hub.
|
|
3
|
-
*
|
|
4
|
-
* Machine A's coordinator (a1) additionally has a second, ordinary local peer (a2) -- proving outbound advertisement and the remote-directory merge work for a local peer that is NOT itself the gateway, not just for the gateway's own agent (which #154 already exercised end-to-end before this issue).
|
|
5
|
-
*
|
|
6
|
-
* Every gateway here explicitly trusts the specific remote device-ids it needs to (agent-comms#156's own gateway allowlist, added after these tests first landed): the allowlist is deny-all by default, so every test below establishes trust before expecting forwarding/merge/routing to happen -- see gateway-trust.integration.test.ts for the deny-by-default behaviour itself (untrusted forwarding/merge/routing all failing) rather than re-proving it per test here.
|
|
7
|
-
*/
|
|
8
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
9
|
-
import { MeshStore } from "../core/mesh-store.js";
|
|
10
|
-
import { dmRoomPath } from "../core/room-path.js";
|
|
11
|
-
import { realHubOverWs, TeardownStack } from "./hub-helpers.js";
|
|
12
|
-
import { wireTestTransport } from "./test-transport.js";
|
|
13
|
-
let nextPort = 22_400;
|
|
14
|
-
function freshPort() {
|
|
15
|
-
nextPort += 1;
|
|
16
|
-
return nextPort;
|
|
17
|
-
}
|
|
18
|
-
/** Short enough that a gossip re-advertisement (which carries the registerAgent'd agent/self extension) fires within a test's own poll budget, unlike the 20s production default. */
|
|
19
|
-
const FAST_GOSSIP_INTERVAL_MS = 50;
|
|
20
|
-
const POLL_ATTEMPTS = 100;
|
|
21
|
-
const POLL_INTERVAL_MS = 50;
|
|
22
|
-
const sleep = async (ms) => new Promise((resolve) => {
|
|
23
|
-
setTimeout(resolve, ms);
|
|
24
|
-
});
|
|
25
|
-
/** Polls an async predicate until it holds, matching broadcast-window.integration.test.ts's own established pattern for this -- the shared test-transport.ts waitFor takes a synchronous condition, which can't await a MeshStore's own async listAgents/serialise-derived checks. */
|
|
26
|
-
async function waitFor(what, check) {
|
|
27
|
-
for (let i = 0; i < POLL_ATTEMPTS; i++) {
|
|
28
|
-
if (await check())
|
|
29
|
-
return;
|
|
30
|
-
await sleep(POLL_INTERVAL_MS);
|
|
31
|
-
}
|
|
32
|
-
expect(false, `timed out waiting for ${what}`).toBeTruthy();
|
|
33
|
-
}
|
|
34
|
-
const cleanups = new TeardownStack();
|
|
35
|
-
afterEach(async () => {
|
|
36
|
-
await cleanups.run();
|
|
37
|
-
});
|
|
38
|
-
describe("gateway forwarding", () => {
|
|
39
|
-
it("advertises a local peer's agent (not just the gateway's own) onto the hub, and a separate machine's gateway merges it into its own knownDevices/listAgents", async () => {
|
|
40
|
-
const hub = await realHubOverWs();
|
|
41
|
-
cleanups.push(hub.close);
|
|
42
|
-
const a1 = new MeshStore({ coordinatorPort: freshPort(), hubUrl: hub.url });
|
|
43
|
-
await wireTestTransport(a1, {
|
|
44
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
45
|
-
});
|
|
46
|
-
await a1.init();
|
|
47
|
-
cleanups.push(async () => a1.shutdown());
|
|
48
|
-
const a2 = new MeshStore({
|
|
49
|
-
coordinatorPort: a1.coordinatorPort,
|
|
50
|
-
hubUrl: hub.url,
|
|
51
|
-
});
|
|
52
|
-
await wireTestTransport(a2, {
|
|
53
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
54
|
-
});
|
|
55
|
-
await a2.init();
|
|
56
|
-
cleanups.push(async () => a2.shutdown());
|
|
57
|
-
await a2.registerAgent({
|
|
58
|
-
name: "a2-local-peer",
|
|
59
|
-
harness: "test",
|
|
60
|
-
cwd: "/test/a2",
|
|
61
|
-
pid: process.pid,
|
|
62
|
-
visibility: "visible",
|
|
63
|
-
tags: ["from-a2"],
|
|
64
|
-
});
|
|
65
|
-
const b1 = new MeshStore({ coordinatorPort: freshPort(), hubUrl: hub.url });
|
|
66
|
-
await wireTestTransport(b1, {
|
|
67
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
68
|
-
});
|
|
69
|
-
await b1.init();
|
|
70
|
-
cleanups.push(async () => b1.shutdown());
|
|
71
|
-
// Deny-all by default (agent-comms#156): a1 needs at least one trusted device to forward anything at all onto the hub, and b1 needs a2's own device-id specifically trusted to merge a2's directory entry (trust is keyed per device, not per remote machine -- see GatewayTrust's own class doc).
|
|
72
|
-
a1.addTrustedGateway(b1.peerId);
|
|
73
|
-
b1.addTrustedGateway(a2.peerId);
|
|
74
|
-
await waitFor("b1 (a separate machine's gateway) to learn of a2 (a non-gateway local peer on machine A) via the hub", async () => {
|
|
75
|
-
const agents = await b1.listAgents(b1.peerId);
|
|
76
|
-
return agents.some((agent) => agent.id === a2.peerId);
|
|
77
|
-
});
|
|
78
|
-
const agents = await b1.listAgents(b1.peerId);
|
|
79
|
-
const discovered = agents.find((agent) => agent.id === a2.peerId);
|
|
80
|
-
expect(discovered).toMatchObject({
|
|
81
|
-
id: a2.peerId,
|
|
82
|
-
name: "a2-local-peer",
|
|
83
|
-
harness: "test",
|
|
84
|
-
cwd: "/test/a2",
|
|
85
|
-
visibility: "visible",
|
|
86
|
-
tags: ["from-a2"],
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
it("never advertises a hidden or ghost agent across the hub", async () => {
|
|
90
|
-
const hub = await realHubOverWs();
|
|
91
|
-
cleanups.push(hub.close);
|
|
92
|
-
const a1 = new MeshStore({ coordinatorPort: freshPort(), hubUrl: hub.url });
|
|
93
|
-
await wireTestTransport(a1, {
|
|
94
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
95
|
-
});
|
|
96
|
-
await a1.init();
|
|
97
|
-
cleanups.push(async () => a1.shutdown());
|
|
98
|
-
const aHidden = new MeshStore({
|
|
99
|
-
coordinatorPort: a1.coordinatorPort,
|
|
100
|
-
hubUrl: hub.url,
|
|
101
|
-
});
|
|
102
|
-
await wireTestTransport(aHidden, {
|
|
103
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
104
|
-
});
|
|
105
|
-
await aHidden.init();
|
|
106
|
-
cleanups.push(async () => aHidden.shutdown());
|
|
107
|
-
await aHidden.registerAgent({
|
|
108
|
-
name: "hidden-peer",
|
|
109
|
-
harness: "test",
|
|
110
|
-
cwd: "/test/hidden",
|
|
111
|
-
pid: process.pid,
|
|
112
|
-
visibility: "hidden",
|
|
113
|
-
tags: [],
|
|
114
|
-
});
|
|
115
|
-
const aGhost = new MeshStore({
|
|
116
|
-
coordinatorPort: a1.coordinatorPort,
|
|
117
|
-
hubUrl: hub.url,
|
|
118
|
-
});
|
|
119
|
-
await wireTestTransport(aGhost, {
|
|
120
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
121
|
-
});
|
|
122
|
-
await aGhost.init();
|
|
123
|
-
cleanups.push(async () => aGhost.shutdown());
|
|
124
|
-
await aGhost.registerAgent({
|
|
125
|
-
name: "ghost-peer",
|
|
126
|
-
harness: "test",
|
|
127
|
-
cwd: "/test/ghost",
|
|
128
|
-
pid: process.pid,
|
|
129
|
-
visibility: "ghost",
|
|
130
|
-
tags: [],
|
|
131
|
-
});
|
|
132
|
-
// A visible control peer on the identical mesh, registered after the hidden/ghost ones -- its own arrival on b1's side is the proof gossip genuinely had time to propagate, ruling out "nothing arrived at all" as a false-negative explanation for hidden/ghost never showing up below.
|
|
133
|
-
const aVisible = new MeshStore({
|
|
134
|
-
coordinatorPort: a1.coordinatorPort,
|
|
135
|
-
hubUrl: hub.url,
|
|
136
|
-
});
|
|
137
|
-
await wireTestTransport(aVisible, {
|
|
138
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
139
|
-
});
|
|
140
|
-
await aVisible.init();
|
|
141
|
-
cleanups.push(async () => aVisible.shutdown());
|
|
142
|
-
await aVisible.registerAgent({
|
|
143
|
-
name: "visible-peer",
|
|
144
|
-
harness: "test",
|
|
145
|
-
cwd: "/test/visible",
|
|
146
|
-
pid: process.pid,
|
|
147
|
-
visibility: "visible",
|
|
148
|
-
tags: [],
|
|
149
|
-
});
|
|
150
|
-
const b1 = new MeshStore({ coordinatorPort: freshPort(), hubUrl: hub.url });
|
|
151
|
-
await wireTestTransport(b1, {
|
|
152
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
153
|
-
});
|
|
154
|
-
await b1.init();
|
|
155
|
-
cleanups.push(async () => b1.shutdown());
|
|
156
|
-
// Deny-all by default (agent-comms#156) -- a1 needs at least one trusted device to forward at all, and b1 needs the visible control peer's own device-id trusted so its arrival can prove gossip had time to propagate. Trusting only aVisible (never aHidden/aGhost) doesn't weaken this test: hidden/ghost are already filtered out at the source (forwardAdvertsToHub's own AGENT_SELF_GOSSIP_KEY eligibility check, upstream of the trust gate), so this proves the trust gate isn't the reason they never arrive.
|
|
157
|
-
a1.addTrustedGateway(b1.peerId);
|
|
158
|
-
b1.addTrustedGateway(aVisible.peerId);
|
|
159
|
-
await waitFor("b1 to see the visible control peer, proving gossip had time to propagate", async () => {
|
|
160
|
-
const agents = await b1.listAgents(b1.peerId);
|
|
161
|
-
return agents.some((agent) => agent.id === aVisible.peerId);
|
|
162
|
-
});
|
|
163
|
-
const agents = await b1.listAgents(b1.peerId);
|
|
164
|
-
expect(agents.some((agent) => agent.id === aHidden.peerId)).toBe(false);
|
|
165
|
-
expect(agents.some((agent) => agent.id === aGhost.peerId)).toBe(false);
|
|
166
|
-
});
|
|
167
|
-
it("routes a DM to a remote gateway's own agent through the hub's relay pairing, delivered with the true sender attributed", async () => {
|
|
168
|
-
const hub = await realHubOverWs();
|
|
169
|
-
cleanups.push(hub.close);
|
|
170
|
-
const a1 = new MeshStore({ coordinatorPort: freshPort(), hubUrl: hub.url });
|
|
171
|
-
await wireTestTransport(a1, {
|
|
172
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
173
|
-
});
|
|
174
|
-
await a1.init();
|
|
175
|
-
cleanups.push(async () => a1.shutdown());
|
|
176
|
-
await a1.registerAgent({
|
|
177
|
-
name: "a1-gateway",
|
|
178
|
-
harness: "test",
|
|
179
|
-
cwd: "/test/a1",
|
|
180
|
-
pid: process.pid,
|
|
181
|
-
visibility: "visible",
|
|
182
|
-
tags: [],
|
|
183
|
-
});
|
|
184
|
-
const b1 = new MeshStore({ coordinatorPort: freshPort(), hubUrl: hub.url });
|
|
185
|
-
await wireTestTransport(b1, {
|
|
186
|
-
presenceReadvertiseIntervalMs: FAST_GOSSIP_INTERVAL_MS,
|
|
187
|
-
});
|
|
188
|
-
await b1.init();
|
|
189
|
-
cleanups.push(async () => b1.shutdown());
|
|
190
|
-
await b1.registerAgent({
|
|
191
|
-
name: "b1-gateway",
|
|
192
|
-
harness: "test",
|
|
193
|
-
cwd: "/test/b1",
|
|
194
|
-
pid: process.pid,
|
|
195
|
-
visibility: "visible",
|
|
196
|
-
tags: [],
|
|
197
|
-
});
|
|
198
|
-
// Deny-all by default (agent-comms#156) -- mutual trust: a1 must trust b1 to accept b1's incoming DM request (consume()'s own isTrusted gate on fromDevice), and b1 must trust a1 for sendRoomRequest's own outbound isTrusted gate to route the request (and the later DM) to a1 at all.
|
|
199
|
-
a1.addTrustedGateway(b1.peerId);
|
|
200
|
-
b1.addTrustedGateway(a1.peerId);
|
|
201
|
-
// Two-round DM consent (section 6), exactly like the local-mesh-only dm-admission.integration.test.ts -- b1's own outbound request is what's actually exercised via the new hub-relay fallback, since a1 is not a local peer of b1's own mesh.
|
|
202
|
-
const dmPath = dmRoomPath(b1.peerId, a1.peerId);
|
|
203
|
-
const requestPromise = b1.requestDmAccess(a1.peerId);
|
|
204
|
-
await waitFor("a1 to see b1's pending DM request via the hub", async () => {
|
|
205
|
-
await Promise.resolve();
|
|
206
|
-
return a1.listPendingRoomJoins().length === 1;
|
|
207
|
-
});
|
|
208
|
-
const [pending] = a1.listPendingRoomJoins();
|
|
209
|
-
expect(pending?.roomPath).toBe(dmPath);
|
|
210
|
-
expect(pending?.requesterId).toBe(b1.peerId);
|
|
211
|
-
a1.acceptRoomJoin(dmPath, b1.peerId);
|
|
212
|
-
await requestPromise;
|
|
213
|
-
const message = await b1.sendDm(b1.peerId, a1.peerId, "hello from b1");
|
|
214
|
-
await waitFor("a1 to receive the DM via the hub", async () => {
|
|
215
|
-
const a1Dms = a1.serialise().dms[dmPath] ?? [];
|
|
216
|
-
return a1Dms.some((m) => m.id === message.message.id);
|
|
217
|
-
});
|
|
218
|
-
const delivered = (a1.serialise().dms[dmPath] ?? []).find((m) => m.id === message.message.id);
|
|
219
|
-
expect(delivered).toMatchObject({
|
|
220
|
-
from: b1.peerId,
|
|
221
|
-
to: a1.peerId,
|
|
222
|
-
content: "hello from b1",
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
//# sourceMappingURL=gateway-forwarding.integration.test.js.map
|