libp2p-mesh 2026.5.18 → 2026.5.19

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/dist/src/mesh.js CHANGED
@@ -77,6 +77,8 @@ export function createMeshNetwork(options) {
77
77
  const seenMessages = new Set();
78
78
  const messageHandlers = new Set();
79
79
  const topicHandlers = new Map();
80
+ const peerConnectHandlers = new Set();
81
+ const peerDisconnectHandlers = new Set();
80
82
  function getDHTService() {
81
83
  return state.node?.services?.dht;
82
84
  }
@@ -215,10 +217,22 @@ export function createMeshNetwork(options) {
215
217
  state.node.addEventListener("peer:connect", (evt) => {
216
218
  const peerIdStr = evt.detail.toString();
217
219
  logger?.info?.(`[libp2p-mesh] Peer connected: ${peerIdStr}`);
220
+ for (const handler of peerConnectHandlers) {
221
+ try {
222
+ handler(peerIdStr);
223
+ }
224
+ catch { }
225
+ }
218
226
  });
219
227
  state.node.addEventListener("peer:disconnect", (evt) => {
220
228
  const peerIdStr = evt.detail.toString();
221
229
  logger?.info?.(`[libp2p-mesh] Peer disconnected: ${peerIdStr}`);
230
+ for (const handler of peerDisconnectHandlers) {
231
+ try {
232
+ handler(peerIdStr);
233
+ }
234
+ catch { }
235
+ }
222
236
  });
223
237
  await state.node.handle(PROTOCOL, async ({ stream, connection }) => {
224
238
  try {
@@ -580,6 +594,14 @@ export function createMeshNetwork(options) {
580
594
  stop,
581
595
  sendToPeer,
582
596
  onMessage,
597
+ onPeerConnect(handler) {
598
+ peerConnectHandlers.add(handler);
599
+ return () => { peerConnectHandlers.delete(handler); };
600
+ },
601
+ onPeerDisconnect(handler) {
602
+ peerDisconnectHandlers.add(handler);
603
+ return () => { peerDisconnectHandlers.delete(handler); };
604
+ },
583
605
  publishToTopic,
584
606
  subscribeToTopic,
585
607
  getLocalPeerId,
@@ -24,14 +24,15 @@ export function registerLibp2pMesh(api) {
24
24
  id: "libp2p-mesh",
25
25
  start: async () => {
26
26
  await mesh.start();
27
- // Register local identity so remote peers can route messages back to us
27
+ // Gather local relay identity info
28
28
  const config = api.pluginConfig;
29
29
  const relayChannel = config?.relayChannel;
30
30
  const relayAccountId = config?.relayAccountId;
31
31
  const instanceIdentity = mesh.getInstanceIdentity();
32
32
  const localInstanceId = instanceIdentity?.id;
33
+ const localPeerId = mesh.getLocalPeerId();
34
+ // Register local identity so remote peers can route messages back to us
33
35
  if (relayChannel && relayAccountId) {
34
- const localPeerId = mesh.getLocalPeerId();
35
36
  const { buildAgentSessionKey } = await import("openclaw/plugin-sdk/core");
36
37
  const sessionKey = buildAgentSessionKey({
37
38
  agentId: api.name,
@@ -48,8 +49,7 @@ export function registerLibp2pMesh(api) {
48
49
  api.logger.info?.(`[libp2p-mesh] Local identity registered: agent=${api.name}, channel=${relayChannel}, account=${relayAccountId}, instanceId=${localInstanceId}`);
49
50
  // Announce identity to all currently-connected peers
50
51
  const identityMsg = buildIdentityMessage(localPeerId, api.name, relayChannel, relayAccountId, localInstanceId);
51
- const connectedPeers = mesh.getConnectedPeers();
52
- for (const peerId of connectedPeers) {
52
+ for (const peerId of mesh.getConnectedPeers()) {
53
53
  try {
54
54
  await mesh.sendToPeer(peerId, JSON.stringify(identityMsg));
55
55
  }
@@ -57,13 +57,24 @@ export function registerLibp2pMesh(api) {
57
57
  // Best-effort; peer may be stale in the connection list
58
58
  }
59
59
  }
60
+ // When a new peer connects, send our identity to them
61
+ mesh.onPeerConnect((peerId) => {
62
+ const msg = buildIdentityMessage(localPeerId, api.name, relayChannel, relayAccountId, localInstanceId);
63
+ mesh.sendToPeer(peerId, JSON.stringify(msg)).catch(() => {
64
+ // Best-effort
65
+ });
66
+ });
67
+ // When a peer disconnects, clean up the identity map
68
+ mesh.onPeerDisconnect((peerId) => {
69
+ peerIdentityMap.unregister(peerId);
70
+ });
60
71
  }
61
72
  // Wire up relay-aware message handler
62
73
  mesh.onMessage((msg) => {
63
74
  handleP2PInbound(msg, buildInboundDeps());
64
75
  });
65
76
  const identity = mesh.getInstanceIdentity();
66
- api.logger.info?.(`[libp2p-mesh] Service started. Peer ID: ${mesh.getLocalPeerId()}`);
77
+ api.logger.info?.(`[libp2p-mesh] Service started. Peer ID: ${localPeerId}`);
67
78
  if (identity) {
68
79
  api.logger.info?.(`[libp2p-mesh] Instance Identity: ${identity.id}`);
69
80
  }
@@ -106,6 +106,8 @@ export interface MeshNetwork {
106
106
  stop(): Promise<void>;
107
107
  sendToPeer(peerId: string, message: string): Promise<void>;
108
108
  onMessage(handler: (msg: P2PMessage) => void): () => void;
109
+ onPeerConnect(handler: (peerId: string) => void): () => void;
110
+ onPeerDisconnect(handler: (peerId: string) => void): () => void;
109
111
  publishToTopic(topic: string, message: string): Promise<void>;
110
112
  subscribeToTopic(topic: string, handler: (msg: string) => void): Promise<void>;
111
113
  getLocalPeerId(): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libp2p-mesh",
3
- "version": "2026.5.18",
3
+ "version": "2026.5.19",
4
4
  "description": "OpenClaw libp2p P2P mesh network plugin for cross-instance agent communication",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/mesh.ts CHANGED
@@ -94,6 +94,8 @@ export function createMeshNetwork(options: {
94
94
  const seenMessages = new Set<string>();
95
95
  const messageHandlers = new Set<(msg: P2PMessage) => void>();
96
96
  const topicHandlers = new Map<string, Set<(msg: string) => void>>();
97
+ const peerConnectHandlers = new Set<(peerId: string) => void>();
98
+ const peerDisconnectHandlers = new Set<(peerId: string) => void>();
97
99
 
98
100
  function getDHTService(): ReturnType<typeof kadDHT> extends (components: infer C) => infer R ? R : never | undefined {
99
101
  return (state.node as any)?.services?.dht;
@@ -258,11 +260,17 @@ export function createMeshNetwork(options: {
258
260
  state.node.addEventListener("peer:connect", (evt) => {
259
261
  const peerIdStr = evt.detail.toString();
260
262
  logger?.info?.(`[libp2p-mesh] Peer connected: ${peerIdStr}`);
263
+ for (const handler of peerConnectHandlers) {
264
+ try { handler(peerIdStr); } catch {}
265
+ }
261
266
  });
262
267
 
263
268
  state.node.addEventListener("peer:disconnect", (evt) => {
264
269
  const peerIdStr = evt.detail.toString();
265
270
  logger?.info?.(`[libp2p-mesh] Peer disconnected: ${peerIdStr}`);
271
+ for (const handler of peerDisconnectHandlers) {
272
+ try { handler(peerIdStr); } catch {}
273
+ }
266
274
  });
267
275
 
268
276
  await state.node.handle(
@@ -667,6 +675,14 @@ export function createMeshNetwork(options: {
667
675
  stop,
668
676
  sendToPeer,
669
677
  onMessage,
678
+ onPeerConnect(handler: (peerId: string) => void): () => void {
679
+ peerConnectHandlers.add(handler);
680
+ return () => { peerConnectHandlers.delete(handler); };
681
+ },
682
+ onPeerDisconnect(handler: (peerId: string) => void): () => void {
683
+ peerDisconnectHandlers.add(handler);
684
+ return () => { peerDisconnectHandlers.delete(handler); };
685
+ },
670
686
  publishToTopic,
671
687
  subscribeToTopic,
672
688
  getLocalPeerId,
package/src/plugin.ts CHANGED
@@ -33,14 +33,16 @@ export function registerLibp2pMesh(api: OpenClawPluginApi) {
33
33
  start: async () => {
34
34
  await mesh.start();
35
35
 
36
- // Register local identity so remote peers can route messages back to us
36
+ // Gather local relay identity info
37
37
  const config = api.pluginConfig as MeshConfig | undefined;
38
38
  const relayChannel = config?.relayChannel;
39
39
  const relayAccountId = config?.relayAccountId;
40
40
  const instanceIdentity = mesh.getInstanceIdentity();
41
41
  const localInstanceId = instanceIdentity?.id;
42
+ const localPeerId = mesh.getLocalPeerId();
43
+
44
+ // Register local identity so remote peers can route messages back to us
42
45
  if (relayChannel && relayAccountId) {
43
- const localPeerId = mesh.getLocalPeerId();
44
46
  const { buildAgentSessionKey } = await import("openclaw/plugin-sdk/core");
45
47
  const sessionKey = buildAgentSessionKey({
46
48
  agentId: api.name,
@@ -66,14 +68,32 @@ export function registerLibp2pMesh(api: OpenClawPluginApi) {
66
68
  relayAccountId,
67
69
  localInstanceId,
68
70
  );
69
- const connectedPeers = mesh.getConnectedPeers();
70
- for (const peerId of connectedPeers) {
71
+ for (const peerId of mesh.getConnectedPeers()) {
71
72
  try {
72
73
  await mesh.sendToPeer(peerId, JSON.stringify(identityMsg));
73
74
  } catch {
74
75
  // Best-effort; peer may be stale in the connection list
75
76
  }
76
77
  }
78
+
79
+ // When a new peer connects, send our identity to them
80
+ mesh.onPeerConnect((peerId: string) => {
81
+ const msg = buildIdentityMessage(
82
+ localPeerId,
83
+ api.name,
84
+ relayChannel,
85
+ relayAccountId,
86
+ localInstanceId,
87
+ );
88
+ mesh.sendToPeer(peerId, JSON.stringify(msg)).catch(() => {
89
+ // Best-effort
90
+ });
91
+ });
92
+
93
+ // When a peer disconnects, clean up the identity map
94
+ mesh.onPeerDisconnect((peerId: string) => {
95
+ peerIdentityMap.unregister(peerId);
96
+ });
77
97
  }
78
98
 
79
99
  // Wire up relay-aware message handler
@@ -82,7 +102,7 @@ export function registerLibp2pMesh(api: OpenClawPluginApi) {
82
102
  });
83
103
 
84
104
  const identity = mesh.getInstanceIdentity();
85
- api.logger.info?.(`[libp2p-mesh] Service started. Peer ID: ${mesh.getLocalPeerId()}`);
105
+ api.logger.info?.(`[libp2p-mesh] Service started. Peer ID: ${localPeerId}`);
86
106
  if (identity) {
87
107
  api.logger.info?.(`[libp2p-mesh] Instance Identity: ${identity.id}`);
88
108
  }
package/src/types.ts CHANGED
@@ -116,6 +116,8 @@ export interface MeshNetwork {
116
116
  stop(): Promise<void>;
117
117
  sendToPeer(peerId: string, message: string): Promise<void>;
118
118
  onMessage(handler: (msg: P2PMessage) => void): () => void;
119
+ onPeerConnect(handler: (peerId: string) => void): () => void;
120
+ onPeerDisconnect(handler: (peerId: string) => void): () => void;
119
121
  publishToTopic(topic: string, message: string): Promise<void>;
120
122
  subscribeToTopic(topic: string, handler: (msg: string) => void): Promise<void>;
121
123
  getLocalPeerId(): string;