libp2p-mesh 2026.6.9 → 2026.6.11

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/README.md CHANGED
@@ -42,66 +42,78 @@ openclaw plugins registry --refresh
42
42
 
43
43
  The published npm package includes compiled JavaScript under `dist/`, so OpenClaw and acpx can load it directly.
44
44
 
45
- ## Configuration
46
-
47
- ### Quick Setup (Recommended)
48
-
49
- After installation, run the interactive setup wizard:
45
+ Then run the setup wizard:
50
46
 
51
47
  ```bash
52
48
  openclaw libp2p-mesh setup
53
49
  ```
54
50
 
55
- The wizard will guide you through:
56
- - Discovery mode (mDNS / Bootstrap / DHT)
57
- - Bootstrap peer addresses (for cross-network scenarios)
58
- - Inbound channel targets (where to display received P2P messages)
59
- - Optional: NAT traversal, circuit relay, fixed ports, and custom instance name
60
-
61
- The configuration is written to `~/.openclaw/openclaw.json` automatically.
51
+ The wizard creates or edits `plugins.entries["libp2p-mesh"].config` in your OpenClaw config file. You do not need to manually edit `openclaw.json`.
62
52
 
63
- ### Incremental Config Management
53
+ After the wizard writes changes, restart the gateway:
64
54
 
65
55
  ```bash
66
- # View all current non-default settings
67
- openclaw libp2p-mesh config list
56
+ openclaw gateway restart
57
+ ```
68
58
 
69
- # Read a single value
70
- openclaw libp2p-mesh config get discovery
59
+ The generated config shape is:
71
60
 
72
- # Set a value
73
- openclaw libp2p-mesh config set discovery bootstrap
61
+ ```json
62
+ {
63
+ "plugins": {
64
+ "entries": {
65
+ "libp2p-mesh": {
66
+ "enabled": true,
67
+ "config": {
68
+ "discovery": "mdns",
69
+ "deliveryAckTimeoutMs": 15000
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ ```
74
76
 
75
- # Add to an array
76
- openclaw libp2p-mesh config set bootstrapList --add /ip4/10.0.0.5/tcp/4001/p2p/12D3KooW...
77
+ ## Configuration
77
78
 
78
- # Remove from an array
79
- openclaw libp2p-mesh config set bootstrapList --remove /ip4/203.0.113.10/tcp/4001/p2p/12D3KooW...
79
+ Use the interactive setup command for first-time configuration and later edits:
80
80
 
81
- # Reset a key to default
82
- openclaw libp2p-mesh config unset relayList
81
+ ```bash
82
+ openclaw libp2p-mesh setup
83
83
  ```
84
84
 
85
- ### Manual Configuration (Advanced)
85
+ On first run, the wizard enables the plugin and writes `plugins.entries["libp2p-mesh"].config`. On later runs, it edits the existing `libp2p-mesh` entry instead of replacing it blindly. It can update the network mode, add or remove inbound delivery targets, preview the final JSON, and only writes after you confirm.
86
+
87
+ The wizard uses OpenClaw's config writer, so the actual file is your normal OpenClaw config path, usually `~/.openclaw/openclaw.json`. You do not need to manually edit `openclaw.json`, and the wizard does not create `channels["libp2p-mesh"]`.
86
88
 
87
- You can still directly edit `~/.openclaw/openclaw.json`:
89
+ Restart the gateway after applying changes:
90
+
91
+ ```bash
92
+ openclaw gateway restart
93
+ ```
88
94
 
89
95
  ### Minimal LAN Setup (Default)
90
96
 
97
+ Run:
98
+
99
+ ```bash
100
+ openclaw libp2p-mesh setup
101
+ ```
102
+
103
+ Choose LAN mode for two computers on the same WiFi or Ethernet segment. The wizard writes:
104
+
91
105
  ```json
92
106
  {
93
107
  "plugins": {
94
- "libp2p-mesh": {
95
- "enabled": true,
96
- "config": {
97
- "discovery": "mdns"
108
+ "entries": {
109
+ "libp2p-mesh": {
110
+ "enabled": true,
111
+ "config": {
112
+ "discovery": "mdns",
113
+ "deliveryAckTimeoutMs": 15000
114
+ }
98
115
  }
99
116
  }
100
- },
101
- "channels": {
102
- "libp2p-mesh": {
103
- "enabled": true
104
- }
105
117
  }
106
118
  }
107
119
  ```
@@ -115,47 +127,83 @@ By default, the node picks a random TCP port. To use a fixed port:
115
127
  ```json
116
128
  {
117
129
  "plugins": {
118
- "libp2p-mesh": {
119
- "enabled": true,
120
- "config": {
121
- "discovery": "mdns",
122
- "listenAddrs": ["/ip4/0.0.0.0/tcp/4001"]
130
+ "entries": {
131
+ "libp2p-mesh": {
132
+ "enabled": true,
133
+ "config": {
134
+ "discovery": "mdns",
135
+ "listenAddrs": ["/ip4/0.0.0.0/tcp/4001"],
136
+ "deliveryAckTimeoutMs": 15000
137
+ }
123
138
  }
124
139
  }
125
- },
126
- "channels": {
127
- "libp2p-mesh": {
128
- "enabled": true
129
- }
130
140
  }
131
141
  }
132
142
  ```
133
143
 
134
144
  ### With Bootstrap Nodes (Cross-Network)
135
145
 
136
- If peers are on different networks, use a bootstrap node:
146
+ If peers are on different networks, run the setup wizard and choose cross-network mode. It prompts for bootstrap and optional relay multiaddrs, then writes:
137
147
 
138
148
  ```json
139
149
  {
140
150
  "plugins": {
141
- "libp2p-mesh": {
142
- "enabled": true,
143
- "config": {
144
- "discovery": "bootstrap",
145
- "bootstrapList": [
146
- "/ip4/203.0.113.10/tcp/4001/p2p/12D3KooW..."
147
- ]
151
+ "entries": {
152
+ "libp2p-mesh": {
153
+ "enabled": true,
154
+ "config": {
155
+ "discovery": "bootstrap",
156
+ "bootstrapList": [
157
+ "/ip4/203.0.113.10/tcp/4001/p2p/12D3KooW..."
158
+ ],
159
+ "relayList": [
160
+ "/ip4/203.0.113.10/tcp/4001/p2p/12D3KooW..."
161
+ ],
162
+ "enableNATTraversal": true,
163
+ "deliveryAckTimeoutMs": 15000
164
+ }
148
165
  }
149
166
  }
150
- },
151
- "channels": {
152
- "libp2p-mesh": {
153
- "enabled": true
167
+ }
168
+ }
169
+ ```
170
+
171
+ ### Multiple Inbound Targets
172
+
173
+ Inbound delivery is owned by the receiving OpenClaw instance. In the setup wizard, choose to add one or more inbound delivery targets. The sender still sends to the receiver's peer ID or instance ID; the receiver decides which local channels display the incoming message.
174
+
175
+ Example wizard output with two targets:
176
+
177
+ ```json
178
+ {
179
+ "plugins": {
180
+ "entries": {
181
+ "libp2p-mesh": {
182
+ "enabled": true,
183
+ "config": {
184
+ "discovery": "mdns",
185
+ "inboundTargets": [
186
+ {
187
+ "id": "feishu-main",
188
+ "channel": "feishu",
189
+ "target": "user:ou_xxx"
190
+ },
191
+ {
192
+ "id": "telegram-main",
193
+ "channel": "telegram",
194
+ "target": "chat:123456"
195
+ }
196
+ ],
197
+ "deliveryAckTimeoutMs": 15000
198
+ }
199
+ }
154
200
  }
155
201
  }
156
202
  }
157
203
  ```
158
204
 
205
+ If `inboundTargets` is an empty array, inbound delivery is disabled. If `inboundTargets` is omitted, the plugin keeps any existing inbound behavior unchanged. When `inboundTargets` is present, it overrides legacy `inboundChannel`/`inboundTarget`.
206
+
159
207
  ### Full Configuration Reference
160
208
 
161
209
  | Key | Type | Default | Description |
@@ -178,7 +226,7 @@ If peers are on different networks, use a bootstrap node:
178
226
  | `announceAddrs` | `string[]` | `[]` | Extra multiaddrs to announce on top of auto-detected ones |
179
227
  | `inboundChannel` | `string` | `undefined` | OpenClaw channel used to display inbound P2P user messages, for example `"feishu"` |
180
228
  | `inboundTarget` | `string` | `undefined` | OpenClaw channel target for inbound P2P messages, for example `user:ou_xxx` or `chat:oc_xxx` |
181
- | `inboundTargets` | `{ id?: string, channel: string, target: string }[]` | `undefined` | Receiver-owned list of channel targets for inbound P2P user messages. When present, overrides `inboundChannel`/`inboundTarget`; an empty array disables inbound delivery |
229
+ | `inboundTargets` | `array` | `undefined` | Optional list of receiver-owned channel targets for inbound P2P user messages. When present, it overrides `inboundChannel`/`inboundTarget`; an empty array disables inbound delivery. |
182
230
  | `deliveryAckTimeoutMs` | `number` | `15000` | Timeout for waiting on remote channel delivery ACKs |
183
231
 
184
232
  ## NAT Traversal
@@ -197,16 +245,20 @@ You need at least one relay node with a public IP. Set it in `relayList`:
197
245
  ```json
198
246
  {
199
247
  "plugins": {
200
- "libp2p-mesh": {
201
- "enabled": true,
202
- "config": {
203
- "discovery": "bootstrap",
204
- "bootstrapList": [
205
- "/ip4/<RELAY-IP>/tcp/4001/p2p/<RELAY-PEER-ID>"
206
- ],
207
- "relayList": [
208
- "/ip4/<RELAY-IP>/tcp/4001/p2p/<RELAY-PEER-ID>"
209
- ]
248
+ "entries": {
249
+ "libp2p-mesh": {
250
+ "enabled": true,
251
+ "config": {
252
+ "discovery": "bootstrap",
253
+ "bootstrapList": [
254
+ "/ip4/<RELAY-IP>/tcp/4001/p2p/<RELAY-PEER-ID>"
255
+ ],
256
+ "relayList": [
257
+ "/ip4/<RELAY-IP>/tcp/4001/p2p/<RELAY-PEER-ID>"
258
+ ],
259
+ "enableNATTraversal": true,
260
+ "deliveryAckTimeoutMs": 15000
261
+ }
210
262
  }
211
263
  }
212
264
  }
@@ -222,13 +274,17 @@ Add `enableCircuitRelayServer: true` to your config and announce the public addr
222
274
  ```json
223
275
  {
224
276
  "plugins": {
225
- "libp2p-mesh": {
226
- "enabled": true,
227
- "config": {
228
- "discovery": "bootstrap",
229
- "listenAddrs": ["/ip4/0.0.0.0/tcp/4001"],
230
- "announceAddrs": ["/ip4/<PUBLIC-IP>/tcp/4001"],
231
- "enableCircuitRelayServer": true
277
+ "entries": {
278
+ "libp2p-mesh": {
279
+ "enabled": true,
280
+ "config": {
281
+ "discovery": "bootstrap",
282
+ "listenAddrs": ["/ip4/0.0.0.0/tcp/4001"],
283
+ "announceAddrs": ["/ip4/<PUBLIC-IP>/tcp/4001"],
284
+ "enableNATTraversal": true,
285
+ "enableCircuitRelayServer": true,
286
+ "deliveryAckTimeoutMs": 15000
287
+ }
232
288
  }
233
289
  }
234
290
  }
@@ -308,75 +364,82 @@ $OPENCLAW_STATE_DIR/libp2p/instance-peer.json
308
364
 
309
365
  Users do not configure this file path. It is plugin-managed state.
310
366
 
311
- For Feishu inbound display, configure the receiving instance:
367
+ For inbound display, run the setup wizard on the receiving instance and add a target:
368
+
369
+ ```bash
370
+ openclaw libp2p-mesh setup
371
+ ```
372
+
373
+ The wizard edits `plugins.entries["libp2p-mesh"].config` and can add, edit, remove, or disable inbound delivery targets. You do not need to manually edit `openclaw.json`.
374
+
375
+ Example result for a single Feishu target:
312
376
 
313
377
  ```json
314
378
  {
315
379
  "plugins": {
316
- "libp2p-mesh": {
317
- "enabled": true,
318
- "config": {
319
- "discovery": "mdns",
320
- "inboundChannel": "feishu",
321
- "inboundTarget": "user:ou_xxx",
322
- "deliveryAckTimeoutMs": 15000
380
+ "entries": {
381
+ "libp2p-mesh": {
382
+ "enabled": true,
383
+ "config": {
384
+ "discovery": "mdns",
385
+ "inboundTargets": [
386
+ {
387
+ "id": "feishu-main",
388
+ "channel": "feishu",
389
+ "target": "user:ou_xxx"
390
+ }
391
+ ],
392
+ "deliveryAckTimeoutMs": 15000
393
+ }
323
394
  }
324
395
  }
325
- },
326
- "channels": {
327
- "libp2p-mesh": { "enabled": true }
328
396
  }
329
397
  }
330
398
  ```
331
399
 
332
- The OpenClaw agent should prefer:
333
-
334
- ```text
335
- p2p_send_instance_message({ "instanceId": "<target-instance-id>", "message": "今晚出来吃饭" })
336
- ```
337
-
338
- The sender reports success only after the remote OpenClaw instance forwards the message to its configured inbound channel and returns a delivery ACK.
339
-
340
400
  ### Multi-channel inbound delivery
341
401
 
342
- To display the same inbound P2P message in multiple local OpenClaw channel targets, configure `inboundTargets` on the receiving instance:
402
+ The sender still calls `p2p_send_instance_message({ "instanceId": "...", "message": "..." })`.
403
+ The receiver chooses where inbound P2P messages appear:
343
404
 
344
405
  ```json
345
406
  {
346
407
  "plugins": {
347
- "libp2p-mesh": {
348
- "enabled": true,
349
- "config": {
350
- "discovery": "mdns",
351
- "inboundTargets": [
352
- {
353
- "id": "feishu-user",
354
- "channel": "feishu",
355
- "target": "user:ou_xxx"
356
- },
357
- {
358
- "id": "telegram-chat",
359
- "channel": "telegram",
360
- "target": "chat:123456"
361
- }
362
- ],
363
- "deliveryAckTimeoutMs": 15000
408
+ "entries": {
409
+ "libp2p-mesh": {
410
+ "enabled": true,
411
+ "config": {
412
+ "discovery": "mdns",
413
+ "inboundTargets": [
414
+ {
415
+ "id": "feishu-main",
416
+ "channel": "feishu",
417
+ "target": "user:ou_xxx"
418
+ },
419
+ {
420
+ "id": "telegram-main",
421
+ "channel": "telegram",
422
+ "target": "chat:123456"
423
+ }
424
+ ],
425
+ "deliveryAckTimeoutMs": 15000
426
+ }
364
427
  }
365
428
  }
366
- },
367
- "channels": {
368
- "libp2p-mesh": { "enabled": true }
369
429
  }
370
430
  }
371
431
  ```
372
432
 
373
- The sender still calls only:
433
+ If `inboundTargets` is present, it is used instead of `inboundChannel`/`inboundTarget`.
434
+ The sender receives per-target delivery status in the tool result.
435
+
436
+ The OpenClaw agent should prefer:
374
437
 
375
438
  ```text
376
439
  p2p_send_instance_message({ "instanceId": "<target-instance-id>", "message": "今晚出来吃饭" })
377
440
  ```
378
441
 
379
- The sender does not choose the receiver channel. The receiving instance owns that routing decision through `inboundTargets`, and the message appears in each configured `channel`/`target`. Each target can include an `id`, which is shown in the sender-side tool result alongside each target's success or failure status.
442
+ The sender reports success only after the remote OpenClaw instance forwards the message to its configured inbound channel and returns a delivery ACK.
380
443
 
381
444
  Tools are not configured in `openclaw.json`; they are registered automatically by the plugin through `api.registerTool()`.
382
445
 
package/api.ts CHANGED
@@ -3,11 +3,9 @@ export { createInstancePeerStore } from "./src/instance-peer-store.js";
3
3
  export { createInstanceRouter } from "./src/instance-router.js";
4
4
  export type {
5
5
  DeliveryAckPayload,
6
- DeliveryTargetResult,
7
6
  InboundDeliveryAdapter,
8
7
  InboundDeliveryRequest,
9
8
  InboundDeliveryResult,
10
- InboundTargetConfig,
11
9
  InstanceAnnouncePayload,
12
10
  InstanceIdentity,
13
11
  InstancePeerRecord,
package/dist/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { createMeshNetwork } from "./src/mesh.js";
2
2
  export { createInstancePeerStore } from "./src/instance-peer-store.js";
3
3
  export { createInstanceRouter } from "./src/instance-router.js";
4
- export type { DeliveryAckPayload, DeliveryTargetResult, InboundDeliveryAdapter, InboundDeliveryRequest, InboundDeliveryResult, InboundTargetConfig, InstanceAnnouncePayload, InstanceIdentity, InstancePeerRecord, InstancePeerStore, InstancePeerTable, InstanceRouter, MeshConfig, MeshNetwork, P2PMessage, P2PMessageType, UserMessagePayload, } from "./src/types.js";
4
+ export type { DeliveryAckPayload, InboundDeliveryAdapter, InboundDeliveryRequest, InboundDeliveryResult, InstanceAnnouncePayload, InstanceIdentity, InstancePeerRecord, InstancePeerStore, InstancePeerTable, InstanceRouter, MeshConfig, MeshNetwork, P2PMessage, P2PMessageType, UserMessagePayload, } from "./src/types.js";
package/dist/index.d.ts CHANGED
@@ -1,9 +1,3 @@
1
- import type { OpenClawPluginConfigSchema } from "openclaw/plugin-sdk/core";
2
- declare const _default: {
3
- id: string;
4
- name: string;
5
- description: string;
6
- configSchema: OpenClawPluginConfigSchema;
7
- register: NonNullable<import("openclaw/plugin-sdk/core").OpenClawPluginDefinition["register"]>;
8
- } & Pick<import("openclaw/plugin-sdk/core").OpenClawPluginDefinition, "reload" | "kind" | "nodeHostCommands" | "securityAuditCollectors">;
9
- export default _default;
1
+ import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/core";
2
+ declare const plugin: OpenClawPluginDefinition;
3
+ export default plugin;
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { definePluginEntry } from "openclaw/plugin-sdk/core";
2
2
  import { registerLibp2pMesh } from "./src/plugin.js";
3
- import { registerLibp2pMeshCli } from "./src/cli.js";
4
3
  function createLibp2pMeshConfigSchema() {
5
4
  return {
6
5
  safeParse(value) {
@@ -160,25 +159,11 @@ function createLibp2pMeshConfigSchema() {
160
159
  },
161
160
  };
162
161
  }
163
- export default definePluginEntry({
162
+ const plugin = definePluginEntry({
164
163
  id: "libp2p-mesh",
165
164
  name: "libp2p Mesh Network",
166
165
  description: "P2P network for cross-instance agent communication via libp2p.",
167
166
  configSchema: createLibp2pMeshConfigSchema(),
168
- register: (api) => {
169
- if (api.registrationMode === "cli-metadata" ||
170
- api.registrationMode === "discovery" ||
171
- api.registrationMode === "full") {
172
- api.registerCli(registerLibp2pMeshCli, {
173
- commands: ["libp2p-mesh"],
174
- });
175
- if (api.registrationMode === "cli-metadata") {
176
- return;
177
- }
178
- }
179
- if (api.registrationMode !== "full") {
180
- return;
181
- }
182
- registerLibp2pMesh(api);
183
- },
167
+ register: registerLibp2pMesh,
184
168
  });
169
+ export default plugin;
@@ -1,19 +1,16 @@
1
1
  function targetLabel(result) {
2
- const id = result.id?.trim();
3
- const location = `${result.channel} / ${result.target}`;
4
- return id ? `${id} (${location})` : location;
2
+ const name = result.id ?? `${result.channel}:${result.target}`;
3
+ return `${name} (${result.channel} / ${result.target})`;
5
4
  }
6
5
  function formatDeliveryResults(instanceId, delivered, results) {
7
- const header = delivered
8
- ? `发往 ${instanceId} 的消息投递结果:`
9
- : `发往 ${instanceId} 的消息投递失败:`;
6
+ const heading = delivered
7
+ ? `发往 ${instanceId} 的消息投递结果`
8
+ : `发往 ${instanceId} 的消息投递失败`;
10
9
  const lines = results.map((result) => {
11
- if (result.ok) {
12
- return `- ${targetLabel(result)}:已送达`;
13
- }
14
- return `- ${targetLabel(result)}:失败:${result.error ?? "unknown error"}`;
10
+ const status = result.ok ? "已送达" : `失败:${result.error ?? "unknown error"}`;
11
+ return `${targetLabel(result)}:${status}`;
15
12
  });
16
- return [header, ...lines].join("\n");
13
+ return [heading, ...lines].join("\n");
17
14
  }
18
15
  export function buildP2PTools(mesh, router) {
19
16
  return [