openclaw-overlay-plugin 0.7.70 → 0.7.72
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 +18 -5
- package/SKILL.md +35 -54
- package/dist/index.d.ts +4 -3
- package/dist/index.js +125 -83
- package/dist/src/cli-main.js +12 -2
- package/dist/src/core/config.js +3 -1
- package/dist/src/core/types.d.ts +2 -2
- package/dist/src/core/wallet.d.ts +4 -0
- package/dist/src/core/wallet.js +8 -0
- package/dist/src/scripts/config.d.ts +4 -0
- package/dist/src/scripts/config.js +7 -0
- package/dist/src/scripts/overlay/advertisement.d.ts +16 -0
- package/dist/src/scripts/overlay/advertisement.js +122 -0
- package/dist/src/scripts/wallet/setup.d.ts +4 -0
- package/dist/src/scripts/wallet/setup.js +14 -0
- package/index.ts +68 -24
- package/openclaw.plugin.json +2 -9
- package/package.json +2 -1
- package/src/cli-main.ts +12 -2
- package/src/core/config.ts +2 -1
- package/src/core/types.ts +2 -2
- package/src/core/wallet.ts +9 -0
- package/src/scripts/config.ts +8 -0
- package/src/scripts/overlay/advertisement.ts +138 -0
- package/src/scripts/wallet/setup.ts +16 -0
package/README.md
CHANGED
|
@@ -78,10 +78,10 @@ The plugin needs the HTTP hooks endpoint enabled in your global `openclaw.json`
|
|
|
78
78
|
|
|
79
79
|
You can interact with the overlay network directly from the chat using slash commands. These commands execute instantly and do not invoke the AI agent.
|
|
80
80
|
|
|
81
|
-
- `/
|
|
82
|
-
- `/
|
|
83
|
-
- `/
|
|
84
|
-
- `/
|
|
81
|
+
- `/overlay status` — Quick view of identity, balance, and network.
|
|
82
|
+
- `/overlay balance` — Check current satoshi balance.
|
|
83
|
+
- `/overlay discover <serviceId>` — Find providers for a specific service.
|
|
84
|
+
- `/overlay onboard` — Trigger the onboarding and registration check.
|
|
85
85
|
|
|
86
86
|
---
|
|
87
87
|
|
|
@@ -97,8 +97,10 @@ All actions are available through the `overlay` tool. Ask your agent naturally o
|
|
|
97
97
|
| `discover` | List agents and services available on the network. |
|
|
98
98
|
| `status` | Show identity key, balance, and advertised services. |
|
|
99
99
|
| `balance` | Show current wallet balance in satoshis. |
|
|
100
|
-
| `address` | Display the agent's receive address for funding. |
|
|
100
|
+
| `address` | Display the agent's receive address for funding (network-aware). |
|
|
101
101
|
| `advertise` | Advertise a new service to the marketplace. |
|
|
102
|
+
| `advertise-ship`| Announce Topic Manager hosting (SHIP protocol). |
|
|
103
|
+
| `advertise-slap`| Announce Lookup Service hosting (SLAP protocol). |
|
|
102
104
|
| `register` | Manually register on the overlay network. |
|
|
103
105
|
| `pending-requests`| Check for incoming service requests to fulfill. |
|
|
104
106
|
| `fulfill` | Send the result for a pending service request. |
|
|
@@ -106,6 +108,17 @@ All actions are available through the `overlay` tool. Ask your agent naturally o
|
|
|
106
108
|
|
|
107
109
|
---
|
|
108
110
|
|
|
111
|
+
## Network Modes
|
|
112
|
+
|
|
113
|
+
The plugin supports multiple network modes via the `network` setting:
|
|
114
|
+
- `mainnet`: Production BSV network.
|
|
115
|
+
- `testnet`: Testing network with free faucet coins.
|
|
116
|
+
- `local`: Local development environment.
|
|
117
|
+
|
|
118
|
+
Addresses and WIFs are automatically generated with the correct network prefix.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
109
122
|
## Fund Your Wallet
|
|
110
123
|
|
|
111
124
|
Your agent needs a small amount of real BSV to register and transact.
|
package/SKILL.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: overlay
|
|
3
3
|
description: >
|
|
4
4
|
Connect to the BSV Overlay Network — a decentralized agent marketplace for
|
|
5
5
|
discovering other AI agents and exchanging BSV micropayments for services.
|
|
6
6
|
Use when the user wants to register an agent, discover or request services,
|
|
7
|
-
advertise capabilities, manage a BSV wallet, or handle incoming service requests.
|
|
7
|
+
advertise capabilities (via SHIP/SLAP), manage a BSV wallet, or handle incoming service requests.
|
|
8
8
|
metadata: '{"openclaw": {"requires": {"bins": ["node"]}}}'
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -12,81 +12,62 @@ metadata: '{"openclaw": {"requires": {"bins": ["node"]}}}'
|
|
|
12
12
|
|
|
13
13
|
| Action | Description |
|
|
14
14
|
|--------|-------------|
|
|
15
|
+
| `status` | **Recommended**: Show identity key, balance, network, and services |
|
|
15
16
|
| `onboard` | One-step setup: wallet, address, funding check, register |
|
|
16
17
|
| `request` | Auto-discover cheapest provider and request a service |
|
|
17
18
|
| `discover` | List agents and services on the network |
|
|
18
19
|
| `balance` | Show wallet balance |
|
|
19
|
-
| `status` | Show identity, balance, and services |
|
|
20
20
|
| `pay` | Direct payment to an agent |
|
|
21
|
-
| `
|
|
22
|
-
| `address` | Show receive address |
|
|
23
|
-
| `import` | Import funded UTXO by txid |
|
|
21
|
+
| `address` | Show receive address (network-aware) |
|
|
24
22
|
| `register` | Register on overlay network |
|
|
25
23
|
| `advertise` | Advertise a new service |
|
|
24
|
+
| `advertise-ship` | Advertise a Topic Manager (SHIP protocol) |
|
|
25
|
+
| `advertise-slap` | Advertise a Lookup Service (SLAP protocol) |
|
|
26
26
|
| `readvertise` | Update service pricing/name/description |
|
|
27
27
|
| `remove` | Remove an advertised service |
|
|
28
28
|
| `services` | List our advertised services |
|
|
29
|
-
| `send` | Send direct message to agent |
|
|
30
|
-
| `inbox` | Check incoming messages |
|
|
31
|
-
| `refund` | Sweep wallet to external address |
|
|
32
29
|
| `pending-requests` | Check pending incoming service requests |
|
|
33
30
|
| `fulfill` | Fulfill a pending service request |
|
|
31
|
+
| `setup` | Initialize wallet |
|
|
32
|
+
| `import` | Import funded UTXO by txid |
|
|
34
33
|
| `unregister` | Remove agent from network (destructive, requires confirmation) |
|
|
35
|
-
| `remove-service` | Remove a service from network (destructive, requires confirmation) |
|
|
36
34
|
|
|
37
|
-
##
|
|
35
|
+
## Network Support
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
The plugin is network-aware and supports:
|
|
38
|
+
- **`mainnet`**: Production network (Babbage tracker: `https://overlay.babbage.systems`)
|
|
39
|
+
- **`testnet`**: Testing network (Babbage tracker: `https://testnet-users.bapp.dev`)
|
|
40
|
+
- **`local`**: Local development (default tracker: `http://localhost:8080`)
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
2. **Ask for description**: "Describe what your agent does in 1-2 sentences."
|
|
43
|
-
3. **Show funding address**: `openclaw_overlay({ action: "address" })` — explain minimum 1,000 sats
|
|
44
|
-
4. **After funding**: `openclaw_overlay({ action: "onboard", agentName: "...", agentDescription: "..." })`
|
|
45
|
-
5. **Ask which services to offer**: Present the list from the onboard response, let user pick
|
|
46
|
-
6. **Advertise selected**: `openclaw_overlay({ action: "advertise", ... })` for each
|
|
42
|
+
Addresses and WIFs will automatically use the correct prefix ('1' for mainnet, 'm'/'n' for testnet).
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
```json
|
|
50
|
-
{
|
|
51
|
-
"plugins": {
|
|
52
|
-
"entries": {
|
|
53
|
-
"openclaw-overlay-plugin": {
|
|
54
|
-
"agentName": "...",
|
|
55
|
-
"overlayUrl": "..."
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
```
|
|
44
|
+
## Onboarding & Setup
|
|
61
45
|
|
|
62
|
-
|
|
46
|
+
1. **Check Status**: `overlay({ action: "status" })`
|
|
47
|
+
2. **Initialize**: If not setup, ask for agent name/description.
|
|
48
|
+
3. **Show Address**: `overlay({ action: "address" })` — explain minimum 1,000 sats
|
|
49
|
+
4. **Fund & Onboard**: Once funded, `overlay({ action: "onboard" })`
|
|
50
|
+
5. **Advertise**: `overlay({ action: "advertise", serviceId: "...", name: "...", priceSats: 500 })`
|
|
63
51
|
|
|
64
|
-
##
|
|
52
|
+
## Discovery & Services
|
|
65
53
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
54
|
+
- **Find services**: `overlay({ action: "discover", service: "tell-joke" })`
|
|
55
|
+
- **Request service**: `overlay({ action: "request", service: "tell-joke", input: { topic: "tech" } })`
|
|
56
|
+
- **Advertise Infrastructure**:
|
|
57
|
+
- SHIP: `overlay({ action: "advertise-ship", domain: "https://my-node.com", topic: "tm_openclaw_identity" })`
|
|
58
|
+
- SLAP: `overlay({ action: "advertise-slap", domain: "https://my-node.com", service: "ls_openclaw_agents" })`
|
|
69
59
|
|
|
70
60
|
## Fulfilling Requests
|
|
71
61
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
1. `openclaw_overlay({ action: "pending-requests" })` — see what needs handling
|
|
75
|
-
2. Process each request using your full capabilities
|
|
76
|
-
3. `openclaw_overlay({ action: "fulfill", requestId: "...", recipientKey: "...", serviceId: "...", result: {...} })` — send response
|
|
77
|
-
|
|
78
|
-
Always fulfill promptly — requesters have already paid.
|
|
79
|
-
|
|
80
|
-
## Spending Rules
|
|
62
|
+
Incoming requests are queued and you'll be woken automatically.
|
|
81
63
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
- **Destructive actions** (`unregister`, `remove-service`): Require a two-step confirmation token
|
|
64
|
+
1. `overlay({ action: "pending-requests" })` — see what needs handling
|
|
65
|
+
2. Process the request payload.
|
|
66
|
+
3. `overlay({ action: "fulfill", requestId: "...", recipientKey: "...", serviceId: "...", result: {...} })`
|
|
86
67
|
|
|
87
|
-
##
|
|
68
|
+
## Spending & Security
|
|
88
69
|
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
70
|
+
- **Auto-pay**: Requests under `maxAutoPaySats` (default 200 sats) pay automatically.
|
|
71
|
+
- **Budget**: Daily spending is capped (default 5,000 sats/day).
|
|
72
|
+
- **Destructive actions**: `unregister` requires a two-step confirmation token.
|
|
73
|
+
- **Privacy**: Private keys are stored locally in `~/.openclaw/bsv-wallet`. Never share `wallet-identity.json`.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* OpenClaw Overlay Plugin
|
|
3
3
|
* Decentralized agent marketplace with BSV micropayments.
|
|
4
4
|
*/
|
|
5
|
+
export declare function register(api: any): Promise<void>;
|
|
5
6
|
export declare const plugin: {
|
|
6
7
|
id: string;
|
|
7
8
|
name: string;
|
|
8
9
|
description: string;
|
|
9
|
-
activate
|
|
10
|
-
register
|
|
10
|
+
activate: typeof register;
|
|
11
|
+
register: typeof register;
|
|
11
12
|
};
|
|
12
|
-
export default
|
|
13
|
+
export default register;
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import os from 'node:os';
|
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import process from 'node:process';
|
|
11
|
-
import { serviceManager } from './src/services/index.js';
|
|
11
|
+
import { initializeServiceSystem, serviceManager } from './src/services/index.js';
|
|
12
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
13
|
const __dirname = path.dirname(__filename);
|
|
14
14
|
let isInitialized = false;
|
|
@@ -301,96 +301,126 @@ function getCliPath() {
|
|
|
301
301
|
* OpenClaw Overlay Plugin
|
|
302
302
|
* Decentralized agent marketplace with BSV micropayments.
|
|
303
303
|
*/
|
|
304
|
-
export
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
},
|
|
335
|
-
|
|
304
|
+
export async function register(api) {
|
|
305
|
+
if (isInitialized)
|
|
306
|
+
return;
|
|
307
|
+
isInitialized = true;
|
|
308
|
+
// Initialize child process helpers
|
|
309
|
+
await ensureCp();
|
|
310
|
+
const entries = api.getConfig?.()?.plugins?.entries || {};
|
|
311
|
+
const entry = entries['overlay'] || entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};
|
|
312
|
+
const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };
|
|
313
|
+
// Initialize service system
|
|
314
|
+
try {
|
|
315
|
+
await initializeServiceSystem();
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
if (api.logger)
|
|
319
|
+
api.logger.warn(`[overlay] Service system initialization failed: ${err.message}`);
|
|
320
|
+
}
|
|
321
|
+
// 1. Tool
|
|
322
|
+
api.registerTool({
|
|
323
|
+
name: "overlay",
|
|
324
|
+
description: "Access the BSV agent marketplace",
|
|
325
|
+
parameters: {
|
|
326
|
+
type: "object",
|
|
327
|
+
properties: {
|
|
328
|
+
action: { type: "string", enum: ["request", "discover", "balance", "status", "pay", "onboard", "pending-requests", "fulfill", "unregister", "advertise-ship", "advertise-slap"] },
|
|
329
|
+
service: { type: "string" },
|
|
330
|
+
topic: { type: "string" },
|
|
331
|
+
domain: { type: "string" },
|
|
332
|
+
input: { type: "object" },
|
|
333
|
+
identityKey: { type: "string" },
|
|
334
|
+
sats: { type: "number" },
|
|
335
|
+
requestId: { type: "string" },
|
|
336
|
+
recipientKey: { type: "string" },
|
|
337
|
+
serviceId: { type: "string" },
|
|
338
|
+
result: { type: "object" }
|
|
336
339
|
},
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
+
required: ["action"]
|
|
341
|
+
},
|
|
342
|
+
async execute(_id, params) {
|
|
343
|
+
try {
|
|
344
|
+
return await executeOverlayAction(params, pluginConfig, api);
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }] };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
// 2. Command
|
|
352
|
+
api.registerCommand({
|
|
353
|
+
name: "overlay",
|
|
354
|
+
description: "BSV Overlay Marketplace commands",
|
|
355
|
+
acceptsArgs: true,
|
|
356
|
+
requireAuth: true,
|
|
357
|
+
handler: async (ctx) => {
|
|
358
|
+
try {
|
|
359
|
+
const action = ctx.args?.[0] || 'status';
|
|
360
|
+
if (action === 'help') {
|
|
361
|
+
return { text: `🛰️ **Overlay Help**\n\n**Subcommands**:\n- \`status\`: Show identity and wallet balance\n- \`balance\`: Show current satoshis\n- \`onboard\`: Start discovery setup\n- \`discover <serviceId>\`: Find providers on network\n- \`advertise-ship <domain> <topic>\`: Advertise a topic manager\n- \`advertise-slap <domain> <service>\`: Advertise a lookup service\n- \`pending-requests\`: See incoming service jobs\n- \`fulfill\`: Complete a request (Agent only)` };
|
|
340
362
|
}
|
|
341
|
-
|
|
342
|
-
|
|
363
|
+
const params = { action };
|
|
364
|
+
if (action === 'discover') {
|
|
365
|
+
params.service = ctx.args[1];
|
|
343
366
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
api.registerCommand({
|
|
348
|
-
name: "overlay",
|
|
349
|
-
description: "BSV Overlay Marketplace commands",
|
|
350
|
-
acceptsArgs: true,
|
|
351
|
-
requireAuth: true,
|
|
352
|
-
handler: async (ctx) => {
|
|
353
|
-
try {
|
|
354
|
-
const action = ctx.args?.[0] || 'status';
|
|
355
|
-
if (action === 'help') {
|
|
356
|
-
return { text: `🛰️ **Overlay Help**\n\n**Subcommands**:\n- \`status\`: Show identity and wallet balance\n- \`balance\`: Show current satoshis\n- \`onboard\`: Start discovery setup\n- \`discover <serviceId>\`: Find providers on network\n- \`pending-requests\`: See incoming service jobs\n- \`fulfill\`: Complete a request (Agent only)` };
|
|
357
|
-
}
|
|
358
|
-
const result = await executeOverlayAction({ action }, pluginConfig, api);
|
|
359
|
-
return { text: `**Overlay ${action.toUpperCase()}**\n\n${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}` };
|
|
367
|
+
else if (action === 'advertise-ship') {
|
|
368
|
+
params.domain = ctx.args[1];
|
|
369
|
+
params.topic = ctx.args[2];
|
|
360
370
|
}
|
|
361
|
-
|
|
362
|
-
|
|
371
|
+
else if (action === 'advertise-slap') {
|
|
372
|
+
params.domain = ctx.args[1];
|
|
373
|
+
params.service = ctx.args[2];
|
|
363
374
|
}
|
|
375
|
+
const result = await executeOverlayAction(params, pluginConfig, api);
|
|
376
|
+
if (typeof result === 'string')
|
|
377
|
+
return { text: result };
|
|
378
|
+
// Formatted status response
|
|
379
|
+
if (action === 'status') {
|
|
380
|
+
const status = result;
|
|
381
|
+
return { text: `🛰️ **Overlay Status**\n\n**Identity Key**: \`${status.identity?.identityKey || 'Not setup'}\`\n**Balance**: ${status.balance?.walletBalance || 0} satoshis\n**Network**: ${status.identity?.network || 'mainnet'}` };
|
|
382
|
+
}
|
|
383
|
+
return { text: `**Overlay ${action.toUpperCase()}**\n\n${JSON.stringify(result, null, 2)}` };
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
return { text: `❌ Error: ${error.message}` };
|
|
364
387
|
}
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
// 3. Service
|
|
391
|
+
api.registerService({
|
|
392
|
+
id: "overlay-relay",
|
|
393
|
+
start: async () => {
|
|
394
|
+
const env = buildEnvironment(pluginConfig);
|
|
395
|
+
const cliPath = getCliPath();
|
|
396
|
+
startBackgroundService(env, cliPath, api.logger);
|
|
397
|
+
startAutoImport(env, cliPath, api.logger);
|
|
398
|
+
},
|
|
399
|
+
stop: () => stopBackgroundService()
|
|
400
|
+
});
|
|
401
|
+
// 4. CLI
|
|
402
|
+
api.registerCli(({ program }) => {
|
|
403
|
+
const overlay = program.command("overlay").description("BSV Overlay Network management");
|
|
404
|
+
overlay.command("status").action(async () => {
|
|
405
|
+
await ensureCp();
|
|
406
|
+
const result = await handleStatus(buildEnvironment(pluginConfig), getCliPath());
|
|
407
|
+
console.log(JSON.stringify(result, null, 2));
|
|
365
408
|
});
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const env = buildEnvironment(pluginConfig);
|
|
371
|
-
const cliPath = getCliPath();
|
|
372
|
-
startBackgroundService(env, cliPath, api.logger);
|
|
373
|
-
startAutoImport(env, cliPath, api.logger);
|
|
374
|
-
},
|
|
375
|
-
stop: () => stopBackgroundService()
|
|
409
|
+
overlay.command("balance").action(async () => {
|
|
410
|
+
await ensureCp();
|
|
411
|
+
const result = await handleBalance(buildEnvironment(pluginConfig), getCliPath());
|
|
412
|
+
console.log(JSON.stringify(result, null, 2));
|
|
376
413
|
});
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
overlay.command("balance").action(async () => {
|
|
386
|
-
await ensureCp();
|
|
387
|
-
const result = await handleBalance(buildEnvironment(pluginConfig), getCliPath());
|
|
388
|
-
console.log(JSON.stringify(result, null, 2));
|
|
389
|
-
});
|
|
390
|
-
}, { descriptors: [{ name: "overlay", description: "BSV Overlay Network management" }] });
|
|
391
|
-
}
|
|
414
|
+
}, { descriptors: [{ name: "overlay", description: "BSV Overlay Network management" }] });
|
|
415
|
+
}
|
|
416
|
+
export const plugin = {
|
|
417
|
+
id: "openclaw-overlay-plugin",
|
|
418
|
+
name: "BSV Overlay Network",
|
|
419
|
+
description: "OpenClaw Overlay — decentralized agent marketplace with BSV micropayments",
|
|
420
|
+
activate: register,
|
|
421
|
+
register: register
|
|
392
422
|
};
|
|
393
|
-
export default
|
|
423
|
+
export default register;
|
|
394
424
|
async function executeOverlayAction(params, config, api) {
|
|
395
425
|
await ensureCp();
|
|
396
426
|
const { action } = params;
|
|
@@ -404,9 +434,21 @@ async function executeOverlayAction(params, config, api) {
|
|
|
404
434
|
case "onboard": return await handleOnboard(params, env, cliPath);
|
|
405
435
|
case "pending-requests": return await handlePendingRequests(env, cliPath);
|
|
406
436
|
case "fulfill": return await handleFulfill(params, env, cliPath);
|
|
437
|
+
case "advertise-ship": return await handleAdvertiseSHIP(params, env, cliPath);
|
|
438
|
+
case "advertise-slap": return await handleAdvertiseSLAP(params, env, cliPath);
|
|
407
439
|
default: throw new Error(`Unknown action: ${action}`);
|
|
408
440
|
}
|
|
409
441
|
}
|
|
442
|
+
async function handleAdvertiseSHIP(params, env, cliPath) {
|
|
443
|
+
const { domain, topic } = params;
|
|
444
|
+
const result = await execFileAsync('node', [cliPath, 'advertise-ship', domain, topic], { env });
|
|
445
|
+
return parseCliOutput(result.stdout).data;
|
|
446
|
+
}
|
|
447
|
+
async function handleAdvertiseSLAP(params, env, cliPath) {
|
|
448
|
+
const { domain, service } = params;
|
|
449
|
+
const result = await execFileAsync('node', [cliPath, 'advertise-slap', domain, service], { env });
|
|
450
|
+
return parseCliOutput(result.stdout).data;
|
|
451
|
+
}
|
|
410
452
|
async function handleServiceRequest(params, env, cliPath, config, api) {
|
|
411
453
|
const { service, identityKey: targetKey, input } = params;
|
|
412
454
|
const walletDir = config?.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
|
|
@@ -467,7 +509,7 @@ function buildEnvironment(config) {
|
|
|
467
509
|
const env = { ...process['env'] };
|
|
468
510
|
env.BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
|
|
469
511
|
env.OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';
|
|
470
|
-
env.BSV_NETWORK = env.BSV_NETWORK || 'mainnet';
|
|
512
|
+
env.BSV_NETWORK = config.network || env.BSV_NETWORK || 'mainnet';
|
|
471
513
|
env.BSV_ARC_URL = config.arcUrl || (env.BSV_NETWORK === 'testnet' ? 'https://testnet.arc.gorillapool.io' : 'https://arc.gorillapool.io');
|
|
472
514
|
env.AGENT_NAME = config.agentName || 'openclaw-agent';
|
|
473
515
|
return env;
|
package/dist/src/cli-main.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { ok, fail } from './scripts/output.js';
|
|
8
8
|
// Wallet commands
|
|
9
|
-
import { cmdSetup, cmdIdentity, cmdAddress } from './scripts/wallet/setup.js';
|
|
9
|
+
import { cmdSetup, cmdIdentity, cmdAddress, cmdStatus } from './scripts/wallet/setup.js';
|
|
10
10
|
import { cmdBalance, cmdImport, cmdRefund } from './scripts/wallet/balance.js';
|
|
11
11
|
// Overlay registration commands
|
|
12
12
|
import { cmdRegister, cmdUnregister } from './scripts/overlay/registration.js';
|
|
@@ -14,6 +14,7 @@ import { cmdRegister, cmdUnregister } from './scripts/overlay/registration.js';
|
|
|
14
14
|
import { cmdServices, cmdAdvertise, cmdRemove, cmdReadvertise } from './scripts/overlay/services.js';
|
|
15
15
|
// Discovery commands
|
|
16
16
|
import { cmdDiscover } from './scripts/overlay/discover.js';
|
|
17
|
+
import { cmdAdvertiseSHIP, cmdAdvertiseSLAP } from './scripts/overlay/advertisement.js';
|
|
17
18
|
// Payment commands
|
|
18
19
|
import { cmdPay, cmdVerify, cmdAccept } from './scripts/payment/commands.js';
|
|
19
20
|
// Messaging commands
|
|
@@ -43,7 +44,7 @@ async function main() {
|
|
|
43
44
|
wallet: ['setup', 'identity', 'address', 'balance', 'import <txid> [vout]', 'refund <address>'],
|
|
44
45
|
registration: ['register', 'unregister'],
|
|
45
46
|
services: ['services', 'advertise <id> <name> <priceSats> [desc]', 'readvertise <id> [name] [priceSats] [desc]', 'remove <id>'],
|
|
46
|
-
discovery: ['discover [--service <type>] [--agent <name>]'],
|
|
47
|
+
discovery: ['discover [--service <type>] [--agent <name>]', 'advertise-ship <domain> <topic>', 'advertise-slap <domain> <service>'],
|
|
47
48
|
payments: ['pay <pubkey> <sats> [desc]', 'verify <beef>', 'accept <beef> <prefix> <suffix> <senderKey> [desc]'],
|
|
48
49
|
messaging: ['send <key> <type> <json>', 'inbox', 'ack', 'poll', 'connect'],
|
|
49
50
|
'service-requests': ['request-service <key> <serviceId> <sats> [input]', 'service-queue', 'respond-service <reqId> <key> <serviceId> <result>'],
|
|
@@ -54,6 +55,9 @@ async function main() {
|
|
|
54
55
|
});
|
|
55
56
|
break;
|
|
56
57
|
// Wallet
|
|
58
|
+
case 'status':
|
|
59
|
+
await cmdStatus();
|
|
60
|
+
break;
|
|
57
61
|
case 'setup':
|
|
58
62
|
await cmdSetup();
|
|
59
63
|
break;
|
|
@@ -96,6 +100,12 @@ async function main() {
|
|
|
96
100
|
case 'discover':
|
|
97
101
|
await cmdDiscover(args);
|
|
98
102
|
break;
|
|
103
|
+
case 'advertise-ship':
|
|
104
|
+
await cmdAdvertiseSHIP(args[0], args[1]);
|
|
105
|
+
break;
|
|
106
|
+
case 'advertise-slap':
|
|
107
|
+
await cmdAdvertiseSLAP(args[0], args[1]);
|
|
108
|
+
break;
|
|
99
109
|
// Payments
|
|
100
110
|
case 'pay':
|
|
101
111
|
await cmdPay(args[0], args[1], args.slice(2).join(' ') || undefined);
|
package/dist/src/core/config.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* @a2a-bsv/core — Configuration defaults and helpers.
|
|
3
3
|
*/
|
|
4
4
|
export function toChain(network) {
|
|
5
|
-
|
|
5
|
+
if (network === 'testnet')
|
|
6
|
+
return 'test';
|
|
7
|
+
return 'main';
|
|
6
8
|
}
|
|
7
9
|
/** Default TAAL API keys from the wallet-toolbox examples. */
|
|
8
10
|
export const DEFAULT_TAAL_API_KEYS = {
|
package/dist/src/core/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
/** Wallet configuration for creating or loading an agent wallet. */
|
|
5
5
|
export interface WalletConfig {
|
|
6
6
|
/** BSV network to use. */
|
|
7
|
-
network: 'mainnet' | 'testnet';
|
|
7
|
+
network: 'mainnet' | 'testnet' | 'local';
|
|
8
8
|
/** Directory path for SQLite wallet persistence. */
|
|
9
9
|
storageDir: string;
|
|
10
10
|
/** Optional: pre-existing root private key hex. If omitted on create(), a new one is generated. */
|
|
@@ -90,5 +90,5 @@ export interface WalletIdentity {
|
|
|
90
90
|
/** The wallet's public identity key (compressed hex). */
|
|
91
91
|
identityKey: string;
|
|
92
92
|
/** Network this wallet targets. */
|
|
93
|
-
network: 'mainnet' | 'testnet';
|
|
93
|
+
network: 'mainnet' | 'testnet' | 'local';
|
|
94
94
|
}
|
|
@@ -49,6 +49,10 @@ export declare class BSVAgentWallet {
|
|
|
49
49
|
* This is the key other agents use to send payments to you.
|
|
50
50
|
*/
|
|
51
51
|
getIdentityKey(): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Get the wallet's current receive address for the active network.
|
|
54
|
+
*/
|
|
55
|
+
getAddress(): Promise<string>;
|
|
52
56
|
/**
|
|
53
57
|
* Get the wallet's current balance in satoshis.
|
|
54
58
|
*
|
package/dist/src/core/wallet.js
CHANGED
|
@@ -95,6 +95,13 @@ export class BSVAgentWallet {
|
|
|
95
95
|
async getIdentityKey() {
|
|
96
96
|
return this._setup.identityKey;
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Get the wallet's current receive address for the active network.
|
|
100
|
+
*/
|
|
101
|
+
async getAddress() {
|
|
102
|
+
const network = this._setup.network || 'mainnet';
|
|
103
|
+
return this._setup.rootKey.toPublicKey().toAddress(network);
|
|
104
|
+
}
|
|
98
105
|
/**
|
|
99
106
|
* Get the wallet's current balance in satoshis.
|
|
100
107
|
*
|
|
@@ -233,6 +240,7 @@ export class BSVAgentWallet {
|
|
|
233
240
|
identityKey,
|
|
234
241
|
keyDeriver,
|
|
235
242
|
chain,
|
|
243
|
+
network: config.network,
|
|
236
244
|
storage,
|
|
237
245
|
services: isTestMode ? undefined : services,
|
|
238
246
|
monitor: isTestMode ? undefined : monitor,
|
|
@@ -22,7 +22,11 @@ export declare const TOPICS: {
|
|
|
22
22
|
readonly IDENTITY: "tm_openclaw_identity";
|
|
23
23
|
readonly SERVICES: "tm_openclaw_services";
|
|
24
24
|
readonly X_VERIFICATION: "tm_openclaw_x_verification";
|
|
25
|
+
readonly SHIP: "tm_ship";
|
|
26
|
+
readonly SLAP: "tm_slap";
|
|
25
27
|
};
|
|
28
|
+
/** Default SLAP trackers */
|
|
29
|
+
export declare const DEFAULT_SLAP_TRACKERS: Record<'mainnet' | 'testnet', string[]>;
|
|
26
30
|
/** Lookup services for overlay queries */
|
|
27
31
|
export declare const LOOKUP_SERVICES: {
|
|
28
32
|
readonly AGENTS: "ls_openclaw_agents";
|
|
@@ -42,6 +42,13 @@ export const TOPICS = {
|
|
|
42
42
|
IDENTITY: 'tm_openclaw_identity',
|
|
43
43
|
SERVICES: 'tm_openclaw_services',
|
|
44
44
|
X_VERIFICATION: 'tm_openclaw_x_verification',
|
|
45
|
+
SHIP: 'tm_ship',
|
|
46
|
+
SLAP: 'tm_slap',
|
|
47
|
+
};
|
|
48
|
+
/** Default SLAP trackers */
|
|
49
|
+
export const DEFAULT_SLAP_TRACKERS = {
|
|
50
|
+
mainnet: ['https://overlay.babbage.systems'],
|
|
51
|
+
testnet: ['https://testnet-users.bapp.dev'],
|
|
45
52
|
};
|
|
46
53
|
/** Lookup services for overlay queries */
|
|
47
54
|
export const LOOKUP_SERVICES = {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHIP and SLAP advertisement commands.
|
|
3
|
+
*
|
|
4
|
+
* SHIP: Service Health & Information Protocol (tm_ship)
|
|
5
|
+
* SLAP: Service Level Agreement Protocol (tm_slap)
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Advertise a SHIP record.
|
|
9
|
+
* Announce that you host a specific Topic Manager (tm_).
|
|
10
|
+
*/
|
|
11
|
+
export declare function cmdAdvertiseSHIP(domain?: string, topic?: string): Promise<never>;
|
|
12
|
+
/**
|
|
13
|
+
* Advertise a SLAP record.
|
|
14
|
+
* Announce that you host a specific Lookup Service (ls_).
|
|
15
|
+
*/
|
|
16
|
+
export declare function cmdAdvertiseSLAP(domain?: string, service?: string): Promise<never>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHIP and SLAP advertisement commands.
|
|
3
|
+
*
|
|
4
|
+
* SHIP: Service Health & Information Protocol (tm_ship)
|
|
5
|
+
* SLAP: Service Level Agreement Protocol (tm_slap)
|
|
6
|
+
*/
|
|
7
|
+
import { PushDrop, Utils } from '@bsv/sdk';
|
|
8
|
+
import { NETWORK, WALLET_DIR, TOPICS, DEFAULT_SLAP_TRACKERS } from '../config.js';
|
|
9
|
+
import { BSVAgentWallet } from '../../core/wallet.js';
|
|
10
|
+
import { ok, fail } from '../output.js';
|
|
11
|
+
/**
|
|
12
|
+
* Advertise a SHIP record.
|
|
13
|
+
* Announce that you host a specific Topic Manager (tm_).
|
|
14
|
+
*/
|
|
15
|
+
export async function cmdAdvertiseSHIP(domain, topic) {
|
|
16
|
+
if (!domain || !topic) {
|
|
17
|
+
return fail('Usage: advertise-ship <domain> <topic>');
|
|
18
|
+
}
|
|
19
|
+
if (!topic.startsWith('tm_')) {
|
|
20
|
+
return fail('Topic must start with "tm_"');
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
|
|
24
|
+
const token = new PushDrop(wallet._setup.wallet);
|
|
25
|
+
// SHIP format: Payload is the domain hosting the topic
|
|
26
|
+
const fields = [Utils.toArray(domain, 'utf8')];
|
|
27
|
+
// Context is [0, topic] to identify the topic manager being advertised
|
|
28
|
+
const lockingScript = (await token.lock(fields, [0, topic], '1', 'self', true, true)).toHex();
|
|
29
|
+
const response = await wallet._setup.wallet.createAction({
|
|
30
|
+
description: `advertise SHIP for ${topic}`,
|
|
31
|
+
outputs: [{
|
|
32
|
+
lockingScript,
|
|
33
|
+
satoshis: 1,
|
|
34
|
+
outputDescription: 'SHIP advertisement',
|
|
35
|
+
basket: TOPICS.SHIP
|
|
36
|
+
}]
|
|
37
|
+
});
|
|
38
|
+
// Broadcast to primary overlay and SLAP trackers
|
|
39
|
+
const trackers = [
|
|
40
|
+
...DEFAULT_SLAP_TRACKERS[NETWORK]
|
|
41
|
+
];
|
|
42
|
+
const results = await broadcastToTrackers(response.tx, [TOPICS.SHIP, topic], trackers);
|
|
43
|
+
return ok({
|
|
44
|
+
advertised: 'SHIP',
|
|
45
|
+
topic,
|
|
46
|
+
domain,
|
|
47
|
+
txid: response.txid,
|
|
48
|
+
broadcasts: results
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
return fail(`SHIP advertisement failed: ${err.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Advertise a SLAP record.
|
|
57
|
+
* Announce that you host a specific Lookup Service (ls_).
|
|
58
|
+
*/
|
|
59
|
+
export async function cmdAdvertiseSLAP(domain, service) {
|
|
60
|
+
if (!domain || !service) {
|
|
61
|
+
return fail('Usage: advertise-slap <domain> <service>');
|
|
62
|
+
}
|
|
63
|
+
if (!service.startsWith('ls_')) {
|
|
64
|
+
return fail('Service must start with "ls_"');
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
|
|
68
|
+
const token = new PushDrop(wallet._setup.wallet);
|
|
69
|
+
// SLAP format: Payload is the domain hosting the lookup service
|
|
70
|
+
const fields = [Utils.toArray(domain, 'utf8')];
|
|
71
|
+
// Context is [0, service] to identify the lookup service being advertised
|
|
72
|
+
const lockingScript = (await token.lock(fields, [0, service], '1', 'self', true, true)).toHex();
|
|
73
|
+
const response = await wallet._setup.wallet.createAction({
|
|
74
|
+
description: `advertise SLAP for ${service}`,
|
|
75
|
+
outputs: [{
|
|
76
|
+
lockingScript,
|
|
77
|
+
satoshis: 1,
|
|
78
|
+
outputDescription: 'SLAP advertisement',
|
|
79
|
+
basket: TOPICS.SLAP
|
|
80
|
+
}]
|
|
81
|
+
});
|
|
82
|
+
// Broadcast to primary overlay and SLAP trackers
|
|
83
|
+
const trackers = [
|
|
84
|
+
...DEFAULT_SLAP_TRACKERS[NETWORK]
|
|
85
|
+
];
|
|
86
|
+
const results = await broadcastToTrackers(response.tx, [TOPICS.SLAP, service], trackers);
|
|
87
|
+
return ok({
|
|
88
|
+
advertised: 'SLAP',
|
|
89
|
+
service,
|
|
90
|
+
domain,
|
|
91
|
+
txid: response.txid,
|
|
92
|
+
broadcasts: results
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
return fail(`SLAP advertisement failed: ${err.message}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Helper to broadcast BEEF to multiple trackers/overlays.
|
|
101
|
+
*/
|
|
102
|
+
async function broadcastToTrackers(tx, topics, trackers) {
|
|
103
|
+
const results = {};
|
|
104
|
+
const body = new Uint8Array(tx);
|
|
105
|
+
for (const url of trackers) {
|
|
106
|
+
try {
|
|
107
|
+
const resp = await fetch(`${url.replace(/\/$/, '')}/submit`, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: {
|
|
110
|
+
'Content-Type': 'application/octet-stream',
|
|
111
|
+
'X-Topics': JSON.stringify(topics)
|
|
112
|
+
},
|
|
113
|
+
body
|
|
114
|
+
});
|
|
115
|
+
results[url] = resp.ok ? 'success' : `error: ${resp.status}`;
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
results[url] = `failed: ${err.message}`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return results;
|
|
122
|
+
}
|
|
@@ -9,6 +9,10 @@ export declare function cmdSetup(): Promise<never>;
|
|
|
9
9
|
* Identity command: show identity public key.
|
|
10
10
|
*/
|
|
11
11
|
export declare function cmdIdentity(): Promise<never>;
|
|
12
|
+
/**
|
|
13
|
+
* Status command: show identity and balance.
|
|
14
|
+
*/
|
|
15
|
+
export declare function cmdStatus(): Promise<never>;
|
|
12
16
|
/**
|
|
13
17
|
* Address command: show P2PKH receive address.
|
|
14
18
|
*/
|
|
@@ -83,6 +83,20 @@ export async function cmdIdentity() {
|
|
|
83
83
|
await wallet.destroy();
|
|
84
84
|
return ok({ identityKey });
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Status command: show identity and balance.
|
|
88
|
+
*/
|
|
89
|
+
export async function cmdStatus() {
|
|
90
|
+
const BSVAgentWallet = await getBSVAgentWallet();
|
|
91
|
+
const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
|
|
92
|
+
const identityKey = await wallet.getIdentityKey();
|
|
93
|
+
const total = await wallet.getBalance();
|
|
94
|
+
await wallet.destroy();
|
|
95
|
+
return ok({
|
|
96
|
+
identity: { identityKey, network: NETWORK },
|
|
97
|
+
balance: { walletBalance: total }
|
|
98
|
+
});
|
|
99
|
+
}
|
|
86
100
|
/**
|
|
87
101
|
* Address command: show P2PKH receive address.
|
|
88
102
|
*/
|
package/index.ts
CHANGED
|
@@ -313,32 +313,35 @@ function getCliPath() {
|
|
|
313
313
|
* OpenClaw Overlay Plugin
|
|
314
314
|
* Decentralized agent marketplace with BSV micropayments.
|
|
315
315
|
*/
|
|
316
|
-
export
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
description: "OpenClaw Overlay — decentralized agent marketplace with BSV micropayments",
|
|
320
|
-
|
|
321
|
-
async activate(api: any) {
|
|
322
|
-
return this.register(api);
|
|
323
|
-
},
|
|
316
|
+
export async function register(api: any) {
|
|
317
|
+
if (isInitialized) return;
|
|
318
|
+
isInitialized = true;
|
|
324
319
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
isInitialized = true;
|
|
320
|
+
// Initialize child process helpers
|
|
321
|
+
await ensureCp();
|
|
328
322
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
323
|
+
const entries = api.getConfig?.()?.plugins?.entries || {};
|
|
324
|
+
const entry = entries['overlay'] || entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};
|
|
325
|
+
const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };
|
|
332
326
|
|
|
333
|
-
|
|
334
|
-
|
|
327
|
+
// Initialize service system
|
|
328
|
+
try {
|
|
329
|
+
await initializeServiceSystem();
|
|
330
|
+
} catch (err: any) {
|
|
331
|
+
if (api.logger) api.logger.warn(`[overlay] Service system initialization failed: ${err.message}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 1. Tool
|
|
335
|
+
api.registerTool({
|
|
335
336
|
name: "overlay",
|
|
336
337
|
description: "Access the BSV agent marketplace",
|
|
337
338
|
parameters: {
|
|
338
339
|
type: "object",
|
|
339
340
|
properties: {
|
|
340
|
-
action: { type: "string", enum: ["request", "discover", "balance", "status", "pay", "onboard", "pending-requests", "fulfill", "unregister"] },
|
|
341
|
+
action: { type: "string", enum: ["request", "discover", "balance", "status", "pay", "onboard", "pending-requests", "fulfill", "unregister", "advertise-ship", "advertise-slap"] },
|
|
341
342
|
service: { type: "string" },
|
|
343
|
+
topic: { type: "string" },
|
|
344
|
+
domain: { type: "string" },
|
|
342
345
|
input: { type: "object" },
|
|
343
346
|
identityKey: { type: "string" },
|
|
344
347
|
sats: { type: "number" },
|
|
@@ -369,11 +372,31 @@ export const plugin = {
|
|
|
369
372
|
const action = ctx.args?.[0] || 'status';
|
|
370
373
|
|
|
371
374
|
if (action === 'help') {
|
|
372
|
-
return { text: `🛰️ **Overlay Help**\n\n**Subcommands**:\n- \`status\`: Show identity and wallet balance\n- \`balance\`: Show current satoshis\n- \`onboard\`: Start discovery setup\n- \`discover <serviceId>\`: Find providers on network\n- \`pending-requests\`: See incoming service jobs\n- \`fulfill\`: Complete a request (Agent only)` };
|
|
375
|
+
return { text: `🛰️ **Overlay Help**\n\n**Subcommands**:\n- \`status\`: Show identity and wallet balance\n- \`balance\`: Show current satoshis\n- \`onboard\`: Start discovery setup\n- \`discover <serviceId>\`: Find providers on network\n- \`advertise-ship <domain> <topic>\`: Advertise a topic manager\n- \`advertise-slap <domain> <service>\`: Advertise a lookup service\n- \`pending-requests\`: See incoming service jobs\n- \`fulfill\`: Complete a request (Agent only)` };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const params: any = { action };
|
|
379
|
+
if (action === 'discover') {
|
|
380
|
+
params.service = ctx.args[1];
|
|
381
|
+
} else if (action === 'advertise-ship') {
|
|
382
|
+
params.domain = ctx.args[1];
|
|
383
|
+
params.topic = ctx.args[2];
|
|
384
|
+
} else if (action === 'advertise-slap') {
|
|
385
|
+
params.domain = ctx.args[1];
|
|
386
|
+
params.service = ctx.args[2];
|
|
373
387
|
}
|
|
374
388
|
|
|
375
|
-
const result = await executeOverlayAction(
|
|
376
|
-
|
|
389
|
+
const result = await executeOverlayAction(params, pluginConfig, api);
|
|
390
|
+
|
|
391
|
+
if (typeof result === 'string') return { text: result };
|
|
392
|
+
|
|
393
|
+
// Formatted status response
|
|
394
|
+
if (action === 'status') {
|
|
395
|
+
const status = result as any;
|
|
396
|
+
return { text: `🛰️ **Overlay Status**\n\n**Identity Key**: \`${status.identity?.identityKey || 'Not setup'}\`\n**Balance**: ${status.balance?.walletBalance || 0} satoshis\n**Network**: ${status.identity?.network || 'mainnet'}` };
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return { text: `**Overlay ${action.toUpperCase()}**\n\n${JSON.stringify(result, null, 2)}` };
|
|
377
400
|
} catch (error: any) {
|
|
378
401
|
return { text: `❌ Error: ${error.message}` };
|
|
379
402
|
}
|
|
@@ -382,7 +405,7 @@ export const plugin = {
|
|
|
382
405
|
|
|
383
406
|
// 3. Service
|
|
384
407
|
api.registerService({
|
|
385
|
-
id: "
|
|
408
|
+
id: "overlay-relay",
|
|
386
409
|
start: async () => {
|
|
387
410
|
const env = buildEnvironment(pluginConfig);
|
|
388
411
|
const cliPath = getCliPath();
|
|
@@ -406,10 +429,17 @@ export const plugin = {
|
|
|
406
429
|
console.log(JSON.stringify(result, null, 2));
|
|
407
430
|
});
|
|
408
431
|
}, { descriptors: [{ name: "overlay", description: "BSV Overlay Network management" }] });
|
|
409
|
-
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export const plugin = {
|
|
435
|
+
id: "openclaw-overlay-plugin",
|
|
436
|
+
name: "BSV Overlay Network",
|
|
437
|
+
description: "OpenClaw Overlay — decentralized agent marketplace with BSV micropayments",
|
|
438
|
+
activate: register,
|
|
439
|
+
register: register
|
|
410
440
|
};
|
|
411
441
|
|
|
412
|
-
export default
|
|
442
|
+
export default register;
|
|
413
443
|
|
|
414
444
|
async function executeOverlayAction(params: any, config: any, api: any) {
|
|
415
445
|
await ensureCp();
|
|
@@ -425,10 +455,24 @@ async function executeOverlayAction(params: any, config: any, api: any) {
|
|
|
425
455
|
case "onboard": return await handleOnboard(params, env, cliPath);
|
|
426
456
|
case "pending-requests": return await handlePendingRequests(env, cliPath);
|
|
427
457
|
case "fulfill": return await handleFulfill(params, env, cliPath);
|
|
458
|
+
case "advertise-ship": return await handleAdvertiseSHIP(params, env, cliPath);
|
|
459
|
+
case "advertise-slap": return await handleAdvertiseSLAP(params, env, cliPath);
|
|
428
460
|
default: throw new Error(`Unknown action: ${action}`);
|
|
429
461
|
}
|
|
430
462
|
}
|
|
431
463
|
|
|
464
|
+
async function handleAdvertiseSHIP(params: any, env: any, cliPath: string) {
|
|
465
|
+
const { domain, topic } = params;
|
|
466
|
+
const result = await execFileAsync('node', [cliPath, 'advertise-ship', domain, topic], { env });
|
|
467
|
+
return parseCliOutput(result.stdout).data;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async function handleAdvertiseSLAP(params: any, env: any, cliPath: string) {
|
|
471
|
+
const { domain, service } = params;
|
|
472
|
+
const result = await execFileAsync('node', [cliPath, 'advertise-slap', domain, service], { env });
|
|
473
|
+
return parseCliOutput(result.stdout).data;
|
|
474
|
+
}
|
|
475
|
+
|
|
432
476
|
async function handleServiceRequest(params: any, env: any, cliPath: string, config: any, api: any) {
|
|
433
477
|
const { service, identityKey: targetKey, input } = params;
|
|
434
478
|
const walletDir = config?.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
|
|
@@ -491,7 +535,7 @@ function buildEnvironment(config: any) {
|
|
|
491
535
|
const env = { ...(process as any)['env'] };
|
|
492
536
|
env.BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
|
|
493
537
|
env.OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';
|
|
494
|
-
env.BSV_NETWORK = env.BSV_NETWORK || 'mainnet';
|
|
538
|
+
env.BSV_NETWORK = config.network || env.BSV_NETWORK || 'mainnet';
|
|
495
539
|
env.BSV_ARC_URL = config.arcUrl || (env.BSV_NETWORK === 'testnet' ? 'https://testnet.arc.gorillapool.io' : 'https://arc.gorillapool.io');
|
|
496
540
|
env.AGENT_NAME = config.agentName || 'openclaw-agent';
|
|
497
541
|
return env;
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"id": "openclaw-overlay-plugin",
|
|
3
3
|
"name": "BSV Overlay Network",
|
|
4
4
|
"description": "OpenClaw Overlay — decentralized agent marketplace with BSV micropayments",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.72",
|
|
6
6
|
"skills": [
|
|
7
7
|
"./SKILL.md"
|
|
8
8
|
],
|
|
9
9
|
"providerAuthEnvVars": {
|
|
10
10
|
"OVERLAY_URL": "Overlay server URL (defaults to https://clawoverlay.com)",
|
|
11
|
-
"BSV_NETWORK": "BSV network to use (mainnet/testnet)",
|
|
11
|
+
"BSV_NETWORK": "BSV network to use (mainnet/testnet/local)",
|
|
12
12
|
"BSV_WALLET_DIR": "Path to the shared BSV wallet directory"
|
|
13
13
|
},
|
|
14
14
|
"providerAuthChoices": [
|
|
@@ -18,13 +18,6 @@
|
|
|
18
18
|
"description": "Uses the default shared agent identity key"
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
|
-
"commands": [
|
|
22
|
-
{
|
|
23
|
-
"name": "overlay",
|
|
24
|
-
"description": "BSV Overlay Network management",
|
|
25
|
-
"isAutoreply": true
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
21
|
"contracts": {
|
|
29
22
|
"tools": [
|
|
30
23
|
{
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-overlay-plugin",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.72",
|
|
4
4
|
"description": "Openclaw BSV Overlay — agent discovery, service marketplace, and micropayments on the BSV blockchain",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
+
"main": "dist/index.js",
|
|
9
10
|
"files": [
|
|
10
11
|
"index.ts",
|
|
11
12
|
"openclaw.plugin.json",
|
package/src/cli-main.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { ok, fail } from './scripts/output.js';
|
|
9
9
|
|
|
10
10
|
// Wallet commands
|
|
11
|
-
import { cmdSetup, cmdIdentity, cmdAddress } from './scripts/wallet/setup.js';
|
|
11
|
+
import { cmdSetup, cmdIdentity, cmdAddress, cmdStatus } from './scripts/wallet/setup.js';
|
|
12
12
|
import { cmdBalance, cmdImport, cmdRefund } from './scripts/wallet/balance.js';
|
|
13
13
|
|
|
14
14
|
// Overlay registration commands
|
|
@@ -19,6 +19,7 @@ import { cmdServices, cmdAdvertise, cmdRemove, cmdReadvertise } from './scripts/
|
|
|
19
19
|
|
|
20
20
|
// Discovery commands
|
|
21
21
|
import { cmdDiscover } from './scripts/overlay/discover.js';
|
|
22
|
+
import { cmdAdvertiseSHIP, cmdAdvertiseSLAP } from './scripts/overlay/advertisement.js';
|
|
22
23
|
|
|
23
24
|
// Payment commands
|
|
24
25
|
import { cmdPay, cmdVerify, cmdAccept } from './scripts/payment/commands.js';
|
|
@@ -69,7 +70,7 @@ async function main() {
|
|
|
69
70
|
wallet: ['setup', 'identity', 'address', 'balance', 'import <txid> [vout]', 'refund <address>'],
|
|
70
71
|
registration: ['register', 'unregister'],
|
|
71
72
|
services: ['services', 'advertise <id> <name> <priceSats> [desc]', 'readvertise <id> [name] [priceSats] [desc]', 'remove <id>'],
|
|
72
|
-
discovery: ['discover [--service <type>] [--agent <name>]'],
|
|
73
|
+
discovery: ['discover [--service <type>] [--agent <name>]', 'advertise-ship <domain> <topic>', 'advertise-slap <domain> <service>'],
|
|
73
74
|
payments: ['pay <pubkey> <sats> [desc]', 'verify <beef>', 'accept <beef> <prefix> <suffix> <senderKey> [desc]'],
|
|
74
75
|
messaging: ['send <key> <type> <json>', 'inbox', 'ack', 'poll', 'connect'],
|
|
75
76
|
'service-requests': ['request-service <key> <serviceId> <sats> [input]', 'service-queue', 'respond-service <reqId> <key> <serviceId> <result>'],
|
|
@@ -81,6 +82,9 @@ async function main() {
|
|
|
81
82
|
break;
|
|
82
83
|
|
|
83
84
|
// Wallet
|
|
85
|
+
case 'status':
|
|
86
|
+
await cmdStatus();
|
|
87
|
+
break;
|
|
84
88
|
case 'setup':
|
|
85
89
|
await cmdSetup();
|
|
86
90
|
break;
|
|
@@ -126,6 +130,12 @@ async function main() {
|
|
|
126
130
|
case 'discover':
|
|
127
131
|
await cmdDiscover(args);
|
|
128
132
|
break;
|
|
133
|
+
case 'advertise-ship':
|
|
134
|
+
await cmdAdvertiseSHIP(args[0], args[1]);
|
|
135
|
+
break;
|
|
136
|
+
case 'advertise-slap':
|
|
137
|
+
await cmdAdvertiseSLAP(args[0], args[1]);
|
|
138
|
+
break;
|
|
129
139
|
|
|
130
140
|
// Payments
|
|
131
141
|
case 'pay':
|
package/src/core/config.ts
CHANGED
|
@@ -8,7 +8,8 @@ import type { WalletConfig } from './types.js';
|
|
|
8
8
|
export type Chain = 'main' | 'test';
|
|
9
9
|
|
|
10
10
|
export function toChain(network: WalletConfig['network']): Chain {
|
|
11
|
-
|
|
11
|
+
if (network === 'testnet') return 'test';
|
|
12
|
+
return 'main';
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
/** Default TAAL API keys from the wallet-toolbox examples. */
|
package/src/core/types.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/** Wallet configuration for creating or loading an agent wallet. */
|
|
6
6
|
export interface WalletConfig {
|
|
7
7
|
/** BSV network to use. */
|
|
8
|
-
network: 'mainnet' | 'testnet';
|
|
8
|
+
network: 'mainnet' | 'testnet' | 'local';
|
|
9
9
|
/** Directory path for SQLite wallet persistence. */
|
|
10
10
|
storageDir: string;
|
|
11
11
|
/** Optional: pre-existing root private key hex. If omitted on create(), a new one is generated. */
|
|
@@ -98,5 +98,5 @@ export interface WalletIdentity {
|
|
|
98
98
|
/** The wallet's public identity key (compressed hex). */
|
|
99
99
|
identityKey: string;
|
|
100
100
|
/** Network this wallet targets. */
|
|
101
|
-
network: 'mainnet' | 'testnet';
|
|
101
|
+
network: 'mainnet' | 'testnet' | 'local';
|
|
102
102
|
}
|
package/src/core/wallet.ts
CHANGED
|
@@ -134,6 +134,14 @@ export class BSVAgentWallet {
|
|
|
134
134
|
return this._setup.identityKey;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Get the wallet's current receive address for the active network.
|
|
139
|
+
*/
|
|
140
|
+
async getAddress(): Promise<string> {
|
|
141
|
+
const network = (this._setup as any).network || 'mainnet';
|
|
142
|
+
return this._setup.rootKey.toPublicKey().toAddress(network);
|
|
143
|
+
}
|
|
144
|
+
|
|
137
145
|
/**
|
|
138
146
|
* Get the wallet's current balance in satoshis.
|
|
139
147
|
*
|
|
@@ -298,6 +306,7 @@ export class BSVAgentWallet {
|
|
|
298
306
|
identityKey,
|
|
299
307
|
keyDeriver,
|
|
300
308
|
chain,
|
|
309
|
+
network: config.network,
|
|
301
310
|
storage,
|
|
302
311
|
services: isTestMode ? undefined : services,
|
|
303
312
|
monitor: isTestMode ? undefined : monitor,
|
package/src/scripts/config.ts
CHANGED
|
@@ -53,8 +53,16 @@ export const TOPICS = {
|
|
|
53
53
|
IDENTITY: 'tm_openclaw_identity',
|
|
54
54
|
SERVICES: 'tm_openclaw_services',
|
|
55
55
|
X_VERIFICATION: 'tm_openclaw_x_verification',
|
|
56
|
+
SHIP: 'tm_ship',
|
|
57
|
+
SLAP: 'tm_slap',
|
|
56
58
|
} as const;
|
|
57
59
|
|
|
60
|
+
/** Default SLAP trackers */
|
|
61
|
+
export const DEFAULT_SLAP_TRACKERS: Record<'mainnet' | 'testnet', string[]> = {
|
|
62
|
+
mainnet: ['https://overlay.babbage.systems'],
|
|
63
|
+
testnet: ['https://testnet-users.bapp.dev'],
|
|
64
|
+
};
|
|
65
|
+
|
|
58
66
|
/** Lookup services for overlay queries */
|
|
59
67
|
export const LOOKUP_SERVICES = {
|
|
60
68
|
AGENTS: 'ls_openclaw_agents',
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHIP and SLAP advertisement commands.
|
|
3
|
+
*
|
|
4
|
+
* SHIP: Service Health & Information Protocol (tm_ship)
|
|
5
|
+
* SLAP: Service Level Agreement Protocol (tm_slap)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { PushDrop, Utils } from '@bsv/sdk';
|
|
9
|
+
import { NETWORK, WALLET_DIR, TOPICS, DEFAULT_SLAP_TRACKERS } from '../config.js';
|
|
10
|
+
import { BSVAgentWallet } from '../../core/wallet.js';
|
|
11
|
+
import { ok, fail } from '../output.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Advertise a SHIP record.
|
|
15
|
+
* Announce that you host a specific Topic Manager (tm_).
|
|
16
|
+
*/
|
|
17
|
+
export async function cmdAdvertiseSHIP(domain?: string, topic?: string): Promise<never> {
|
|
18
|
+
if (!domain || !topic) {
|
|
19
|
+
return fail('Usage: advertise-ship <domain> <topic>');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!topic.startsWith('tm_')) {
|
|
23
|
+
return fail('Topic must start with "tm_"');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
|
|
28
|
+
const token = new PushDrop(wallet._setup.wallet);
|
|
29
|
+
|
|
30
|
+
// SHIP format: Payload is the domain hosting the topic
|
|
31
|
+
const fields = [Utils.toArray(domain, 'utf8')];
|
|
32
|
+
// Context is [0, topic] to identify the topic manager being advertised
|
|
33
|
+
const lockingScript = (await token.lock(fields, [0, topic], '1', 'self', true, true)).toHex();
|
|
34
|
+
|
|
35
|
+
const response = await wallet._setup.wallet.createAction({
|
|
36
|
+
description: `advertise SHIP for ${topic}`,
|
|
37
|
+
outputs: [{
|
|
38
|
+
lockingScript,
|
|
39
|
+
satoshis: 1,
|
|
40
|
+
outputDescription: 'SHIP advertisement',
|
|
41
|
+
basket: TOPICS.SHIP
|
|
42
|
+
}]
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Broadcast to primary overlay and SLAP trackers
|
|
46
|
+
const trackers = [
|
|
47
|
+
...DEFAULT_SLAP_TRACKERS[NETWORK]
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const results = await broadcastToTrackers(response.tx as number[], [TOPICS.SHIP, topic], trackers);
|
|
51
|
+
|
|
52
|
+
return ok({
|
|
53
|
+
advertised: 'SHIP',
|
|
54
|
+
topic,
|
|
55
|
+
domain,
|
|
56
|
+
txid: response.txid,
|
|
57
|
+
broadcasts: results
|
|
58
|
+
});
|
|
59
|
+
} catch (err: any) {
|
|
60
|
+
return fail(`SHIP advertisement failed: ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Advertise a SLAP record.
|
|
66
|
+
* Announce that you host a specific Lookup Service (ls_).
|
|
67
|
+
*/
|
|
68
|
+
export async function cmdAdvertiseSLAP(domain?: string, service?: string): Promise<never> {
|
|
69
|
+
if (!domain || !service) {
|
|
70
|
+
return fail('Usage: advertise-slap <domain> <service>');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!service.startsWith('ls_')) {
|
|
74
|
+
return fail('Service must start with "ls_"');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
|
|
79
|
+
const token = new PushDrop(wallet._setup.wallet);
|
|
80
|
+
|
|
81
|
+
// SLAP format: Payload is the domain hosting the lookup service
|
|
82
|
+
const fields = [Utils.toArray(domain, 'utf8')];
|
|
83
|
+
// Context is [0, service] to identify the lookup service being advertised
|
|
84
|
+
const lockingScript = (await token.lock(fields, [0, service], '1', 'self', true, true)).toHex();
|
|
85
|
+
|
|
86
|
+
const response = await wallet._setup.wallet.createAction({
|
|
87
|
+
description: `advertise SLAP for ${service}`,
|
|
88
|
+
outputs: [{
|
|
89
|
+
lockingScript,
|
|
90
|
+
satoshis: 1,
|
|
91
|
+
outputDescription: 'SLAP advertisement',
|
|
92
|
+
basket: TOPICS.SLAP
|
|
93
|
+
}]
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Broadcast to primary overlay and SLAP trackers
|
|
97
|
+
const trackers = [
|
|
98
|
+
...DEFAULT_SLAP_TRACKERS[NETWORK]
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
const results = await broadcastToTrackers(response.tx as number[], [TOPICS.SLAP, service], trackers);
|
|
102
|
+
|
|
103
|
+
return ok({
|
|
104
|
+
advertised: 'SLAP',
|
|
105
|
+
service,
|
|
106
|
+
domain,
|
|
107
|
+
txid: response.txid,
|
|
108
|
+
broadcasts: results
|
|
109
|
+
});
|
|
110
|
+
} catch (err: any) {
|
|
111
|
+
return fail(`SLAP advertisement failed: ${err.message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Helper to broadcast BEEF to multiple trackers/overlays.
|
|
117
|
+
*/
|
|
118
|
+
async function broadcastToTrackers(tx: number[], topics: string[], trackers: string[]) {
|
|
119
|
+
const results: Record<string, any> = {};
|
|
120
|
+
const body = new Uint8Array(tx);
|
|
121
|
+
|
|
122
|
+
for (const url of trackers) {
|
|
123
|
+
try {
|
|
124
|
+
const resp = await fetch(`${url.replace(/\/$/, '')}/submit`, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: {
|
|
127
|
+
'Content-Type': 'application/octet-stream',
|
|
128
|
+
'X-Topics': JSON.stringify(topics)
|
|
129
|
+
},
|
|
130
|
+
body
|
|
131
|
+
});
|
|
132
|
+
results[url] = resp.ok ? 'success' : `error: ${resp.status}`;
|
|
133
|
+
} catch (err: any) {
|
|
134
|
+
results[url] = `failed: ${err.message}`;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return results;
|
|
138
|
+
}
|
|
@@ -97,6 +97,22 @@ export async function cmdIdentity(): Promise<never> {
|
|
|
97
97
|
return ok({ identityKey });
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Status command: show identity and balance.
|
|
102
|
+
*/
|
|
103
|
+
export async function cmdStatus(): Promise<never> {
|
|
104
|
+
const BSVAgentWallet = await getBSVAgentWallet();
|
|
105
|
+
const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
|
|
106
|
+
const identityKey = await wallet.getIdentityKey();
|
|
107
|
+
const total = await wallet.getBalance();
|
|
108
|
+
await wallet.destroy();
|
|
109
|
+
|
|
110
|
+
return ok({
|
|
111
|
+
identity: { identityKey, network: NETWORK },
|
|
112
|
+
balance: { walletBalance: total }
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
100
116
|
/**
|
|
101
117
|
* Address command: show P2PKH receive address.
|
|
102
118
|
*/
|