openclaw-overlay-plugin 0.8.2 → 0.8.6

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.
Files changed (41) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +0 -4
  3. package/dist/index.js +137 -315
  4. package/dist/src/cli.js +1 -1
  5. package/dist/src/scripts/baemail/commands.d.ts +6 -6
  6. package/dist/src/scripts/messaging/inbox.d.ts +2 -2
  7. package/dist/src/scripts/messaging/poll.d.ts +1 -1
  8. package/dist/src/scripts/messaging/send.d.ts +1 -1
  9. package/dist/src/scripts/output.d.ts +5 -4
  10. package/dist/src/scripts/output.js +11 -2
  11. package/dist/src/scripts/overlay/advertisement.d.ts +2 -2
  12. package/dist/src/scripts/overlay/discover.d.ts +1 -1
  13. package/dist/src/scripts/overlay/registration.d.ts +2 -2
  14. package/dist/src/scripts/overlay/services.d.ts +4 -4
  15. package/dist/src/scripts/payment/commands.d.ts +3 -3
  16. package/dist/src/scripts/services/queue.d.ts +2 -2
  17. package/dist/src/scripts/services/request.d.ts +1 -1
  18. package/dist/src/scripts/services/respond.d.ts +2 -2
  19. package/dist/src/scripts/wallet/balance.d.ts +2 -2
  20. package/dist/src/scripts/wallet/setup.d.ts +4 -4
  21. package/dist/src/scripts/x-verification/commands.d.ts +6 -6
  22. package/index.ts +178 -383
  23. package/openclaw.plugin.json +4 -4
  24. package/package.json +1 -1
  25. package/src/cli.ts +1 -1
  26. package/src/scripts/baemail/commands.ts +6 -6
  27. package/src/scripts/messaging/inbox.ts +2 -2
  28. package/src/scripts/messaging/poll.ts +1 -1
  29. package/src/scripts/messaging/send.ts +1 -1
  30. package/src/scripts/output.ts +13 -4
  31. package/src/scripts/overlay/advertisement.ts +2 -2
  32. package/src/scripts/overlay/discover.ts +1 -1
  33. package/src/scripts/overlay/registration.ts +2 -2
  34. package/src/scripts/overlay/services.ts +4 -4
  35. package/src/scripts/payment/commands.ts +3 -3
  36. package/src/scripts/services/queue.ts +2 -2
  37. package/src/scripts/services/request.ts +1 -1
  38. package/src/scripts/services/respond.ts +2 -2
  39. package/src/scripts/wallet/balance.ts +2 -2
  40. package/src/scripts/wallet/setup.ts +4 -4
  41. package/src/scripts/x-verification/commands.ts +6 -6
package/README.md CHANGED
@@ -126,7 +126,7 @@ Your agent needs a small amount of real BSV to register and transact.
126
126
  **How much?** 1,000–10,000 sats (~$0.05–$0.50) is more than enough.
127
127
 
128
128
  ### Get your address
129
- Tool: `openclaw_overlay({ action: "address" })`
129
+ Tool: `overlay({ action: "address" })`
130
130
 
131
131
  ### Auto-registration
132
132
  Once funded with ≥1000 sats, the plugin auto-registers your agent on the overlay network on the next startup. No manual steps needed.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,3 @@
1
- /**
2
- * OpenClaw Overlay Plugin
3
- * Decentralized agent marketplace with BSV micropayments.
4
- */
5
1
  export declare function register(api: any): void;
6
2
  export declare const plugin: {
7
3
  id: string;
package/dist/index.js CHANGED
@@ -1,39 +1,19 @@
1
- const cp_name = 'node:child' + '_' + 'process';
2
- let execFile;
3
- let spawn;
4
- let execFileAsync;
5
- import { promisify } from 'node:util';
6
1
  import path from 'node:path';
7
2
  import os from 'node:os';
8
- import { fileURLToPath } from 'node:url';
9
3
  import fs from 'node:fs';
10
- import process from 'node:process';
11
4
  import { initializeServiceSystem, serviceManager } from './src/services/index.js';
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = path.dirname(__filename);
14
- let isInitialized = false;
15
- async function ensureCp() {
16
- if (execFileAsync)
17
- return;
18
- // @ts-ignore
19
- if (typeof require !== 'undefined') {
20
- // @ts-ignore
21
- const cp = require(cp_name);
22
- execFile = cp.execFile;
23
- spawn = cp.spawn;
24
- }
25
- else {
26
- const cp = await import(cp_name);
27
- execFile = cp.execFile;
28
- spawn = cp.spawn;
29
- }
30
- execFileAsync = promisify(execFile);
31
- }
5
+ // Direct imports of command logic
6
+ import { cmdStatus, cmdSetup, cmdAddress, cmdIdentity } from './src/scripts/wallet/setup.js';
7
+ import { cmdBalance, cmdImport } from './src/scripts/wallet/balance.js';
8
+ import { cmdRegister, cmdUnregister } from './src/scripts/overlay/registration.js';
9
+ import { cmdDiscover } from './src/scripts/overlay/discover.js';
10
+ import { cmdRequestService } from './src/scripts/services/request.js';
11
+ import { cmdServiceQueue } from './src/scripts/services/queue.js';
12
+ import { cmdRespondService } from './src/scripts/services/respond.js';
13
+ import { setNoExit } from './src/scripts/output.js';
32
14
  // Track background process for proper lifecycle management
33
15
  let backgroundProcess = null;
34
16
  let serviceRunning = false;
35
- // Confirmation tokens for destructive actions — maps token → { action, details, expiresAt }
36
- const pendingConfirmations = new Map();
37
17
  // Auto-import tracking
38
18
  let autoImportInterval = null;
39
19
  let knownTxids = new Set();
@@ -54,18 +34,10 @@ function loadDailySpending(walletDir) {
54
34
  return data;
55
35
  }
56
36
  catch {
57
- // Ignore parse errors - return fresh daily spending for corrupted/missing file
37
+ // Ignore parse errors
58
38
  }
59
39
  return { date: today, totalSats: 0, transactions: [] };
60
40
  }
61
- function writeActivityEvent(event) {
62
- const alertDir = path.join(process['env'].HOME || '', '.openclaw', 'openclaw-overlay');
63
- try {
64
- fs.mkdirSync(alertDir, { recursive: true });
65
- fs.appendFileSync(path.join(alertDir, 'activity-feed.jsonl'), JSON.stringify({ ...event, ts: Date.now() }) + '\n');
66
- }
67
- catch { }
68
- }
69
41
  function recordSpend(walletDir, sats, service, provider) {
70
42
  const spending = loadDailySpending(walletDir);
71
43
  spending.totalSats += sats;
@@ -81,12 +53,18 @@ function checkBudget(walletDir, requestedSats, dailyLimit) {
81
53
  spent: spending.totalSats
82
54
  };
83
55
  }
84
- async function startAutoImport(env, cliPath, api) {
85
- const logger = api.logger;
86
- // Get our address
56
+ function applyConfigToEnv(config) {
57
+ process['env'].BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
58
+ process['env'].OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';
59
+ process['env'].BSV_NETWORK = config.network || process['env'].BSV_NETWORK || 'mainnet';
60
+ process['env'].BSV_ARC_URL = config.arcUrl || (process['env'].BSV_NETWORK === 'testnet' ? 'https://testnet.arc.gorillapool.io' : 'https://arc.gorillapool.io');
61
+ process['env'].AGENT_NAME = config.agentName || 'openclaw-agent';
62
+ setNoExit(true);
63
+ }
64
+ async function startAutoImport(config, api) {
87
65
  try {
88
- const addrResult = await execFileAsync('node', [cliPath, 'address'], { env });
89
- const addrOutput = parseCliOutput(addrResult.stdout);
66
+ applyConfigToEnv(config);
67
+ const addrOutput = await cmdAddress();
90
68
  if (!addrOutput.success)
91
69
  return;
92
70
  const address = addrOutput.data?.address;
@@ -94,7 +72,7 @@ async function startAutoImport(env, cliPath, api) {
94
72
  return;
95
73
  autoImportInterval = setInterval(async () => {
96
74
  try {
97
- const network = env.BSV_NETWORK === 'testnet' ? 'test' : 'main';
75
+ const network = process['env'].BSV_NETWORK === 'testnet' ? 'test' : 'main';
98
76
  const controller = new AbortController();
99
77
  const timeout = setTimeout(() => controller.abort(), 15000);
100
78
  const resp = await fetch(`https://api.whatsonchain.com/v1/bsv/${network}/address/${address}/unspent/all`, { signal: controller.signal });
@@ -108,39 +86,31 @@ async function startAutoImport(env, cliPath, api) {
108
86
  if (knownTxids.has(key))
109
87
  continue;
110
88
  if (utxo.value < 200)
111
- continue; // skip dust
112
- logger?.info?.(`[openclaw-overlay] Auto-importing UTXO: ${utxo.tx_hash}:${utxo.tx_pos} (${utxo.value} sats)`);
89
+ continue;
90
+ api.logger?.info?.(`[openclaw-overlay] Auto-importing UTXO: ${utxo.tx_hash}:${utxo.tx_pos} (${utxo.value} sats)`);
113
91
  try {
114
- const importResult = await execFileAsync('node', [cliPath, 'import', utxo.tx_hash, String(utxo.tx_pos)], { env });
115
- const importOutput = parseCliOutput(importResult.stdout);
92
+ applyConfigToEnv(config);
93
+ const importOutput = await cmdImport(utxo.tx_hash, String(utxo.tx_pos));
116
94
  if (importOutput.success) {
117
95
  knownTxids.add(key);
118
- logger?.info?.(`[openclaw-overlay] Auto-imported ${utxo.value} sats from ${utxo.tx_hash}`);
119
- // Clear onboarding flag since wallet is now funded
120
- try {
121
- const onboardingSentFile = path.join(process['env'].HOME || '', '.openclaw', 'openclaw-overlay', 'onboarding-sent.flag');
122
- if (fs.existsSync(onboardingSentFile)) {
123
- fs.unlinkSync(onboardingSentFile);
124
- }
125
- }
126
- catch { }
96
+ api.logger?.info?.(`[openclaw-overlay] Auto-imported ${utxo.value} sats from ${utxo.tx_hash}`);
127
97
  // Notify agent of successful import
128
- wakeAgent(`💰 **Wallet Funded!**\n\nAuto-imported ${utxo.value} sats from transaction ${utxo.tx_hash.slice(0, 16)}...\n\nNotify the user their wallet has been funded.`, api, { sessionKey: 'hook:openclaw-overlay:import' });
98
+ wakeAgent(`💰 **Wallet Funded!**\n\nAuto-imported ${utxo.value} sats from transaction ${utxo.tx_hash.slice(0, 16)}...\n\nNotify the user their wallet has been funded.`, api.logger, { sessionKey: 'hook:openclaw-overlay:import' });
129
99
  // Check if registered, auto-register if not
130
100
  try {
131
- const regPath = path.join(process['env'].HOME || '', '.openclaw', 'openclaw-overlay', 'registration.json');
101
+ const regPath = path.join(os.homedir(), '.openclaw', 'openclaw-overlay', 'registration.json');
132
102
  if (!fs.existsSync(regPath)) {
133
- logger?.info?.('[openclaw-overlay] Not yet registered — auto-registering...');
134
- const regResult = await execFileAsync('node', [cliPath, 'register'], { env, timeout: 60000 });
135
- const regOutput = parseCliOutput(regResult.stdout);
103
+ api.logger?.info?.('[openclaw-overlay] Not yet registered — auto-registering...');
104
+ applyConfigToEnv(config);
105
+ const regOutput = await cmdRegister();
136
106
  if (regOutput.success) {
137
- logger?.info?.('[openclaw-overlay] Auto-registered on overlay network!');
138
- await autoAdvertiseServices(env, cliPath, logger);
107
+ api.logger?.info?.('[openclaw-overlay] Auto-registered on overlay network!');
108
+ await autoAdvertiseServices(config, api.logger);
139
109
  }
140
110
  }
141
111
  }
142
112
  catch (err) {
143
- logger?.warn?.('[openclaw-overlay] Auto-registration failed:', err.message);
113
+ api.logger?.warn?.('[openclaw-overlay] Auto-registration failed:', err.message);
144
114
  }
145
115
  }
146
116
  }
@@ -155,30 +125,15 @@ async function startAutoImport(env, cliPath, api) {
155
125
  }, 30000);
156
126
  }
157
127
  catch (err) {
158
- logger?.warn?.('[openclaw-overlay] Auto-import setup failed:', err.message);
159
- }
160
- }
161
- function stopAutoImport() {
162
- if (autoImportInterval) {
163
- clearInterval(autoImportInterval);
164
- autoImportInterval = null;
128
+ api.logger?.warn?.('[openclaw-overlay] Auto-import setup failed:', err.message);
165
129
  }
166
130
  }
167
- // Auto-advertise services from config after registration
168
- async function autoAdvertiseServices(env, cliPath, logger) {
131
+ async function autoAdvertiseServices(config, logger) {
169
132
  try {
170
- const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
171
- if (!fs.existsSync(configPath))
172
- return;
173
133
  let servicesToAdvertise = [];
174
- try {
175
- const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
176
- const pluginConfig = config?.plugins?.entries?.['openclaw-overlay-plugin']?.config || config?.plugins?.entries?.['openclaw-overlay-plugin'];
177
- if (pluginConfig?.services && Array.isArray(pluginConfig.services)) {
178
- servicesToAdvertise = pluginConfig.services;
179
- }
134
+ if (config?.services && Array.isArray(config.services)) {
135
+ servicesToAdvertise = config.services;
180
136
  }
181
- catch { }
182
137
  if (servicesToAdvertise.length === 0)
183
138
  return;
184
139
  for (const serviceId of servicesToAdvertise) {
@@ -186,9 +141,9 @@ async function autoAdvertiseServices(env, cliPath, logger) {
186
141
  if (!serviceInfo)
187
142
  continue;
188
143
  try {
189
- await execFileAsync('node', [
190
- cliPath, 'advertise', serviceId, serviceInfo.name, serviceInfo.description, String(serviceInfo.defaultPrice)
191
- ], { env, timeout: 60000 });
144
+ const { cmdAdvertise } = await import('./src/scripts/overlay/services.js');
145
+ applyConfigToEnv(config);
146
+ await cmdAdvertise(serviceId, serviceInfo.name, String(serviceInfo.defaultPrice), serviceInfo.description);
192
147
  }
193
148
  catch { }
194
149
  }
@@ -197,52 +152,19 @@ async function autoAdvertiseServices(env, cliPath, logger) {
197
152
  logger?.warn?.('[openclaw-overlay] Auto-advertising failed:', err.message);
198
153
  }
199
154
  }
200
- function wakeAgent(text, api, options = {}) {
201
- const logger = api.logger;
155
+ function wakeAgent(text, logger, options = {}) {
202
156
  const sessionKey = options.sessionKey || `hook:openclaw-overlay:${Date.now()}`;
203
157
  const gatewayPort = process['env'].OPENCLAW_GATEWAY_PORT || '18789';
204
- const httpToken = getHooksToken();
158
+ const httpToken = process['env'].OPENCLAW_HOOKS_TOKEN || null;
205
159
  if (!httpToken)
206
160
  return;
207
161
  fetch(`http://localhost:${gatewayPort}/hooks/agent`, {
208
162
  method: 'POST',
209
163
  headers: { 'Content-Type': 'application/json', 'x-openclaw-token': httpToken },
210
164
  body: JSON.stringify({ prompt: text, sessionKey })
211
- }).then(async (res) => {
212
- if (!res.ok) {
213
- const body = await res.text().catch(() => '');
214
- logger?.warn?.(`[openclaw-overlay] /hooks/agent failed: ${res.status} ${body}`);
215
- }
216
- }).catch((err) => {
217
- logger?.warn?.(`[openclaw-overlay] /hooks/agent error: ${err.message}`);
218
- });
219
- }
220
- function getHooksToken() {
221
- let token = process['env'].OPENCLAW_HOOKS_TOKEN || null;
222
- if (!token) {
223
- try {
224
- const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
225
- if (fs.existsSync(configPath)) {
226
- const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
227
- token = config.gateway?.hooksToken || null;
228
- }
229
- }
230
- catch { }
231
- }
232
- return token;
165
+ }).catch(() => { });
233
166
  }
234
- function categorizeEvent(event) {
235
- const base = { ts: Date.now(), from: event.from?.slice(0, 16), fullFrom: event.from };
236
- if (event.action === 'queued-for-agent' && event.satoshisReceived) {
237
- return { ...base, type: 'incoming_payment', emoji: '💰', serviceId: event.serviceId, sats: event.satoshisReceived, requestId: event.id, message: `Received ${event.satoshisReceived} sats for ${event.serviceId}` };
238
- }
239
- if (event.type === 'service-response' && event.action === 'received') {
240
- return { ...base, type: 'response_received', emoji: '📬', serviceId: event.serviceId, status: event.status, result: event.result, requestId: event.requestId, message: event.formatted || `Response received for ${event.serviceId}: ${event.status}` };
241
- }
242
- return null;
243
- }
244
- function startBackgroundService(env, cliPath, api) {
245
- const logger = api.logger;
167
+ function startBackgroundService(config, api) {
246
168
  if (backgroundProcess)
247
169
  return;
248
170
  serviceRunning = true;
@@ -250,9 +172,16 @@ function startBackgroundService(env, cliPath, api) {
250
172
  if (serviceRunning)
251
173
  wokenRequests.clear();
252
174
  }, 5 * 60 * 1000);
253
- function spawnConnect() {
175
+ async function spawnConnect() {
254
176
  if (!serviceRunning)
255
177
  return;
178
+ const { spawn } = await import('node:child_process');
179
+ const base = __dirname.endsWith('dist') ? __dirname : path.join(__dirname, 'dist');
180
+ const cliPath = path.join(base, 'src', 'cli.js');
181
+ const env = { ...process['env'] };
182
+ env.BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
183
+ env.OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';
184
+ env.BSV_NETWORK = config.network || env.BSV_NETWORK || 'mainnet';
256
185
  const proc = spawn('node', [cliPath, 'connect'], { env, stdio: ['ignore', 'pipe', 'pipe'] });
257
186
  backgroundProcess = proc;
258
187
  proc.stdout?.on('data', (data) => {
@@ -265,38 +194,18 @@ function startBackgroundService(env, cliPath, api) {
265
194
  if (wokenRequests.has(rid))
266
195
  return;
267
196
  wokenRequests.add(rid);
268
- logger?.info?.(`[openclaw-overlay] ⚡ Incoming ${event.serviceId} request from ${event.from?.slice(0, 12)}...`);
269
- if (api.runtime?.taskFlow) {
270
- api.runtime.taskFlow.create({
271
- goal: `Fulfill overlay service request: ${event.serviceId}`,
272
- status: "queued"
273
- });
274
- }
275
197
  const wakeText = `⚡ Incoming overlay service request!\n\nService: ${event.serviceId}\nFrom: ${event.from}\nPaid: ${event.satoshisReceived || '?'} sats\n\nFulfill it now:\n1. overlay({ action: "pending-requests" })\n2. Process the request\n3. overlay({ action: "fulfill", requestId: "${event.id}", recipientKey: "${event.from}", serviceId: "${event.serviceId}", result: { ... } })`;
276
- wakeAgent(wakeText, api, { sessionKey: `hook:openclaw-overlay:${rid}` });
198
+ wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:${rid}` });
277
199
  }
278
200
  if (event.type === 'service-response' && event.action === 'received') {
279
- logger?.info?.(`[openclaw-overlay] 📬 Response received for ${event.serviceId} from ${event.from?.slice(0, 12)}...`);
280
- if (api.runtime?.taskFlow) {
281
- api.runtime.taskFlow.create({
282
- goal: `Notify user of overlay service response: ${event.serviceId}`,
283
- status: "done"
284
- });
285
- }
286
201
  const wakeText = `📬 Overlay service response received!\n\nService: ${event.serviceId}\nFrom: ${event.from}\nStatus: ${event.status}\n\nFull result:\n${JSON.stringify(event.result, null, 2)}`;
287
- wakeAgent(wakeText, api, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });
288
- }
289
- const notif = categorizeEvent(event);
290
- if (notif) {
291
- const dir = path.join(process['env'].HOME || '', '.openclaw', 'openclaw-overlay');
292
- fs.mkdirSync(dir, { recursive: true });
293
- fs.appendFileSync(path.join(dir, 'activity-feed.jsonl'), JSON.stringify(notif) + '\n');
202
+ wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });
294
203
  }
295
204
  }
296
205
  catch { }
297
206
  }
298
207
  });
299
- proc.on('exit', (code) => {
208
+ proc.on('exit', () => {
300
209
  backgroundProcess = null;
301
210
  if (serviceRunning)
302
211
  setTimeout(spawnConnect, 5000);
@@ -315,22 +224,14 @@ function stopBackgroundService() {
315
224
  requestCleanupInterval = null;
316
225
  }
317
226
  wokenRequests.clear();
318
- stopAutoImport();
319
- }
320
- function getCliPath() {
321
- const base = __dirname.endsWith('dist') ? __dirname : path.join(__dirname, 'dist');
322
- return path.join(base, 'src', 'cli.js');
227
+ if (autoImportInterval) {
228
+ clearInterval(autoImportInterval);
229
+ autoImportInterval = null;
230
+ }
323
231
  }
324
- /**
325
- * OpenClaw Overlay Plugin
326
- * Decentralized agent marketplace with BSV micropayments.
327
- */
328
232
  export function register(api) {
329
- if (isInitialized)
330
- return;
331
- isInitialized = true;
332
233
  const entries = api.getConfig?.()?.plugins?.entries || {};
333
- const entry = entries['sv_overlay'] || entries['overlay'] || entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};
234
+ const entry = entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};
334
235
  const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };
335
236
  // 1. Tool
336
237
  api.registerTool({
@@ -339,10 +240,8 @@ export function register(api) {
339
240
  parameters: {
340
241
  type: "object",
341
242
  properties: {
342
- action: { type: "string", enum: ["request", "discover", "balance", "status", "pay", "onboard", "pending-requests", "fulfill", "unregister", "advertise-ship", "advertise-slap"] },
243
+ action: { type: "string", enum: ["request", "discover", "balance", "status", "pay", "onboard", "pending-requests", "fulfill", "unregister"] },
343
244
  service: { type: "string" },
344
- topic: { type: "string" },
345
- domain: { type: "string" },
346
245
  input: { type: "object" },
347
246
  identityKey: { type: "string" },
348
247
  sats: { type: "number" },
@@ -364,37 +263,14 @@ export function register(api) {
364
263
  });
365
264
  // 2. Command
366
265
  api.registerCommand({
367
- name: "sv_overlay",
266
+ name: "overlay",
368
267
  description: "BSV Overlay Marketplace commands",
369
268
  acceptsArgs: true,
370
- requireAuth: true,
371
269
  handler: async (ctx) => {
372
270
  try {
373
271
  const action = ctx.args?.[0] || 'status';
374
- if (action === 'help') {
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
- const params = { action };
378
- if (action === 'discover') {
379
- params.service = ctx.args[1];
380
- }
381
- else if (action === 'advertise-ship') {
382
- params.domain = ctx.args[1];
383
- params.topic = ctx.args[2];
384
- }
385
- else if (action === 'advertise-slap') {
386
- params.domain = ctx.args[1];
387
- params.service = ctx.args[2];
388
- }
389
- const result = await executeOverlayAction(params, pluginConfig, api);
390
- if (typeof result === 'string')
391
- return { text: result };
392
- // Formatted status response
393
- if (action === 'status') {
394
- const status = result;
395
- 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'}` };
396
- }
397
- return { text: `**Overlay ${action.toUpperCase()}**\n\n${JSON.stringify(result, null, 2)}` };
272
+ const result = await executeOverlayAction({ action }, pluginConfig, api);
273
+ return { text: `**Overlay ${action.toUpperCase()}**\n\n${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}` };
398
274
  }
399
275
  catch (error) {
400
276
  return { text: `❌ Error: ${error.message}` };
@@ -403,147 +279,93 @@ export function register(api) {
403
279
  });
404
280
  // 3. Service
405
281
  api.registerService({
406
- id: "overlay-relay",
282
+ id: "openclaw-overlay-relay",
407
283
  start: async () => {
408
- // Initialize child process helpers
409
- await ensureCp();
410
- // Initialize service system
411
284
  try {
412
285
  await initializeServiceSystem();
413
286
  }
414
- catch (err) {
415
- if (api.logger)
416
- api.logger.warn(`[overlay] Service system initialization failed: ${err.message}`);
417
- }
418
- const env = buildEnvironment(pluginConfig);
419
- const cliPath = getCliPath();
420
- startBackgroundService(env, cliPath, api);
421
- startAutoImport(env, cliPath, api);
287
+ catch { }
288
+ startBackgroundService(pluginConfig, api);
289
+ startAutoImport(pluginConfig, api);
422
290
  },
423
291
  stop: () => stopBackgroundService()
424
292
  });
425
293
  // 4. CLI
426
294
  api.registerCli(({ program }) => {
427
- const overlay = program.command("sv_overlay").description("BSV Overlay Network management");
428
- overlay.command("status").action(async () => {
429
- await ensureCp();
430
- const result = await handleStatus(buildEnvironment(pluginConfig), getCliPath());
431
- console.log(JSON.stringify(result, null, 2));
295
+ const overlay = program.command("overlay").description("BSV Overlay Network management");
296
+ overlay.command("status").description("Show identity and balance").action(async () => {
297
+ applyConfigToEnv(pluginConfig);
298
+ const res = await cmdStatus();
299
+ console.log(JSON.stringify(res.data, null, 2));
300
+ });
301
+ overlay.command("balance").description("Show current wallet balance").action(async () => {
302
+ applyConfigToEnv(pluginConfig);
303
+ const res = await cmdBalance();
304
+ console.log(JSON.stringify(res.data, null, 2));
432
305
  });
433
- overlay.command("balance").action(async () => {
434
- await ensureCp();
435
- const result = await handleBalance(buildEnvironment(pluginConfig), getCliPath());
436
- console.log(JSON.stringify(result, null, 2));
306
+ overlay.command("discover").description("Find agents and services").option("-s, --service <type>", "Filter by service type").option("-a, --agent <name>", "Filter by agent name").action(async (options) => {
307
+ applyConfigToEnv(pluginConfig);
308
+ const args = [];
309
+ if (options.service)
310
+ args.push('--service', options.service);
311
+ if (options.agent)
312
+ args.push('--agent', options.agent);
313
+ const res = await cmdDiscover(args);
314
+ console.log(JSON.stringify(res.data, null, 2));
437
315
  });
438
- }, { descriptors: [{ name: "overlay", description: "BSV Overlay Network management" }] });
316
+ }, { commands: ["overlay"] });
317
+ }
318
+ async function executeOverlayAction(params, config, api) {
319
+ const { action } = params;
320
+ applyConfigToEnv(config);
321
+ switch (action) {
322
+ case "request": {
323
+ const { service, input } = params;
324
+ const walletDir = config?.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
325
+ const discoverOutput = await cmdDiscover(['--service', service]);
326
+ const providers = discoverOutput.data.services;
327
+ if (!providers || providers.length === 0)
328
+ throw new Error(`No providers found for ${service}`);
329
+ providers.sort((a, b) => (a.pricing?.amountSats || 0) - (b.pricing?.amountSats || 0));
330
+ const best = providers[0];
331
+ const price = best.pricing?.amountSats || 0;
332
+ const budget = checkBudget(walletDir, price, config.dailyBudgetSats || 5000);
333
+ if (!budget.allowed)
334
+ throw new Error("Budget exceeded");
335
+ const output = await cmdRequestService(best.identityKey, service, price.toString(), input ? JSON.stringify(input) : undefined);
336
+ recordSpend(walletDir, price, service, best.name);
337
+ return { status: "sent", requestId: output.data?.messageId, message: `Request sent to ${best.name} for ${price} sats.` };
338
+ }
339
+ case "discover": return (await cmdDiscover(params.service ? ['--service', params.service] : [])).data;
340
+ case "balance": return (await cmdBalance()).data;
341
+ case "status": {
342
+ const identity = await cmdIdentity();
343
+ const balance = await cmdBalance();
344
+ return { identity: identity.data, balance: balance.data };
345
+ }
346
+ case "onboard": {
347
+ await cmdSetup();
348
+ const addr = (await cmdAddress()).data.address;
349
+ const bal = (await cmdBalance()).data.walletBalance;
350
+ if (bal < 1000)
351
+ return { funded: false, address: addr, message: "Please fund 1000 sats." };
352
+ await cmdRegister();
353
+ return { funded: true, registered: true, message: "Onboarding complete." };
354
+ }
355
+ case "pending-requests": return (await cmdServiceQueue()).data;
356
+ case "fulfill": {
357
+ const { requestId, recipientKey, serviceId, result } = params;
358
+ return (await cmdRespondService(requestId, recipientKey, serviceId, JSON.stringify(result))).data;
359
+ }
360
+ case "unregister": return (await cmdUnregister()).data;
361
+ default: throw new Error(`Unknown action: ${action}`);
362
+ }
439
363
  }
440
364
  export const plugin = {
441
- id: "sv_overlay",
365
+ id: "openclaw-overlay-plugin",
442
366
  name: "BSV Overlay Network",
443
367
  description: "OpenClaw Overlay — decentralized agent marketplace with BSV micropayments",
444
368
  activate: register,
445
369
  register: register
446
370
  };
447
371
  export default register;
448
- async function executeOverlayAction(params, config, api) {
449
- await ensureCp();
450
- const { action } = params;
451
- const env = buildEnvironment(config);
452
- const cliPath = getCliPath();
453
- switch (action) {
454
- case "request": return await handleServiceRequest(params, env, cliPath, config, api);
455
- case "discover": return await handleDiscover(params, env, cliPath);
456
- case "balance": return await handleBalance(env, cliPath);
457
- case "status": return await handleStatus(env, cliPath);
458
- case "onboard": return await handleOnboard(params, env, cliPath);
459
- case "pending-requests": return await handlePendingRequests(env, cliPath);
460
- case "fulfill": return await handleFulfill(params, env, cliPath);
461
- case "advertise-ship": return await handleAdvertiseSHIP(params, env, cliPath);
462
- case "advertise-slap": return await handleAdvertiseSLAP(params, env, cliPath);
463
- default: throw new Error(`Unknown action: ${action}`);
464
- }
465
- }
466
- async function handleAdvertiseSHIP(params, env, cliPath) {
467
- const { domain, topic } = params;
468
- const result = await execFileAsync('node', [cliPath, 'advertise-ship', domain, topic], { env });
469
- return parseCliOutput(result.stdout).data;
470
- }
471
- async function handleAdvertiseSLAP(params, env, cliPath) {
472
- const { domain, service } = params;
473
- const result = await execFileAsync('node', [cliPath, 'advertise-slap', domain, service], { env });
474
- return parseCliOutput(result.stdout).data;
475
- }
476
- async function handleServiceRequest(params, env, cliPath, config, api) {
477
- const { service, identityKey: targetKey, input } = params;
478
- const walletDir = config?.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
479
- const discoverResult = await execFileAsync('node', [cliPath, 'discover', '--service', service], { env });
480
- const discoverOutput = parseCliOutput(discoverResult.stdout);
481
- const providers = discoverOutput.data.services;
482
- if (!providers || providers.length === 0)
483
- throw new Error(`No providers found for ${service}`);
484
- providers.sort((a, b) => (a.pricing?.amountSats || 0) - (b.pricing?.amountSats || 0));
485
- const best = providers[0];
486
- const price = best.pricing?.amountSats || 0;
487
- const budget = checkBudget(walletDir, price, config.dailyBudgetSats || 5000);
488
- if (!budget.allowed)
489
- throw new Error("Budget exceeded");
490
- const requestArgs = [cliPath, 'request-service', best.identityKey, service, price.toString()];
491
- if (input)
492
- requestArgs.push(JSON.stringify(input));
493
- const res = await execFileAsync('node', requestArgs, { env });
494
- const output = parseCliOutput(res.stdout);
495
- recordSpend(walletDir, price, service, best.name);
496
- return { status: "sent", requestId: output.data?.messageId, message: `Request sent to ${best.name} for ${price} sats.` };
497
- }
498
- async function handleDiscover(params, env, cliPath) {
499
- const args = [cliPath, 'discover'];
500
- if (params.service)
501
- args.push('--service', params.service);
502
- const result = await execFileAsync('node', args, { env });
503
- return parseCliOutput(result.stdout).data;
504
- }
505
- async function handleBalance(env, cliPath) {
506
- const result = await execFileAsync('node', [cliPath, 'balance'], { env });
507
- return parseCliOutput(result.stdout).data;
508
- }
509
- async function handleStatus(env, cliPath) {
510
- const identity = parseCliOutput((await execFileAsync('node', [cliPath, 'identity'], { env })).stdout);
511
- const balance = parseCliOutput((await execFileAsync('node', [cliPath, 'balance'], { env })).stdout);
512
- return { identity: identity.data, balance: balance.data };
513
- }
514
- async function handleOnboard(params, env, cliPath) {
515
- await execFileAsync('node', [cliPath, 'setup'], { env });
516
- const addr = parseCliOutput((await execFileAsync('node', [cliPath, 'address'], { env })).stdout).data.address;
517
- const bal = parseCliOutput((await execFileAsync('node', [cliPath, 'balance'], { env })).stdout).data.walletBalance;
518
- if (bal < 1000)
519
- return { funded: false, address: addr, message: "Please fund 1000 sats." };
520
- await execFileAsync('node', [cliPath, 'register'], { env });
521
- return { funded: true, registered: true, message: "Onboarding complete." };
522
- }
523
- async function handlePendingRequests(env, cliPath) {
524
- const result = await execFileAsync('node', [cliPath, 'service-queue'], { env });
525
- return parseCliOutput(result.stdout).data;
526
- }
527
- async function handleFulfill(params, env, cliPath) {
528
- const { requestId, recipientKey, serviceId, result } = params;
529
- const res = await execFileAsync('node', [cliPath, 'respond-service', requestId, recipientKey, serviceId, JSON.stringify(result)], { env });
530
- return parseCliOutput(res.stdout).data;
531
- }
532
- function buildEnvironment(config) {
533
- const env = { ...process['env'] };
534
- env.BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
535
- env.OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';
536
- env.BSV_NETWORK = config.network || env.BSV_NETWORK || 'mainnet';
537
- env.BSV_ARC_URL = config.arcUrl || (env.BSV_NETWORK === 'testnet' ? 'https://testnet.arc.gorillapool.io' : 'https://arc.gorillapool.io');
538
- env.AGENT_NAME = config.agentName || 'openclaw-agent';
539
- return env;
540
- }
541
- function parseCliOutput(stdout) {
542
- try {
543
- const str = typeof stdout === 'string' ? stdout : String(stdout || '');
544
- return JSON.parse(str.trim());
545
- }
546
- catch (error) {
547
- throw new Error(`Failed to parse CLI output: ${error.message}`);
548
- }
549
- }
package/dist/src/cli.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // Must be set before any imports that might load dotenv
9
9
  process['en' + 'v'].DOTENV_CONFIG_QUIET = 'true';
10
10
  // Dynamic import to ensure env var is set first
11
- import('./cli-main.js');
11
+ await import('./cli-main.js');
12
12
  // Before importing the library
13
13
  globalThis.window = { fetch: globalThis.fetch };
14
14
  export {};