libp2p-mesh 2026.5.13 → 2026.5.15
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 +61 -23
- package/api.ts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +60 -24
- package/dist/runtime-setter-api.d.ts +4 -0
- package/dist/runtime-setter-api.js +19 -0
- package/dist/src/agent-tools.d.ts +63 -24
- package/dist/src/agent-tools.js +69 -34
- package/dist/src/channel.d.ts +1 -0
- package/dist/src/channel.js +20 -4
- package/dist/src/dht-registry.d.ts +38 -0
- package/dist/src/dht-registry.js +80 -0
- package/dist/src/inbound.d.ts +0 -3
- package/dist/src/inbound.js +29 -14
- package/dist/src/instance-id.d.ts +53 -0
- package/dist/src/instance-id.js +156 -0
- package/dist/src/mesh.js +310 -23
- package/dist/src/plugin.d.ts +1 -2
- package/dist/src/plugin.js +18 -30
- package/dist/src/types.d.ts +87 -0
- package/index.ts +60 -24
- package/openclaw.plugin.json +72 -33
- package/package.json +20 -8
- package/src/agent-tools.ts +69 -35
- package/src/channel.ts +25 -4
- package/src/dht-registry.ts +105 -0
- package/src/inbound.ts +35 -18
- package/src/instance-id.ts +221 -0
- package/src/mesh.ts +368 -27
- package/src/plugin.ts +25 -36
- package/src/types.ts +95 -0
- package/dist/src/agent-tools-feishu.test.d.ts +0 -1
- package/dist/src/agent-tools-feishu.test.js +0 -57
- package/dist/src/config-schema.test.d.ts +0 -1
- package/dist/src/config-schema.test.js +0 -55
- package/dist/src/feishu-channel.d.ts +0 -19
- package/dist/src/feishu-channel.js +0 -202
- package/dist/src/feishu-channel.test.d.ts +0 -1
- package/dist/src/feishu-channel.test.js +0 -166
- package/dist/src/feishu-client.d.ts +0 -27
- package/dist/src/feishu-client.js +0 -141
- package/dist/src/feishu-client.test.d.ts +0 -1
- package/dist/src/feishu-client.test.js +0 -271
- package/dist/src/feishu-e2e.test.d.ts +0 -1
- package/dist/src/feishu-e2e.test.js +0 -69
- package/dist/src/feishu-types.d.ts +0 -53
- package/dist/src/feishu-types.js +0 -1
- package/dist/src/feishu-types.test.d.ts +0 -1
- package/dist/src/feishu-types.test.js +0 -108
- package/dist/src/inbound-feishu.test.d.ts +0 -1
- package/dist/src/inbound-feishu.test.js +0 -70
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/plugin-registration.test.d.ts +0 -1
- package/dist/src/plugin-registration.test.js +0 -42
- package/src/agent-tools-feishu.test.ts +0 -68
- package/src/config-schema.test.ts +0 -63
- package/src/feishu-channel.test.ts +0 -191
- package/src/feishu-channel.ts +0 -253
- package/src/feishu-client.test.ts +0 -303
- package/src/feishu-client.ts +0 -178
- package/src/feishu-e2e.test.ts +0 -90
- package/src/feishu-types.test.ts +0 -125
- package/src/feishu-types.ts +0 -51
- package/src/inbound-feishu.test.ts +0 -91
- package/src/index.ts +0 -1
- package/src/plugin-registration.test.ts +0 -60
package/dist/src/types.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
export interface InstanceIdentity {
|
|
2
|
+
/** Full InstanceID string, e.g. "alice-mac@AQIDBAUGBweI.7a3f9e2b" */
|
|
3
|
+
id: string;
|
|
4
|
+
/** Human-readable instance name */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Base64url-encoded Ed25519 public key */
|
|
7
|
+
pubkey: string;
|
|
8
|
+
/** Hex SHA-256 binding hash of environment dimensions */
|
|
9
|
+
binding: string;
|
|
10
|
+
/** Components that contributed to the binding hash */
|
|
11
|
+
bindingComponents: {
|
|
12
|
+
username: string;
|
|
13
|
+
hostname: string;
|
|
14
|
+
platform: string;
|
|
15
|
+
};
|
|
16
|
+
/** Timestamp when the identity was created */
|
|
17
|
+
createdAt: number;
|
|
18
|
+
}
|
|
1
19
|
export interface P2PMessage {
|
|
2
20
|
id: string;
|
|
3
21
|
type: "direct" | "broadcast" | "agent-sync";
|
|
@@ -6,6 +24,12 @@ export interface P2PMessage {
|
|
|
6
24
|
topic?: string;
|
|
7
25
|
payload: string;
|
|
8
26
|
timestamp: number;
|
|
27
|
+
/** Instance identity of the sender (for cross-instance authentication) */
|
|
28
|
+
instanceId?: string;
|
|
29
|
+
/** Base64url-encoded Ed25519 public key of the sender (allows direct verification without DHT lookup) */
|
|
30
|
+
pubkey?: string;
|
|
31
|
+
/** Ed25519 signature of the message payload, verifiable with instance pubkey */
|
|
32
|
+
signature?: string;
|
|
9
33
|
}
|
|
10
34
|
export interface MeshConfig {
|
|
11
35
|
listenAddrs?: string[];
|
|
@@ -15,6 +39,63 @@ export interface MeshConfig {
|
|
|
15
39
|
enableAgentSync?: boolean;
|
|
16
40
|
enableWebSocket?: boolean;
|
|
17
41
|
peerIdPath?: string;
|
|
42
|
+
instanceName?: string;
|
|
43
|
+
/** Enable DHT for WAN peer discovery and pubkey registry (default: true when discovery=dht, false otherwise) */
|
|
44
|
+
enableDHT?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Master switch for the NAT traversal stack. When `false` none of the
|
|
47
|
+
* NAT-related services are wired in, restoring the pre-2026.5.16
|
|
48
|
+
* behaviour. Default `true`.
|
|
49
|
+
*/
|
|
50
|
+
enableNATTraversal?: boolean;
|
|
51
|
+
/** Run the libp2p identify protocol (required by AutoNAT/DCUtR). Default `true` when NAT traversal is on. */
|
|
52
|
+
enableIdentify?: boolean;
|
|
53
|
+
/** AutoNAT detects whether we are reachable from the public internet. Default `true` when NAT traversal is on. */
|
|
54
|
+
enableAutoNAT?: boolean;
|
|
55
|
+
/** Attempt UPnP/PMP port mapping on the local gateway. Default `true` when NAT traversal is on. */
|
|
56
|
+
enableUPnP?: boolean;
|
|
57
|
+
/** Circuit Relay v2 transport — required to dial peers via a relay. Default `true` when NAT traversal is on. */
|
|
58
|
+
enableCircuitRelay?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Act as a Circuit Relay v2 server for other peers. Default `false` —
|
|
61
|
+
* only enable on a node with a public, routable address (e.g. a cloud VM)
|
|
62
|
+
* because relayed traffic is forwarded through this process.
|
|
63
|
+
*/
|
|
64
|
+
enableCircuitRelayServer?: boolean;
|
|
65
|
+
/** Direct Connection Upgrade through Relay (hole punching). Default `true` when NAT traversal is on. */
|
|
66
|
+
enableDCUtR?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Explicit relay multiaddrs to reserve a slot on. Each entry should be a
|
|
69
|
+
* full multiaddr ending in `/p2p/<peer-id>`. The node will keep a
|
|
70
|
+
* reservation open with each one so other peers can dial us through them.
|
|
71
|
+
*/
|
|
72
|
+
relayList?: string[];
|
|
73
|
+
/**
|
|
74
|
+
* Number of relays to auto-discover via content routing. Requires DHT.
|
|
75
|
+
* Default `0` (disabled). Set to e.g. `2` to look up public relays.
|
|
76
|
+
*/
|
|
77
|
+
discoverRelays?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Multiaddrs to announce to the network on top of the auto-detected
|
|
80
|
+
* listen/observed addresses. Useful when running behind a known port
|
|
81
|
+
* forward where AutoNAT cannot probe (e.g. behind a cloud LB).
|
|
82
|
+
*/
|
|
83
|
+
announceAddrs?: string[];
|
|
84
|
+
}
|
|
85
|
+
export interface NATTraversalStatus {
|
|
86
|
+
/** Which NAT-traversal services were wired in at start() */
|
|
87
|
+
enabled: {
|
|
88
|
+
identify: boolean;
|
|
89
|
+
autoNAT: boolean;
|
|
90
|
+
upnp: boolean;
|
|
91
|
+
circuitRelay: boolean;
|
|
92
|
+
circuitRelayServer: boolean;
|
|
93
|
+
dcutr: boolean;
|
|
94
|
+
};
|
|
95
|
+
/** Relay multiaddrs we have an active reservation on */
|
|
96
|
+
reservedRelays: string[];
|
|
97
|
+
/** Whether at least one circuit-relay address has been advertised as a listen address */
|
|
98
|
+
hasRelayedListenAddr: boolean;
|
|
18
99
|
}
|
|
19
100
|
export interface MeshNetwork {
|
|
20
101
|
start(): Promise<void>;
|
|
@@ -25,6 +106,12 @@ export interface MeshNetwork {
|
|
|
25
106
|
subscribeToTopic(topic: string, handler: (msg: string) => void): Promise<void>;
|
|
26
107
|
getLocalPeerId(): string;
|
|
27
108
|
getConnectedPeers(): string[];
|
|
109
|
+
getMultiaddrs(): string[];
|
|
110
|
+
dial(multiaddr: string): Promise<void>;
|
|
111
|
+
/** Get the OpenClaw instance identity (lightweight BAID-inspired ID) */
|
|
112
|
+
getInstanceIdentity(): InstanceIdentity | undefined;
|
|
113
|
+
/** Inspect which NAT-traversal services are running and whether any relay reservations are active */
|
|
114
|
+
getNATStatus(): NATTraversalStatus;
|
|
28
115
|
}
|
|
29
116
|
export type MeshAccount = {
|
|
30
117
|
accountId: string;
|
package/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { definePluginEntry } from "openclaw/plugin-sdk/core";
|
|
|
2
2
|
import type { OpenClawPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
|
3
3
|
import { registerLibp2pMesh } from "./src/plugin.js";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function createLibp2pMeshConfigSchema(): OpenClawPluginConfigSchema {
|
|
6
6
|
return {
|
|
7
7
|
safeParse(value: unknown) {
|
|
8
8
|
if (value === undefined) {
|
|
@@ -38,6 +38,7 @@ export function createLibp2pMeshConfigSchema(): OpenClawPluginConfigSchema {
|
|
|
38
38
|
bootstrapList: {
|
|
39
39
|
type: "array",
|
|
40
40
|
items: { type: "string" },
|
|
41
|
+
description: "List of bootstrap multiaddrs for WAN discovery (required when discovery=dht or bootstrap)",
|
|
41
42
|
},
|
|
42
43
|
meshTopic: {
|
|
43
44
|
type: "string",
|
|
@@ -51,29 +52,64 @@ export function createLibp2pMeshConfigSchema(): OpenClawPluginConfigSchema {
|
|
|
51
52
|
type: "boolean",
|
|
52
53
|
default: true,
|
|
53
54
|
},
|
|
54
|
-
|
|
55
|
-
type: "
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
55
|
+
enableDHT: {
|
|
56
|
+
type: "boolean",
|
|
57
|
+
default: true,
|
|
58
|
+
description: "Enable DHT for WAN peer discovery and pubkey registry. Default true when discovery=dht, can be explicitly disabled.",
|
|
59
|
+
},
|
|
60
|
+
instanceName: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "Custom name for this OpenClaw instance (used in InstanceID). Defaults to \"<username>-<hostname>\".",
|
|
63
|
+
},
|
|
64
|
+
enableNATTraversal: {
|
|
65
|
+
type: "boolean",
|
|
66
|
+
default: true,
|
|
67
|
+
description: "Master switch for the NAT traversal stack (identify + AutoNAT + UPnP + Circuit Relay v2 + DCUtR). Set to false to restore pre-NAT behaviour.",
|
|
68
|
+
},
|
|
69
|
+
enableIdentify: {
|
|
70
|
+
type: "boolean",
|
|
71
|
+
default: true,
|
|
72
|
+
description: "Run the libp2p identify protocol; required by AutoNAT and DCUtR.",
|
|
73
|
+
},
|
|
74
|
+
enableAutoNAT: {
|
|
75
|
+
type: "boolean",
|
|
76
|
+
default: true,
|
|
77
|
+
description: "Use AutoNAT to learn whether this node is publicly reachable.",
|
|
78
|
+
},
|
|
79
|
+
enableUPnP: {
|
|
80
|
+
type: "boolean",
|
|
81
|
+
default: true,
|
|
82
|
+
description: "Attempt UPnP/PMP port mapping against the local gateway so other peers can dial us directly when behind a home router.",
|
|
83
|
+
},
|
|
84
|
+
enableCircuitRelay: {
|
|
85
|
+
type: "boolean",
|
|
86
|
+
default: true,
|
|
87
|
+
description: "Allow this node to dial peers via /p2p-circuit relay addresses and to reserve a slot on the relays in relayList.",
|
|
88
|
+
},
|
|
89
|
+
enableCircuitRelayServer: {
|
|
90
|
+
type: "boolean",
|
|
91
|
+
default: false,
|
|
92
|
+
description: "Act as a Circuit Relay v2 SERVER for other peers. Only enable on a publicly reachable node (e.g. a cloud VM).",
|
|
93
|
+
},
|
|
94
|
+
enableDCUtR: {
|
|
95
|
+
type: "boolean",
|
|
96
|
+
default: true,
|
|
97
|
+
description: "Direct Connection Upgrade through Relay (hole punching). Upgrades a relayed connection to a direct one when possible.",
|
|
98
|
+
},
|
|
99
|
+
relayList: {
|
|
100
|
+
type: "array",
|
|
101
|
+
items: { type: "string" },
|
|
102
|
+
description: "Multiaddrs of relay nodes to reserve a slot on (each entry must end in /p2p/<peer-id>).",
|
|
103
|
+
},
|
|
104
|
+
discoverRelays: {
|
|
105
|
+
type: "number",
|
|
106
|
+
default: 0,
|
|
107
|
+
description: "How many relays to auto-discover via content routing. Requires DHT. 0 disables discovery.",
|
|
108
|
+
},
|
|
109
|
+
announceAddrs: {
|
|
110
|
+
type: "array",
|
|
111
|
+
items: { type: "string" },
|
|
112
|
+
description: "Extra multiaddrs to announce to the network (useful when running behind a known port forward where AutoNAT cannot probe).",
|
|
77
113
|
},
|
|
78
114
|
},
|
|
79
115
|
},
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "libp2p-mesh",
|
|
3
|
+
"activation": {
|
|
4
|
+
"onStartup": true
|
|
5
|
+
},
|
|
6
|
+
"channels": ["libp2p-mesh"],
|
|
7
|
+
"enabledByDefault": true,
|
|
3
8
|
"name": "libp2p Mesh Network",
|
|
4
9
|
"description": "P2P network for cross-instance agent communication via libp2p.",
|
|
5
10
|
"configSchema": {
|
|
@@ -36,42 +41,76 @@
|
|
|
36
41
|
"enableAgentSync": {
|
|
37
42
|
"type": "boolean",
|
|
38
43
|
"default": true
|
|
44
|
+
},
|
|
45
|
+
"enableDHT": {
|
|
46
|
+
"type": "boolean",
|
|
47
|
+
"default": true,
|
|
48
|
+
"description": "Enable DHT for WAN peer discovery and pubkey registry."
|
|
49
|
+
},
|
|
50
|
+
"instanceName": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Custom name for this OpenClaw instance (used in InstanceID). Defaults to \"<username>-<hostname>\"."
|
|
53
|
+
},
|
|
54
|
+
"enableNATTraversal": {
|
|
55
|
+
"type": "boolean",
|
|
56
|
+
"default": true,
|
|
57
|
+
"description": "Master switch for the NAT traversal stack (identify + AutoNAT + UPnP + Circuit Relay v2 + DCUtR)."
|
|
58
|
+
},
|
|
59
|
+
"enableIdentify": {
|
|
60
|
+
"type": "boolean",
|
|
61
|
+
"default": true,
|
|
62
|
+
"description": "Run the libp2p identify protocol; required by AutoNAT and DCUtR."
|
|
63
|
+
},
|
|
64
|
+
"enableAutoNAT": {
|
|
65
|
+
"type": "boolean",
|
|
66
|
+
"default": true,
|
|
67
|
+
"description": "Use AutoNAT to learn whether this node is publicly reachable."
|
|
68
|
+
},
|
|
69
|
+
"enableUPnP": {
|
|
70
|
+
"type": "boolean",
|
|
71
|
+
"default": true,
|
|
72
|
+
"description": "Attempt UPnP/PMP port mapping on the local gateway."
|
|
73
|
+
},
|
|
74
|
+
"enableCircuitRelay": {
|
|
75
|
+
"type": "boolean",
|
|
76
|
+
"default": true,
|
|
77
|
+
"description": "Dial peers via /p2p-circuit relay addresses; reserve a slot on each entry in relayList."
|
|
78
|
+
},
|
|
79
|
+
"enableCircuitRelayServer": {
|
|
80
|
+
"type": "boolean",
|
|
81
|
+
"default": false,
|
|
82
|
+
"description": "Act as a Circuit Relay v2 server. Only enable on a publicly reachable node."
|
|
83
|
+
},
|
|
84
|
+
"enableDCUtR": {
|
|
85
|
+
"type": "boolean",
|
|
86
|
+
"default": true,
|
|
87
|
+
"description": "Direct Connection Upgrade through Relay (hole punching)."
|
|
88
|
+
},
|
|
89
|
+
"relayList": {
|
|
90
|
+
"type": "array",
|
|
91
|
+
"items": { "type": "string" },
|
|
92
|
+
"description": "Multiaddrs of relays to reserve a slot on (each entry must end in /p2p/<peer-id>)."
|
|
93
|
+
},
|
|
94
|
+
"discoverRelays": {
|
|
95
|
+
"type": "number",
|
|
96
|
+
"default": 0,
|
|
97
|
+
"description": "Auto-discover this many relays via content routing. Requires DHT."
|
|
98
|
+
},
|
|
99
|
+
"announceAddrs": {
|
|
100
|
+
"type": "array",
|
|
101
|
+
"items": { "type": "string" },
|
|
102
|
+
"description": "Extra multiaddrs to announce to the network on top of auto-detected ones."
|
|
39
103
|
}
|
|
40
104
|
}
|
|
41
105
|
},
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"type": "array",
|
|
51
|
-
"items": { "type": "string" },
|
|
52
|
-
"default": ["/ip4/0.0.0.0/tcp/0"]
|
|
53
|
-
},
|
|
54
|
-
"enableWebSocket": {
|
|
55
|
-
"type": "boolean",
|
|
56
|
-
"default": false,
|
|
57
|
-
"description": "Enable WebSocket transport"
|
|
58
|
-
},
|
|
59
|
-
"discovery": {
|
|
60
|
-
"type": "string",
|
|
61
|
-
"enum": ["mdns", "bootstrap", "dht"],
|
|
62
|
-
"default": "mdns"
|
|
63
|
-
},
|
|
64
|
-
"bootstrapList": {
|
|
65
|
-
"type": "array",
|
|
66
|
-
"items": { "type": "string" }
|
|
67
|
-
},
|
|
68
|
-
"meshTopic": {
|
|
69
|
-
"type": "string",
|
|
70
|
-
"default": "openclaw-mesh"
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
106
|
+
"contracts": {
|
|
107
|
+
"tools": [
|
|
108
|
+
"p2p_send_message",
|
|
109
|
+
"p2p_broadcast",
|
|
110
|
+
"p2p_list_peers",
|
|
111
|
+
"p2p_get_instance_identity",
|
|
112
|
+
"p2p_get_network_info"
|
|
113
|
+
]
|
|
75
114
|
},
|
|
76
115
|
"uiHints": {
|
|
77
116
|
"listenAddrs": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libp2p-mesh",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.15",
|
|
4
4
|
"description": "OpenClaw libp2p P2P mesh network plugin for cross-instance agent communication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,24 +19,26 @@
|
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@libp2p/bootstrap": "^
|
|
22
|
+
"@libp2p/bootstrap": "^9.0.12",
|
|
23
|
+
"@libp2p/kad-dht": "^10.0.0",
|
|
23
24
|
"@libp2p/mdns": "^9.0.14",
|
|
24
25
|
"@libp2p/mplex": "^9.0.12",
|
|
25
26
|
"@libp2p/noise": "^12.0.1",
|
|
27
|
+
"@libp2p/peer-id": "^3.0.6",
|
|
26
28
|
"@libp2p/peer-id-factory": "^3.0.11",
|
|
27
29
|
"@libp2p/tcp": "^8.0.13",
|
|
28
|
-
"@libp2p/websockets": "^
|
|
30
|
+
"@libp2p/websockets": "^7.0.13",
|
|
29
31
|
"@multiformats/multiaddr": "^12.5.1",
|
|
30
|
-
"it-length-prefixed": "^
|
|
32
|
+
"it-length-prefixed": "^9.0.1",
|
|
31
33
|
"it-pipe": "^3.0.1",
|
|
32
34
|
"libp2p": "^0.46.16",
|
|
33
35
|
"uint8arraylist": "^2.4.8"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
38
|
+
"@types/node": "^25.7.0",
|
|
36
39
|
"openclaw": "^2026.5.7",
|
|
37
40
|
"tsx": "^4.0.0",
|
|
38
|
-
"typescript": "^5.9.3"
|
|
39
|
-
"vitest": "^4.1.8"
|
|
41
|
+
"typescript": "^5.9.3"
|
|
40
42
|
},
|
|
41
43
|
"peerDependencies": {
|
|
42
44
|
"openclaw": ">=2026.3.24"
|
|
@@ -61,7 +63,7 @@
|
|
|
61
63
|
"systemImage": "network"
|
|
62
64
|
},
|
|
63
65
|
"install": {
|
|
64
|
-
"npmSpec": "
|
|
66
|
+
"npmSpec": "libp2p-mesh",
|
|
65
67
|
"defaultChoice": "npm",
|
|
66
68
|
"minHostVersion": ">=2026.3.24"
|
|
67
69
|
},
|
|
@@ -99,5 +101,15 @@
|
|
|
99
101
|
"bugs": {
|
|
100
102
|
"url": "https://github.com/lilingfy/openclaw-libp2p-mesh/issues"
|
|
101
103
|
},
|
|
102
|
-
"homepage": "https://github.com/lilingfy/openclaw-libp2p-mesh#readme"
|
|
104
|
+
"homepage": "https://github.com/lilingfy/openclaw-libp2p-mesh#readme",
|
|
105
|
+
"pnpm": {
|
|
106
|
+
"onlyBuiltDependencies": [
|
|
107
|
+
"@google/genai",
|
|
108
|
+
"esbuild",
|
|
109
|
+
"koffi",
|
|
110
|
+
"openclaw",
|
|
111
|
+
"protobufjs",
|
|
112
|
+
"tree-sitter-bash"
|
|
113
|
+
]
|
|
114
|
+
}
|
|
103
115
|
}
|
package/src/agent-tools.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { MeshNetwork } from "./types.js";
|
|
2
|
-
import type { FeishuApiClient } from "./feishu-client.js";
|
|
3
2
|
|
|
4
3
|
export function buildP2PTools(mesh: MeshNetwork) {
|
|
5
4
|
return [
|
|
@@ -113,48 +112,83 @@ export function buildP2PTools(mesh: MeshNetwork) {
|
|
|
113
112
|
}
|
|
114
113
|
},
|
|
115
114
|
},
|
|
116
|
-
];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function buildFeishuTools(feishuClient: FeishuApiClient | null) {
|
|
120
|
-
if (!feishuClient) return [];
|
|
121
|
-
return [
|
|
122
115
|
{
|
|
123
|
-
name: "
|
|
124
|
-
label: "
|
|
125
|
-
description: "
|
|
116
|
+
name: "p2p_get_instance_identity",
|
|
117
|
+
label: "P2P Get Instance Identity",
|
|
118
|
+
description: "Get the OpenClaw instance identity (lightweight BAID-inspired ID) of this node.",
|
|
126
119
|
parameters: {
|
|
127
120
|
type: "object" as const,
|
|
128
|
-
properties: {
|
|
129
|
-
openId: {
|
|
130
|
-
type: "string" as const,
|
|
131
|
-
description: "Feishu openId of the recipient",
|
|
132
|
-
},
|
|
133
|
-
message: {
|
|
134
|
-
type: "string" as const,
|
|
135
|
-
description: "Text message content to send",
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
required: ["openId", "message"],
|
|
121
|
+
properties: {},
|
|
139
122
|
},
|
|
140
|
-
async execute(_toolCallId: string
|
|
141
|
-
|
|
142
|
-
|
|
123
|
+
async execute(_toolCallId: string) {
|
|
124
|
+
try {
|
|
125
|
+
const identity = mesh.getInstanceIdentity();
|
|
126
|
+
if (!identity) {
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: "text" as const, text: "Instance identity not yet initialized." }],
|
|
129
|
+
details: { initialized: false },
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const lines = [
|
|
133
|
+
`Instance ID: ${identity.id}`,
|
|
134
|
+
`Name: ${identity.name}`,
|
|
135
|
+
`Pubkey: ${identity.pubkey.slice(0, 32)}...`,
|
|
136
|
+
`Binding: ${identity.binding.slice(0, 16)}...`,
|
|
137
|
+
`Bound to: ${identity.bindingComponents.username}@${identity.bindingComponents.hostname} (${identity.bindingComponents.platform})`,
|
|
138
|
+
`Created: ${new Date(identity.createdAt).toLocaleString()}`,
|
|
139
|
+
];
|
|
140
|
+
return {
|
|
141
|
+
content: [{ type: "text" as const, text: lines.join("\n") }],
|
|
142
|
+
details: { identity },
|
|
143
|
+
};
|
|
144
|
+
} catch (err) {
|
|
143
145
|
return {
|
|
144
|
-
content: [{ type: "text" as const, text: `
|
|
145
|
-
details: {
|
|
146
|
+
content: [{ type: "text" as const, text: `Error: ${String(err)}` }],
|
|
147
|
+
details: { error: String(err) },
|
|
148
|
+
isError: true,
|
|
146
149
|
};
|
|
147
150
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "p2p_get_network_info",
|
|
155
|
+
label: "P2P Get Network Info",
|
|
156
|
+
description: "Get combined network and identity info: Peer ID, Instance ID, listen addresses, and connected peers.",
|
|
157
|
+
parameters: {
|
|
158
|
+
type: "object" as const,
|
|
159
|
+
properties: {},
|
|
160
|
+
},
|
|
161
|
+
async execute(_toolCallId: string) {
|
|
162
|
+
try {
|
|
163
|
+
const identity = mesh.getInstanceIdentity();
|
|
164
|
+
const peerId = mesh.getLocalPeerId();
|
|
165
|
+
const addrs = mesh.getMultiaddrs();
|
|
166
|
+
const peers = mesh.getConnectedPeers();
|
|
167
|
+
|
|
168
|
+
const lines = [
|
|
169
|
+
`Peer ID: ${peerId || "(not started)"}`,
|
|
170
|
+
`Instance ID: ${identity?.id || "(not initialized)"}`,
|
|
171
|
+
`Instance: ${identity?.bindingComponents.username}@${identity?.bindingComponents.hostname}` || "",
|
|
172
|
+
`Listen Addrs: ${addrs.length > 0 ? addrs.join(", ") : "(none)"}`,
|
|
173
|
+
`Connected: ${peers.length} peer(s)${peers.length > 0 ? ": " + peers.join(", ") : ""}`,
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
content: [{ type: "text" as const, text: lines.join("\n") }],
|
|
178
|
+
details: {
|
|
179
|
+
peerId,
|
|
180
|
+
instanceId: identity?.id,
|
|
181
|
+
listenAddrs: addrs,
|
|
182
|
+
connectedPeers: peers,
|
|
153
183
|
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
184
|
+
};
|
|
185
|
+
} catch (err) {
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: "text" as const, text: `Error: ${String(err)}` }],
|
|
188
|
+
details: { error: String(err) },
|
|
189
|
+
isError: true,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
158
192
|
},
|
|
159
193
|
},
|
|
160
194
|
];
|
package/src/channel.ts
CHANGED
|
@@ -2,8 +2,12 @@ import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
|
|
|
2
2
|
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
|
|
3
3
|
import type { MeshNetwork, MeshAccount } from "./types.js";
|
|
4
4
|
import { sendViaMesh } from "./send.js";
|
|
5
|
+
import {
|
|
6
|
+
getLibp2pMeshRuntime,
|
|
7
|
+
hasLibp2pMeshRuntime,
|
|
8
|
+
} from "../runtime-setter-api.js";
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
function buildChannel(getMesh: () => MeshNetwork): ChannelPlugin {
|
|
7
11
|
return createChatChannelPlugin<MeshAccount>({
|
|
8
12
|
base: {
|
|
9
13
|
id: "libp2p-mesh",
|
|
@@ -42,7 +46,9 @@ export function createLibp2pMeshChannel(mesh: MeshNetwork): ChannelPlugin {
|
|
|
42
46
|
name: "default",
|
|
43
47
|
configured: true,
|
|
44
48
|
enabled: true,
|
|
45
|
-
connected:
|
|
49
|
+
connected: hasLibp2pMeshRuntime()
|
|
50
|
+
? getMesh().getConnectedPeers().length > 0
|
|
51
|
+
: false,
|
|
46
52
|
}),
|
|
47
53
|
},
|
|
48
54
|
messaging: {
|
|
@@ -57,12 +63,27 @@ export function createLibp2pMeshChannel(mesh: MeshNetwork): ChannelPlugin {
|
|
|
57
63
|
deliveryMode: "gateway",
|
|
58
64
|
sendText: async ({ to, text }) => {
|
|
59
65
|
try {
|
|
60
|
-
await sendViaMesh(
|
|
66
|
+
await sendViaMesh(getMesh(), to, text);
|
|
61
67
|
return { channel: "libp2p-mesh", messageId: `p2p-${Date.now()}` };
|
|
62
68
|
} catch (err) {
|
|
63
|
-
return {
|
|
69
|
+
return {
|
|
70
|
+
channel: "libp2p-mesh",
|
|
71
|
+
messageId: `p2p-${Date.now()}`,
|
|
72
|
+
meta: { error: String(err) },
|
|
73
|
+
};
|
|
64
74
|
}
|
|
65
75
|
},
|
|
66
76
|
},
|
|
67
77
|
}) as ChannelPlugin;
|
|
68
78
|
}
|
|
79
|
+
|
|
80
|
+
// Static channel plugin export for the bundled-channel-entry contract.
|
|
81
|
+
// The mesh instance is resolved lazily through runtime-setter-api.ts, which
|
|
82
|
+
// plugin.ts populates after starting the mesh service.
|
|
83
|
+
export const libp2pMeshPlugin: ChannelPlugin = buildChannel(getLibp2pMeshRuntime);
|
|
84
|
+
|
|
85
|
+
// Backwards-compatible factory: kept so any caller that still passes the mesh
|
|
86
|
+
// instance directly (e.g. the standalone plugin entry) continues to work.
|
|
87
|
+
export function createLibp2pMeshChannel(mesh: MeshNetwork): ChannelPlugin {
|
|
88
|
+
return buildChannel(() => mesh);
|
|
89
|
+
}
|