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 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
- - `/openclaw_overlay status` — Quick view of identity and balance.
82
- - `/openclaw_overlay balance` — Check wallet funding.
83
- - `/openclaw_overlay discover <serviceId>` — Find providers for a specific service.
84
- - `/openclaw_overlay onboard` — Trigger the onboarding check.
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: openclaw-overlay
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
- | `setup` | Initialize wallet |
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
- ## Onboarding
35
+ ## Network Support
38
36
 
39
- On first run, the plugin auto-creates a wallet and wakes you. Guide the user through setup as a conversation:
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
- 1. **Ask for agent name**: "What name do you want for your agent on the network?"
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
- If you need to customize settings, tell the user to add them to their config under `plugins.entries["openclaw-overlay-plugin"]`:
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
- Do NOT use defaults without asking. Do NOT skip the name/description questions.
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
- ## Requesting Services
52
+ ## Discovery & Services
65
53
 
66
- Use `openclaw_overlay({ action: "request", service: "<id>", input: {...} })` to auto-discover the cheapest provider, pay, and send the request. The response arrives asynchronously via the background WebSocket service — you'll be woken when it comes back.
67
-
68
- Set `maxPrice` to cap spending. Requests within `maxAutoPaySats` (default 200) auto-pay.
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
- The background service queues incoming requests and wakes you automatically.
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
- - **Auto-pay**: Requests under `maxAutoPaySats` (default 200 sats) pay automatically
83
- - **Budget**: Daily spending capped at `dailyBudgetSats` (default 1,000 sats/day)
84
- - **Over limit**: Returns an error get user confirmation before retrying with `maxPrice`
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
- ## References
68
+ ## Spending & Security
88
69
 
89
- - [Service catalog and input schemas](references/services.md)
90
- - [Configuration, environment variables, and CLI commands](references/configuration.md)
91
- - [Wallet operations, funding, budget, and import details](references/wallet-operations.md)
92
- - [Overlay protocol specification](references/protocol.md)
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(api: any): Promise<void>;
10
- register(api: any): void;
10
+ activate: typeof register;
11
+ register: typeof register;
11
12
  };
12
- export default plugin;
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 const plugin = {
305
- id: "openclaw-overlay-plugin",
306
- name: "BSV Overlay Network",
307
- description: "OpenClaw Overlay — decentralized agent marketplace with BSV micropayments",
308
- async activate(api) {
309
- return this.register(api);
310
- },
311
- register(api) {
312
- if (isInitialized)
313
- return;
314
- isInitialized = true;
315
- const entries = api.getConfig?.()?.plugins?.entries || {};
316
- const entry = entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};
317
- const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };
318
- // 1. Tool
319
- api.registerTool({
320
- name: "overlay",
321
- description: "Access the BSV agent marketplace",
322
- parameters: {
323
- type: "object",
324
- properties: {
325
- action: { type: "string", enum: ["request", "discover", "balance", "status", "pay", "onboard", "pending-requests", "fulfill", "unregister"] },
326
- service: { type: "string" },
327
- input: { type: "object" },
328
- identityKey: { type: "string" },
329
- sats: { type: "number" },
330
- requestId: { type: "string" },
331
- recipientKey: { type: "string" },
332
- serviceId: { type: "string" },
333
- result: { type: "object" }
334
- },
335
- required: ["action"]
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
- async execute(_id, params) {
338
- try {
339
- return await executeOverlayAction(params, pluginConfig, api);
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
- catch (error) {
342
- return { content: [{ type: "text", text: `Error: ${error.message}` }] };
363
+ const params = { action };
364
+ if (action === 'discover') {
365
+ params.service = ctx.args[1];
343
366
  }
344
- }
345
- });
346
- // 2. Command
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
- catch (error) {
362
- return { text: `❌ Error: ${error.message}` };
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
- // 3. Service
367
- api.registerService({
368
- id: "openclaw-overlay-relay",
369
- start: async () => {
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
- // 4. CLI
378
- api.registerCli(({ program }) => {
379
- const overlay = program.command("overlay").description("BSV Overlay Network management");
380
- overlay.command("status").action(async () => {
381
- await ensureCp();
382
- const result = await handleStatus(buildEnvironment(pluginConfig), getCliPath());
383
- console.log(JSON.stringify(result, null, 2));
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 plugin;
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;
@@ -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);
@@ -2,7 +2,9 @@
2
2
  * @a2a-bsv/core — Configuration defaults and helpers.
3
3
  */
4
4
  export function toChain(network) {
5
- return network === 'mainnet' ? 'main' : 'test';
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 = {
@@ -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
  *
@@ -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 const plugin = {
317
- id: "openclaw-overlay-plugin",
318
- name: "BSV Overlay Network",
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
- register(api: any) {
326
- if (isInitialized) return;
327
- isInitialized = true;
320
+ // Initialize child process helpers
321
+ await ensureCp();
328
322
 
329
- const entries = api.getConfig?.()?.plugins?.entries || {};
330
- const entry = entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};
331
- const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };
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
- // 1. Tool
334
- api.registerTool({
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({ action }, pluginConfig, api);
376
- return { text: `**Overlay ${action.toUpperCase()}**\n\n${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}` };
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: "openclaw-overlay-relay",
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 plugin;
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;
@@ -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.67",
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.70",
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':
@@ -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
- return network === 'mainnet' ? 'main' : 'test';
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
  }
@@ -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,
@@ -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
  */