libp2p-mesh 2026.5.12
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/LICENSE +21 -0
- package/README.md +273 -0
- package/api.ts +2 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +62 -0
- package/dist/src/agent-tools.d.ts +130 -0
- package/dist/src/agent-tools.js +116 -0
- package/dist/src/channel.d.ts +3 -0
- package/dist/src/channel.js +66 -0
- package/dist/src/discovery.d.ts +6 -0
- package/dist/src/discovery.js +7 -0
- package/dist/src/inbound.d.ts +11 -0
- package/dist/src/inbound.js +16 -0
- package/dist/src/mesh.d.ts +10 -0
- package/dist/src/mesh.js +305 -0
- package/dist/src/plugin.d.ts +2 -0
- package/dist/src/plugin.js +84 -0
- package/dist/src/pubsub.d.ts +3 -0
- package/dist/src/pubsub.js +10 -0
- package/dist/src/send.d.ts +2 -0
- package/dist/src/send.js +9 -0
- package/dist/src/types.d.ts +33 -0
- package/dist/src/types.js +1 -0
- package/index.ts +65 -0
- package/openclaw.plugin.json +97 -0
- package/package.json +102 -0
- package/src/agent-tools.ts +116 -0
- package/src/channel.ts +68 -0
- package/src/discovery.ts +13 -0
- package/src/inbound.ts +31 -0
- package/src/mesh.ts +338 -0
- package/src/plugin.ts +96 -0
- package/src/pubsub.ts +17 -0
- package/src/send.ts +11 -0
- package/src/types.ts +36 -0
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { OpenClawPluginApi, PluginCommandContext, PluginCommandResult } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import { createLibp2pMeshChannel } from "./channel.js";
|
|
3
|
+
import { handleP2PInbound, type InboundHandlerDeps } from "./inbound.js";
|
|
4
|
+
import { createMeshNetwork } from "./mesh.js";
|
|
5
|
+
import { buildP2PTools } from "./agent-tools.js";
|
|
6
|
+
import { sendViaMesh } from "./send.js";
|
|
7
|
+
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
|
|
8
|
+
|
|
9
|
+
export function registerLibp2pMesh(api: OpenClawPluginApi) {
|
|
10
|
+
const mesh = createMeshNetwork({
|
|
11
|
+
config: api.pluginConfig as { listenAddrs?: string[]; discovery?: "mdns" | "bootstrap" | "dht"; bootstrapList?: string[]; meshTopic?: string; enableAgentSync?: boolean; enableWebSocket?: boolean } | undefined,
|
|
12
|
+
logger: api.logger,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const channel = createLibp2pMeshChannel(mesh);
|
|
16
|
+
|
|
17
|
+
// 1. Register Service (manages libp2p node lifecycle)
|
|
18
|
+
api.registerService({
|
|
19
|
+
id: "libp2p-mesh",
|
|
20
|
+
start: async () => {
|
|
21
|
+
api.logger.info?.("[libp2p-mesh] >>> Service start called");
|
|
22
|
+
await mesh.start();
|
|
23
|
+
api.logger.info?.(`[libp2p-mesh] >>> mesh.start() done, Peer ID: ${mesh.getLocalPeerId()}`);
|
|
24
|
+
mesh.onMessage((msg) => {
|
|
25
|
+
api.logger.info?.("[libp2p-mesh] >>> onMessage handler registered");
|
|
26
|
+
const sendToChannel: InboundHandlerDeps["sendToChannel"] = async (_channelId, target, text) => {
|
|
27
|
+
try {
|
|
28
|
+
await sendViaMesh(mesh, target, text);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
api.logger.error?.(`[libp2p-mesh] sendToChannel error: ${err}`);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
handleP2PInbound(msg, { logger: api.logger, sendToChannel });
|
|
34
|
+
});
|
|
35
|
+
api.logger.info?.(`[libp2p-mesh] Service started. Peer ID: ${mesh.getLocalPeerId()}`);
|
|
36
|
+
},
|
|
37
|
+
stop: async () => {
|
|
38
|
+
await mesh.stop();
|
|
39
|
+
api.logger.info?.("[libp2p-mesh] Service stopped.");
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 2. Register Channel (lightweight debugging surface)
|
|
44
|
+
api.registerChannel({
|
|
45
|
+
plugin: channel as ChannelPlugin,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 3. Register Agent Tools
|
|
49
|
+
const tools = buildP2PTools(mesh);
|
|
50
|
+
for (const tool of tools) {
|
|
51
|
+
api.registerTool(tool as never);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 4. Register Command (injects agent prompt guidance so the LLM knows to use
|
|
55
|
+
// p2p_list_peers / p2p_send_message / p2p_broadcast for P2P mesh operations
|
|
56
|
+
// instead of the built-in `nodes` tool, which only manages OpenClaw paired nodes.)
|
|
57
|
+
api.registerCommand({
|
|
58
|
+
name: "p2p",
|
|
59
|
+
description: "P2P mesh network operations (list peers, send messages, broadcast).",
|
|
60
|
+
agentPromptGuidance: [
|
|
61
|
+
"When the user asks to list, show, or discover P2P mesh peers or connected agents, use the p2p_list_peers tool. Do NOT use the built-in `nodes` tool for this — `nodes` lists OpenClaw paired nodes, not P2P mesh peers.",
|
|
62
|
+
"When the user asks to send a direct message to another peer/agent, use p2p_send_message.",
|
|
63
|
+
"When the user asks to broadcast a message to all peers on a topic, use p2p_broadcast.",
|
|
64
|
+
],
|
|
65
|
+
handler: (_ctx: PluginCommandContext): PluginCommandResult => {
|
|
66
|
+
return { text: "P2P mesh tools are available. Use p2p_list_peers, p2p_send_message, or p2p_broadcast." };
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 5. Register CLI commands for operator access
|
|
71
|
+
api.registerCli(
|
|
72
|
+
(ctx) => {
|
|
73
|
+
ctx.program
|
|
74
|
+
.command("status")
|
|
75
|
+
.description("Show local peer ID and connected peers")
|
|
76
|
+
.action(async () => {
|
|
77
|
+
const peers = mesh.getConnectedPeers();
|
|
78
|
+
ctx.logger.info?.(`Local Peer ID: ${mesh.getLocalPeerId()}\nConnected peers (${peers.length}): ${peers.join(", ") || "none"}`);
|
|
79
|
+
});
|
|
80
|
+
ctx.program
|
|
81
|
+
.command("peers")
|
|
82
|
+
.description("Alias for mesh status — list connected peers")
|
|
83
|
+
.action(async () => {
|
|
84
|
+
const peers = mesh.getConnectedPeers();
|
|
85
|
+
ctx.logger.info?.(`Connected peers (${peers.length}): ${peers.join(", ") || "none"}`);
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
{ parentPath: ["p2p"] },
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// 6. Register Hook (log received messages for observability)
|
|
92
|
+
api.registerHook("message:received", async (event) => {
|
|
93
|
+
const ctx = event.context as { channelId?: string } | undefined;
|
|
94
|
+
api.logger.debug?.(`[libp2p-mesh] message received on channel ${ctx?.channelId ?? "unknown"}`);
|
|
95
|
+
}, { name: "libp2p-mesh-message-received" });
|
|
96
|
+
}
|
package/src/pubsub.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MeshNetwork } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export async function broadcastToMesh(mesh: MeshNetwork, topic: string, message: string): Promise<void> {
|
|
4
|
+
await mesh.publishToTopic(topic, message);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function subscribeToMeshTopic(
|
|
8
|
+
mesh: MeshNetwork,
|
|
9
|
+
topic: string,
|
|
10
|
+
handler: (msg: string) => void,
|
|
11
|
+
): Promise<() => void> {
|
|
12
|
+
await mesh.subscribeToTopic(topic, handler);
|
|
13
|
+
return () => {
|
|
14
|
+
// Unsubscribe is not directly supported in the current MeshNetwork interface;
|
|
15
|
+
// the handler reference could be stored externally for future cleanup.
|
|
16
|
+
};
|
|
17
|
+
}
|
package/src/send.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MeshNetwork } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export async function sendViaMesh(mesh: MeshNetwork, peerId: string, text: string): Promise<void> {
|
|
4
|
+
if (!peerId || !peerId.trim()) {
|
|
5
|
+
throw new Error("Peer ID is required");
|
|
6
|
+
}
|
|
7
|
+
if (!text || !text.trim()) {
|
|
8
|
+
throw new Error("Message text is required");
|
|
9
|
+
}
|
|
10
|
+
await mesh.sendToPeer(peerId.trim(), text.trim());
|
|
11
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface P2PMessage {
|
|
2
|
+
id: string;
|
|
3
|
+
type: "direct" | "broadcast" | "agent-sync";
|
|
4
|
+
from: string;
|
|
5
|
+
to?: string;
|
|
6
|
+
topic?: string;
|
|
7
|
+
payload: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface MeshConfig {
|
|
12
|
+
listenAddrs?: string[];
|
|
13
|
+
discovery?: "mdns" | "bootstrap" | "dht";
|
|
14
|
+
bootstrapList?: string[];
|
|
15
|
+
meshTopic?: string;
|
|
16
|
+
enableAgentSync?: boolean;
|
|
17
|
+
enableWebSocket?: boolean;
|
|
18
|
+
peerIdPath?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface MeshNetwork {
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
stop(): Promise<void>;
|
|
24
|
+
sendToPeer(peerId: string, message: string): Promise<void>;
|
|
25
|
+
onMessage(handler: (msg: P2PMessage) => void): () => void;
|
|
26
|
+
publishToTopic(topic: string, message: string): Promise<void>;
|
|
27
|
+
subscribeToTopic(topic: string, handler: (msg: string) => void): Promise<void>;
|
|
28
|
+
getLocalPeerId(): string;
|
|
29
|
+
getConnectedPeers(): string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type MeshAccount = {
|
|
33
|
+
accountId: string;
|
|
34
|
+
configured: boolean;
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
};
|