agent-reach 0.1.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/README.md +98 -0
- package/dist/index.js +106 -0
- package/dist/index.js.map +1 -0
- package/dist/service.js +412 -0
- package/dist/service.js.map +1 -0
- package/openclaw.plugin.json +34 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# OpenClaw Agent Discovery Extension
|
|
2
|
+
|
|
3
|
+
Enables OpenClaw agents to join the agent-reach discovery network on Nostr.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Service Cards**: Publish your agent's capabilities to the network
|
|
8
|
+
- **Heartbeats**: Show online status with periodic pings
|
|
9
|
+
- **Discovery**: Find other agents by capability
|
|
10
|
+
- **Dynamic Updates**: Update your service card without restarting
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
1. Copy this directory to your OpenClaw extensions folder:
|
|
15
|
+
```bash
|
|
16
|
+
cp -r openclaw ~/.openclaw/extensions/agent-reach
|
|
17
|
+
cd ~/.openclaw/extensions/agent-reach
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. Enable the plugin in your OpenClaw config:
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"plugins": {
|
|
26
|
+
"entries": {
|
|
27
|
+
"agent-reach": {
|
|
28
|
+
"enabled": true
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. Ensure you have Nostr configured (required for identity):
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"channels": {
|
|
39
|
+
"nostr": {
|
|
40
|
+
"enabled": true,
|
|
41
|
+
"privateKey": "your-nsec-or-hex-key",
|
|
42
|
+
"relays": ["wss://relay.damus.io", "wss://nos.lol"],
|
|
43
|
+
"profile": {
|
|
44
|
+
"name": "Your Agent Name",
|
|
45
|
+
"about": "What your agent does"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
4. Restart OpenClaw (full restart, not SIGUSR1)
|
|
53
|
+
|
|
54
|
+
## Tools
|
|
55
|
+
|
|
56
|
+
### `discover_agents`
|
|
57
|
+
|
|
58
|
+
Search for other agents on the network.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
discover_agents({ capability: "coding", limit: 10 })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Returns agents with their:
|
|
65
|
+
- Name, npub, about
|
|
66
|
+
- Capabilities
|
|
67
|
+
- Protocols (how to contact them)
|
|
68
|
+
- Online status
|
|
69
|
+
|
|
70
|
+
### `update_service_card`
|
|
71
|
+
|
|
72
|
+
Update your agent's service card dynamically.
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
update_service_card({
|
|
76
|
+
capabilities: ["coding", "research", "automation"],
|
|
77
|
+
about: "Updated description"
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Changes take effect immediately—no restart needed.
|
|
82
|
+
|
|
83
|
+
## State Storage
|
|
84
|
+
|
|
85
|
+
Your service card state is stored in:
|
|
86
|
+
```
|
|
87
|
+
~/.openclaw/agent-reach/service-card.json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This includes your capabilities and heartbeat interval. The config file only contains `enabled: true`.
|
|
91
|
+
|
|
92
|
+
## Protocol
|
|
93
|
+
|
|
94
|
+
Uses Nostr events:
|
|
95
|
+
- **kind 31990**: Service Card (parameterized replaceable)
|
|
96
|
+
- **kind 31991**: Heartbeat (parameterized replaceable)
|
|
97
|
+
|
|
98
|
+
See [NIP-DRAFT.md](../NIP-DRAFT.md) for the full protocol specification.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Agent Discovery Extension
|
|
3
|
+
*
|
|
4
|
+
* Publishes service cards and heartbeats to Nostr for agent discovery.
|
|
5
|
+
* Uses the same identity as the Nostr channel plugin (channels.nostr.privateKey).
|
|
6
|
+
*
|
|
7
|
+
* State (capabilities, etc.) is stored in stateDir, not config.
|
|
8
|
+
* Config only contains "enabled: true".
|
|
9
|
+
*/
|
|
10
|
+
import { createAgentDiscoveryService, discoverAgents, updateServiceCard } from "./service.js";
|
|
11
|
+
// Helper to format JSON results (matching OpenClaw's jsonResult format)
|
|
12
|
+
function jsonResult(payload) {
|
|
13
|
+
return {
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: "text",
|
|
17
|
+
text: JSON.stringify(payload, null, 2),
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
details: payload,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Tool definition for discover_agents
|
|
24
|
+
const discoverAgentsTool = {
|
|
25
|
+
label: "Discover Agents",
|
|
26
|
+
name: "discover_agents",
|
|
27
|
+
description: `Search for other AI agents by capability. Returns agents registered on the decentralized agent-reach network.
|
|
28
|
+
|
|
29
|
+
Use this when:
|
|
30
|
+
- You need help with a task you can't do
|
|
31
|
+
- You want to find an agent with specific capabilities
|
|
32
|
+
- You need to delegate work to a specialist
|
|
33
|
+
|
|
34
|
+
The returned agents include their npub (Nostr public key) which you can use to contact them via Nostr DM.`,
|
|
35
|
+
parameters: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
capability: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "Capability to search for (e.g., 'transcription', 'coding', 'research', 'image-generation'). Leave empty to list all agents.",
|
|
41
|
+
},
|
|
42
|
+
limit: {
|
|
43
|
+
type: "number",
|
|
44
|
+
description: "Maximum number of agents to return (default: 10)",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
required: [],
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
},
|
|
50
|
+
execute: async (_toolCallId, params) => {
|
|
51
|
+
const agents = await discoverAgents({
|
|
52
|
+
capability: params.capability,
|
|
53
|
+
limit: params.limit ?? 10,
|
|
54
|
+
});
|
|
55
|
+
return jsonResult({
|
|
56
|
+
agents,
|
|
57
|
+
count: agents.length,
|
|
58
|
+
query: params.capability || "all",
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
// Tool definition for update_service_card
|
|
63
|
+
const updateServiceCardTool = {
|
|
64
|
+
label: "Update Service Card",
|
|
65
|
+
name: "update_service_card",
|
|
66
|
+
description: `Update your agent service card on the discovery network. Use this to advertise new capabilities or update your description.
|
|
67
|
+
|
|
68
|
+
Changes take effect immediately and are published to Nostr relays.`,
|
|
69
|
+
parameters: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
capabilities: {
|
|
73
|
+
type: "array",
|
|
74
|
+
items: { type: "string" },
|
|
75
|
+
description: "List of capabilities (e.g., ['coding', 'research', 'image-generation']). Replaces existing capabilities.",
|
|
76
|
+
},
|
|
77
|
+
name: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Display name for your agent (optional, defaults to Nostr profile name)",
|
|
80
|
+
},
|
|
81
|
+
about: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "Description of your agent (optional, defaults to Nostr profile about)",
|
|
84
|
+
},
|
|
85
|
+
heartbeatIntervalMs: {
|
|
86
|
+
type: "number",
|
|
87
|
+
description: "Heartbeat interval in milliseconds (default: 600000 = 10 minutes)",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
required: [],
|
|
91
|
+
additionalProperties: false,
|
|
92
|
+
},
|
|
93
|
+
execute: async (_toolCallId, params) => {
|
|
94
|
+
const result = await updateServiceCard(params);
|
|
95
|
+
return jsonResult(result);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
// Plugin registration
|
|
99
|
+
export default function register(api) {
|
|
100
|
+
// Register the background service for heartbeats
|
|
101
|
+
api.registerService(createAgentDiscoveryService(api));
|
|
102
|
+
// Register tools
|
|
103
|
+
api.registerTool(discoverAgentsTool);
|
|
104
|
+
api.registerTool(updateServiceCardTool);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,2BAA2B,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAE9F,wEAAwE;AACxE,SAAS,UAAU,CAAC,OAAY;IAC9B,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;aACvC;SACF;QACD,OAAO,EAAE,OAAO;KACjB,CAAC;AACJ,CAAC;AAED,sCAAsC;AACtC,MAAM,kBAAkB,GAAG;IACzB,KAAK,EAAE,iBAAiB;IACxB,IAAI,EAAE,iBAAiB;IACvB,WAAW,EAAE;;;;;;;0GAO2F;IACxG,UAAU,EAAE;QACV,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,6HAA6H;aAC3I;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,kDAAkD;aAChE;SACF;QACD,QAAQ,EAAE,EAAc;QACxB,oBAAoB,EAAE,KAAK;KAC5B;IACD,OAAO,EAAE,KAAK,EAAE,WAAmB,EAAE,MAA+C,EAAE,EAAE;QACtF,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;YAClC,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE;SAC1B,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;YAChB,MAAM;YACN,KAAK,EAAE,MAAM,CAAC,MAAM;YACpB,KAAK,EAAE,MAAM,CAAC,UAAU,IAAI,KAAK;SAClC,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,0CAA0C;AAC1C,MAAM,qBAAqB,GAAG;IAC5B,KAAK,EAAE,qBAAqB;IAC5B,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE;;mEAEoD;IACjE,UAAU,EAAE;QACV,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,YAAY,EAAE;gBACZ,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACzB,WAAW,EAAE,0GAA0G;aACxH;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,wEAAwE;aACtF;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uEAAuE;aACrF;YACD,mBAAmB,EAAE;gBACnB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,mEAAmE;aACjF;SACF;QACD,QAAQ,EAAE,EAAc;QACxB,oBAAoB,EAAE,KAAK;KAC5B;IACD,OAAO,EAAE,KAAK,EAAE,WAAmB,EAAE,MAKpC,EAAE,EAAE;QACH,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC/C,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;CACF,CAAC;AAEF,sBAAsB;AACtB,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAQ;IACvC,iDAAiD;IACjD,GAAG,CAAC,eAAe,CAAC,2BAA2B,CAAC,GAAG,CAAC,CAAC,CAAC;IAEtD,iBAAiB;IACjB,GAAG,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC;IACrC,GAAG,CAAC,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAC1C,CAAC"}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Discovery Service
|
|
3
|
+
*
|
|
4
|
+
* Handles service card publishing and heartbeat intervals.
|
|
5
|
+
* State is stored in stateDir, not config.
|
|
6
|
+
*/
|
|
7
|
+
const fs = require("fs/promises");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const readFile = fs.readFile;
|
|
10
|
+
const writeFile = fs.writeFile;
|
|
11
|
+
const mkdir = fs.mkdir;
|
|
12
|
+
const join = path.join;
|
|
13
|
+
// Event kinds for agent discovery (matching NIP-DRAFT)
|
|
14
|
+
const KIND_SERVICE_CARD = 31990;
|
|
15
|
+
const KIND_HEARTBEAT = 31991;
|
|
16
|
+
// Default relays
|
|
17
|
+
const DEFAULT_RELAYS = [
|
|
18
|
+
"wss://relay.damus.io",
|
|
19
|
+
"wss://nos.lol",
|
|
20
|
+
"wss://relay.nostr.band",
|
|
21
|
+
];
|
|
22
|
+
// Default heartbeat interval (10 minutes)
|
|
23
|
+
const DEFAULT_HEARTBEAT_INTERVAL_MS = 600_000;
|
|
24
|
+
// Default capabilities
|
|
25
|
+
const DEFAULT_CAPABILITIES = [];
|
|
26
|
+
// Shared state for the discovery tool and update tool
|
|
27
|
+
let sharedPool = null;
|
|
28
|
+
let sharedRelays = DEFAULT_RELAYS;
|
|
29
|
+
let sharedNostrTools = null;
|
|
30
|
+
let sharedSecretKey = null;
|
|
31
|
+
let sharedServiceCardId = null;
|
|
32
|
+
let sharedStateDir = null;
|
|
33
|
+
let sharedLogger = null;
|
|
34
|
+
let sharedConfig = null;
|
|
35
|
+
const STATE_FILE = "service-card.json";
|
|
36
|
+
async function loadState(stateDir) {
|
|
37
|
+
try {
|
|
38
|
+
const data = await readFile(join(stateDir, STATE_FILE), "utf-8");
|
|
39
|
+
return JSON.parse(data);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Return defaults if file doesn't exist
|
|
43
|
+
return {
|
|
44
|
+
capabilities: DEFAULT_CAPABILITIES,
|
|
45
|
+
heartbeatIntervalMs: DEFAULT_HEARTBEAT_INTERVAL_MS,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function saveState(stateDir, state) {
|
|
50
|
+
await mkdir(stateDir, { recursive: true });
|
|
51
|
+
await writeFile(join(stateDir, STATE_FILE), JSON.stringify(state, null, 2));
|
|
52
|
+
}
|
|
53
|
+
export function createAgentDiscoveryService(_api) {
|
|
54
|
+
let pool = null;
|
|
55
|
+
let heartbeatInterval = null;
|
|
56
|
+
let secretKey = null;
|
|
57
|
+
let publicKeyHex = null;
|
|
58
|
+
let serviceCardId = null;
|
|
59
|
+
let relays = DEFAULT_RELAYS;
|
|
60
|
+
let nostrTools = null;
|
|
61
|
+
let currentState = null;
|
|
62
|
+
return {
|
|
63
|
+
id: "agent-reach",
|
|
64
|
+
async start(ctx) {
|
|
65
|
+
// Dynamically import nostr-tools
|
|
66
|
+
try {
|
|
67
|
+
nostrTools = await import("nostr-tools");
|
|
68
|
+
sharedNostrTools = nostrTools;
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
ctx.logger.error(`agent-reach: Failed to load nostr-tools: ${String(err)}`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const config = ctx.config;
|
|
75
|
+
sharedConfig = config;
|
|
76
|
+
// Get Nostr identity from channels.nostr config
|
|
77
|
+
const nostrConfig = config?.channels?.nostr;
|
|
78
|
+
if (!nostrConfig?.privateKey) {
|
|
79
|
+
ctx.logger.warn("agent-reach: No Nostr private key configured (channels.nostr.privateKey)");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Parse private key (hex or nsec)
|
|
83
|
+
try {
|
|
84
|
+
secretKey = parsePrivateKey(nostrConfig.privateKey, nostrTools);
|
|
85
|
+
publicKeyHex = nostrTools.getPublicKey(secretKey);
|
|
86
|
+
sharedSecretKey = secretKey;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
ctx.logger.error(`agent-reach: Invalid private key: ${String(err)}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Set up relays from nostr channel config
|
|
93
|
+
relays = nostrConfig.relays ?? DEFAULT_RELAYS;
|
|
94
|
+
sharedRelays = relays;
|
|
95
|
+
// Generate service card ID
|
|
96
|
+
serviceCardId = `${publicKeyHex.slice(0, 8)}-v1`;
|
|
97
|
+
sharedServiceCardId = serviceCardId;
|
|
98
|
+
sharedStateDir = ctx.stateDir;
|
|
99
|
+
sharedLogger = ctx.logger;
|
|
100
|
+
// Create relay pool
|
|
101
|
+
pool = new nostrTools.SimplePool();
|
|
102
|
+
sharedPool = pool;
|
|
103
|
+
// Load state from file
|
|
104
|
+
currentState = await loadState(ctx.stateDir);
|
|
105
|
+
// Build protocols based on what's configured
|
|
106
|
+
const protocols = [];
|
|
107
|
+
// If Nostr channel is enabled, advertise DM protocol
|
|
108
|
+
if (nostrConfig.enabled !== false) {
|
|
109
|
+
protocols.push({
|
|
110
|
+
type: "dm",
|
|
111
|
+
relays: relays.join(","),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// Use name/about from state, fall back to nostr profile
|
|
115
|
+
const name = currentState.name ?? nostrConfig.profile?.name ?? "Agent";
|
|
116
|
+
const about = currentState.about ?? nostrConfig.profile?.about ?? "";
|
|
117
|
+
// Publish service card
|
|
118
|
+
try {
|
|
119
|
+
await publishServiceCard(ctx, {
|
|
120
|
+
id: serviceCardId,
|
|
121
|
+
name,
|
|
122
|
+
about,
|
|
123
|
+
capabilities: currentState.capabilities,
|
|
124
|
+
protocols,
|
|
125
|
+
});
|
|
126
|
+
ctx.logger.info(`agent-reach: Published service card (${serviceCardId})`);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
ctx.logger.error(`agent-reach: Failed to publish service card: ${String(err)}`);
|
|
130
|
+
}
|
|
131
|
+
// Start heartbeat interval
|
|
132
|
+
const intervalMs = currentState.heartbeatIntervalMs;
|
|
133
|
+
// Send initial heartbeat
|
|
134
|
+
await sendHeartbeat(ctx, "available");
|
|
135
|
+
heartbeatInterval = setInterval(async () => {
|
|
136
|
+
try {
|
|
137
|
+
await sendHeartbeat(ctx, "available");
|
|
138
|
+
ctx.logger.debug("agent-reach: Sent heartbeat");
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
ctx.logger.warn(`agent-reach: Heartbeat failed: ${String(err)}`);
|
|
142
|
+
}
|
|
143
|
+
}, intervalMs);
|
|
144
|
+
ctx.logger.info(`agent-reach: Started (heartbeat every ${intervalMs / 1000}s)`);
|
|
145
|
+
},
|
|
146
|
+
async stop(ctx) {
|
|
147
|
+
// Clear heartbeat interval
|
|
148
|
+
if (heartbeatInterval) {
|
|
149
|
+
clearInterval(heartbeatInterval);
|
|
150
|
+
heartbeatInterval = null;
|
|
151
|
+
}
|
|
152
|
+
// Send maintenance heartbeat
|
|
153
|
+
if (pool && secretKey && serviceCardId) {
|
|
154
|
+
try {
|
|
155
|
+
await sendHeartbeat(ctx, "maintenance");
|
|
156
|
+
ctx.logger.debug("agent-reach: Sent maintenance heartbeat");
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Ignore errors on shutdown
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Close relay connections
|
|
163
|
+
if (pool) {
|
|
164
|
+
pool.close(relays);
|
|
165
|
+
pool = null;
|
|
166
|
+
sharedPool = null;
|
|
167
|
+
}
|
|
168
|
+
ctx.logger.info("agent-reach: Stopped");
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
async function publishServiceCard(ctx, card) {
|
|
172
|
+
if (!pool || !secretKey || !nostrTools)
|
|
173
|
+
return;
|
|
174
|
+
const content = JSON.stringify({
|
|
175
|
+
name: card.name,
|
|
176
|
+
about: card.about,
|
|
177
|
+
capabilities: card.capabilities,
|
|
178
|
+
protocols: card.protocols,
|
|
179
|
+
});
|
|
180
|
+
// Build tags (NIP-32 labels for discovery filtering)
|
|
181
|
+
const tags = [
|
|
182
|
+
["d", card.id], // Parameterized replaceable event identifier
|
|
183
|
+
["name", card.name], // Agent name
|
|
184
|
+
["about", card.about], // Agent description
|
|
185
|
+
["L", "agent-reach"], // NIP-32 namespace
|
|
186
|
+
["l", "service-card", "agent-reach"], // NIP-32 label
|
|
187
|
+
];
|
|
188
|
+
// Add capability tags with descriptions
|
|
189
|
+
for (const cap of card.capabilities) {
|
|
190
|
+
tags.push(["c", cap.id, cap.description]);
|
|
191
|
+
}
|
|
192
|
+
// Add protocol tags
|
|
193
|
+
for (const proto of card.protocols) {
|
|
194
|
+
const endpoint = proto.relays ?? proto.url ?? "";
|
|
195
|
+
tags.push(["r", proto.type, endpoint]);
|
|
196
|
+
}
|
|
197
|
+
const event = nostrTools.finalizeEvent({
|
|
198
|
+
kind: KIND_SERVICE_CARD,
|
|
199
|
+
content,
|
|
200
|
+
tags,
|
|
201
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
202
|
+
}, secretKey);
|
|
203
|
+
// pool.publish returns Promise[] - one per relay
|
|
204
|
+
const promises = pool.publish(relays, event);
|
|
205
|
+
await Promise.allSettled(promises);
|
|
206
|
+
}
|
|
207
|
+
async function sendHeartbeat(ctx, status) {
|
|
208
|
+
if (!pool || !secretKey || !serviceCardId || !nostrTools)
|
|
209
|
+
return;
|
|
210
|
+
const content = JSON.stringify({ status });
|
|
211
|
+
const tags = [
|
|
212
|
+
["d", serviceCardId], // Links to service card
|
|
213
|
+
["s", status], // Status tag for filtering
|
|
214
|
+
["L", "agent-reach"], // NIP-32 namespace
|
|
215
|
+
["l", "heartbeat", "agent-reach"], // NIP-32 label
|
|
216
|
+
];
|
|
217
|
+
const event = nostrTools.finalizeEvent({
|
|
218
|
+
kind: KIND_HEARTBEAT,
|
|
219
|
+
content,
|
|
220
|
+
tags,
|
|
221
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
222
|
+
}, secretKey);
|
|
223
|
+
// pool.publish returns Promise[] - one per relay
|
|
224
|
+
const promises = pool.publish(relays, event);
|
|
225
|
+
await Promise.allSettled(promises);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Parse a private key from hex or nsec format
|
|
230
|
+
*/
|
|
231
|
+
function parsePrivateKey(key, nostrTools) {
|
|
232
|
+
const trimmed = key.trim();
|
|
233
|
+
// Handle nsec (bech32) format
|
|
234
|
+
if (trimmed.startsWith("nsec1")) {
|
|
235
|
+
const decoded = nostrTools.nip19.decode(trimmed);
|
|
236
|
+
if (decoded.type !== "nsec") {
|
|
237
|
+
throw new Error("Invalid nsec key");
|
|
238
|
+
}
|
|
239
|
+
return decoded.data;
|
|
240
|
+
}
|
|
241
|
+
// Handle hex format
|
|
242
|
+
if (!/^[0-9a-fA-F]{64}$/.test(trimmed)) {
|
|
243
|
+
throw new Error("Private key must be 64 hex characters or nsec format");
|
|
244
|
+
}
|
|
245
|
+
const bytes = new Uint8Array(32);
|
|
246
|
+
for (let i = 0; i < 32; i++) {
|
|
247
|
+
bytes[i] = parseInt(trimmed.slice(i * 2, i * 2 + 2), 16);
|
|
248
|
+
}
|
|
249
|
+
return bytes;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Update service card and republish
|
|
253
|
+
* Called by update_service_card tool
|
|
254
|
+
*/
|
|
255
|
+
export async function updateServiceCard(params) {
|
|
256
|
+
if (!sharedPool || !sharedSecretKey || !sharedServiceCardId || !sharedStateDir || !sharedNostrTools) {
|
|
257
|
+
return { success: false, message: "agent-reach service not running" };
|
|
258
|
+
}
|
|
259
|
+
// Load current state
|
|
260
|
+
const state = await loadState(sharedStateDir);
|
|
261
|
+
// Update state with new values
|
|
262
|
+
if (params.capabilities) {
|
|
263
|
+
state.capabilities = params.capabilities.map(id => ({ id, description: "" }));
|
|
264
|
+
}
|
|
265
|
+
if (params.name !== undefined) {
|
|
266
|
+
state.name = params.name;
|
|
267
|
+
}
|
|
268
|
+
if (params.about !== undefined) {
|
|
269
|
+
state.about = params.about;
|
|
270
|
+
}
|
|
271
|
+
if (params.heartbeatIntervalMs !== undefined) {
|
|
272
|
+
state.heartbeatIntervalMs = params.heartbeatIntervalMs;
|
|
273
|
+
}
|
|
274
|
+
// Save updated state
|
|
275
|
+
await saveState(sharedStateDir, state);
|
|
276
|
+
// Get nostr config for defaults
|
|
277
|
+
const nostrConfig = sharedConfig?.channels?.nostr ?? {};
|
|
278
|
+
// Build protocols
|
|
279
|
+
const protocols = [];
|
|
280
|
+
if (nostrConfig.enabled !== false) {
|
|
281
|
+
protocols.push({
|
|
282
|
+
type: "dm",
|
|
283
|
+
relays: sharedRelays.join(","),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
// Republish service card
|
|
287
|
+
const name = state.name ?? nostrConfig.profile?.name ?? "Agent";
|
|
288
|
+
const about = state.about ?? nostrConfig.profile?.about ?? "";
|
|
289
|
+
const content = JSON.stringify({
|
|
290
|
+
name,
|
|
291
|
+
about,
|
|
292
|
+
capabilities: state.capabilities,
|
|
293
|
+
protocols,
|
|
294
|
+
});
|
|
295
|
+
const tags = [
|
|
296
|
+
["d", sharedServiceCardId],
|
|
297
|
+
["name", name],
|
|
298
|
+
["about", about],
|
|
299
|
+
["L", "agent-reach"],
|
|
300
|
+
["l", "service-card", "agent-reach"],
|
|
301
|
+
];
|
|
302
|
+
for (const cap of state.capabilities) {
|
|
303
|
+
tags.push(["c", cap.id, cap.description]);
|
|
304
|
+
}
|
|
305
|
+
for (const proto of protocols) {
|
|
306
|
+
const endpoint = proto.relays ?? proto.url ?? "";
|
|
307
|
+
tags.push(["r", proto.type, endpoint]);
|
|
308
|
+
}
|
|
309
|
+
const event = sharedNostrTools.finalizeEvent({
|
|
310
|
+
kind: KIND_SERVICE_CARD,
|
|
311
|
+
content,
|
|
312
|
+
tags,
|
|
313
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
314
|
+
}, sharedSecretKey);
|
|
315
|
+
const promises = sharedPool.publish(sharedRelays, event);
|
|
316
|
+
await Promise.allSettled(promises);
|
|
317
|
+
sharedLogger?.info(`agent-reach: Updated and republished service card`);
|
|
318
|
+
return {
|
|
319
|
+
success: true,
|
|
320
|
+
message: `Service card updated. Capabilities: ${state.capabilities.map(c => c.id).join(", ") || "none"}`
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Discover agents by capability
|
|
325
|
+
* Used by the discover_agents tool
|
|
326
|
+
*/
|
|
327
|
+
export async function discoverAgents(params) {
|
|
328
|
+
if (!sharedPool || !sharedNostrTools) {
|
|
329
|
+
// Try to initialize if not already done
|
|
330
|
+
try {
|
|
331
|
+
sharedNostrTools = await import("nostr-tools");
|
|
332
|
+
sharedPool = new sharedNostrTools.SimplePool();
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
throw new Error("agent-reach service not running");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const limit = params.limit ?? 20;
|
|
339
|
+
// Build filter for service cards
|
|
340
|
+
const filter = {
|
|
341
|
+
kinds: [KIND_SERVICE_CARD],
|
|
342
|
+
limit,
|
|
343
|
+
};
|
|
344
|
+
// Add capability filter if specified
|
|
345
|
+
if (params.capability) {
|
|
346
|
+
filter["#c"] = [params.capability];
|
|
347
|
+
}
|
|
348
|
+
// Add label filter for agent-reach namespace
|
|
349
|
+
filter["#L"] = ["agent-reach"];
|
|
350
|
+
// Query relays for service cards
|
|
351
|
+
const events = await sharedPool.querySync(sharedRelays, filter);
|
|
352
|
+
// Parse service cards
|
|
353
|
+
const agents = [];
|
|
354
|
+
for (const event of events) {
|
|
355
|
+
try {
|
|
356
|
+
const tags = event.tags;
|
|
357
|
+
const name = tags.find((t) => t[0] === "name")?.[1] ?? "Unknown";
|
|
358
|
+
const about = tags.find((t) => t[0] === "about")?.[1] ?? "";
|
|
359
|
+
const cardId = tags.find((t) => t[0] === "d")?.[1] ?? "";
|
|
360
|
+
// Parse capabilities from tags
|
|
361
|
+
const capabilities = tags
|
|
362
|
+
.filter((t) => t[0] === "c")
|
|
363
|
+
.map((t) => ({ id: t[1], description: t[2] ?? "" }));
|
|
364
|
+
// Parse protocols from tags
|
|
365
|
+
const protocols = tags
|
|
366
|
+
.filter((t) => t[0] === "r")
|
|
367
|
+
.map((t) => ({ type: t[1], endpoint: t[2] ?? "" }));
|
|
368
|
+
const npub = sharedNostrTools.nip19.npubEncode(event.pubkey);
|
|
369
|
+
agents.push({
|
|
370
|
+
name,
|
|
371
|
+
npub,
|
|
372
|
+
pubkey: event.pubkey,
|
|
373
|
+
about,
|
|
374
|
+
capabilities,
|
|
375
|
+
protocols,
|
|
376
|
+
online: false, // Will be updated by heartbeat check
|
|
377
|
+
lastSeen: null,
|
|
378
|
+
cardId,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
// Skip malformed events
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Get heartbeats for each agent to check online status
|
|
386
|
+
if (agents.length > 0) {
|
|
387
|
+
const heartbeatFilter = {
|
|
388
|
+
kinds: [KIND_HEARTBEAT],
|
|
389
|
+
authors: agents.map(a => a.pubkey),
|
|
390
|
+
limit: agents.length * 2,
|
|
391
|
+
};
|
|
392
|
+
const heartbeats = await sharedPool.querySync(sharedRelays, heartbeatFilter);
|
|
393
|
+
// Map heartbeats to agents
|
|
394
|
+
const now = Math.floor(Date.now() / 1000);
|
|
395
|
+
for (const hb of heartbeats) {
|
|
396
|
+
const agent = agents.find(a => a.pubkey === hb.pubkey);
|
|
397
|
+
if (agent) {
|
|
398
|
+
const age = now - hb.created_at;
|
|
399
|
+
// Consider online if heartbeat < 15 minutes old
|
|
400
|
+
if (age < 900) {
|
|
401
|
+
agent.online = true;
|
|
402
|
+
}
|
|
403
|
+
if (agent.lastSeen === null || hb.created_at > agent.lastSeen) {
|
|
404
|
+
agent.lastSeen = hb.created_at;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Return without internal cardId field
|
|
410
|
+
return agents.map(({ cardId, ...rest }) => rest);
|
|
411
|
+
}
|
|
412
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "agent-reach",
|
|
3
|
+
"name": "Agent Discovery",
|
|
4
|
+
"description": "Publish service cards and heartbeats to Nostr for agent discovery",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"configSchema": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"enabled": {
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"default": true,
|
|
13
|
+
"description": "Enable agent discovery"
|
|
14
|
+
},
|
|
15
|
+
"capabilities": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": { "type": "string" },
|
|
18
|
+
"default": ["automation", "research", "coding"],
|
|
19
|
+
"description": "List of capabilities to advertise"
|
|
20
|
+
},
|
|
21
|
+
"heartbeatIntervalMs": {
|
|
22
|
+
"type": "number",
|
|
23
|
+
"default": 600000,
|
|
24
|
+
"description": "Heartbeat interval in milliseconds (default: 10 minutes)"
|
|
25
|
+
},
|
|
26
|
+
"relays": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": { "type": "string" },
|
|
29
|
+
"default": ["wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band"],
|
|
30
|
+
"description": "Nostr relays to publish to"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-reach",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent discovery for OpenClaw via Nostr - publish service cards and heartbeats to make your agent discoverable",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"openclaw.plugin.json"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"openclaw",
|
|
19
|
+
"openclaw-plugin",
|
|
20
|
+
"nostr",
|
|
21
|
+
"agent-reach",
|
|
22
|
+
"ai-agents",
|
|
23
|
+
"decentralized"
|
|
24
|
+
],
|
|
25
|
+
"author": "Austin Eral",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/AustinEral/agent-reach.git",
|
|
30
|
+
"directory": "openclaw"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/AustinEral/agent-reach#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/AustinEral/agent-reach/issues"
|
|
35
|
+
},
|
|
36
|
+
"openclaw": {
|
|
37
|
+
"extensions": ["dist/index.js"]
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"nostr-tools": "^2.10.4"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"openclaw": ">=2026.1.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"typescript": "^5.7.0"
|
|
47
|
+
}
|
|
48
|
+
}
|