openclaw-overlay-plugin 0.8.4 → 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 (40) hide show
  1. package/dist/index.d.ts +0 -1
  2. package/dist/index.js +118 -231
  3. package/dist/src/cli.js +1 -1
  4. package/dist/src/scripts/baemail/commands.d.ts +6 -6
  5. package/dist/src/scripts/messaging/inbox.d.ts +2 -2
  6. package/dist/src/scripts/messaging/poll.d.ts +1 -1
  7. package/dist/src/scripts/messaging/send.d.ts +1 -1
  8. package/dist/src/scripts/output.d.ts +5 -4
  9. package/dist/src/scripts/output.js +11 -2
  10. package/dist/src/scripts/overlay/advertisement.d.ts +2 -2
  11. package/dist/src/scripts/overlay/discover.d.ts +1 -1
  12. package/dist/src/scripts/overlay/registration.d.ts +2 -2
  13. package/dist/src/scripts/overlay/services.d.ts +4 -4
  14. package/dist/src/scripts/payment/commands.d.ts +3 -3
  15. package/dist/src/scripts/services/queue.d.ts +2 -2
  16. package/dist/src/scripts/services/request.d.ts +1 -1
  17. package/dist/src/scripts/services/respond.d.ts +2 -2
  18. package/dist/src/scripts/wallet/balance.d.ts +2 -2
  19. package/dist/src/scripts/wallet/setup.d.ts +4 -4
  20. package/dist/src/scripts/x-verification/commands.d.ts +6 -6
  21. package/index.ts +117 -244
  22. package/openclaw.plugin.json +2 -2
  23. package/package.json +1 -1
  24. package/src/cli.ts +1 -1
  25. package/src/scripts/baemail/commands.ts +6 -6
  26. package/src/scripts/messaging/inbox.ts +2 -2
  27. package/src/scripts/messaging/poll.ts +1 -1
  28. package/src/scripts/messaging/send.ts +1 -1
  29. package/src/scripts/output.ts +13 -4
  30. package/src/scripts/overlay/advertisement.ts +2 -2
  31. package/src/scripts/overlay/discover.ts +1 -1
  32. package/src/scripts/overlay/registration.ts +2 -2
  33. package/src/scripts/overlay/services.ts +4 -4
  34. package/src/scripts/payment/commands.ts +3 -3
  35. package/src/scripts/services/queue.ts +2 -2
  36. package/src/scripts/services/request.ts +1 -1
  37. package/src/scripts/services/respond.ts +2 -2
  38. package/src/scripts/wallet/balance.ts +2 -2
  39. package/src/scripts/wallet/setup.ts +4 -4
  40. package/src/scripts/x-verification/commands.ts +6 -6
@@ -4,12 +4,12 @@
4
4
  /**
5
5
  * Pay command: send satoshis to another agent.
6
6
  */
7
- export declare function cmdPay(pubkey: string | undefined, satoshis: string | undefined, description?: string): Promise<never>;
7
+ export declare function cmdPay(pubkey: string | undefined, satoshis: string | undefined, description?: string): Promise<any>;
8
8
  /**
9
9
  * Verify command: verify an incoming payment BEEF.
10
10
  */
11
- export declare function cmdVerify(beefBase64: string | undefined): Promise<never>;
11
+ export declare function cmdVerify(beefBase64: string | undefined): Promise<any>;
12
12
  /**
13
13
  * Accept command: accept and internalize a payment.
14
14
  */
15
- export declare function cmdAccept(beef: string | undefined, derivationPrefix: string | undefined, derivationSuffix: string | undefined, senderIdentityKey: string | undefined, description?: string): Promise<never>;
15
+ export declare function cmdAccept(beef: string | undefined, derivationPrefix: string | undefined, derivationSuffix: string | undefined, senderIdentityKey: string | undefined, description?: string): Promise<any>;
@@ -4,8 +4,8 @@
4
4
  /**
5
5
  * Service queue command: list pending service requests.
6
6
  */
7
- export declare function cmdServiceQueue(): Promise<never>;
7
+ export declare function cmdServiceQueue(): Promise<any>;
8
8
  /**
9
9
  * Research queue command: list pending research requests.
10
10
  */
11
- export declare function cmdResearchQueue(): Promise<never>;
11
+ export declare function cmdResearchQueue(): Promise<any>;
@@ -4,4 +4,4 @@
4
4
  /**
5
5
  * Request service command: send a service request with optional payment.
6
6
  */
7
- export declare function cmdRequestService(targetKey: string | undefined, serviceId: string | undefined, satsStr?: string, inputJsonStr?: string): Promise<never>;
7
+ export declare function cmdRequestService(targetKey: string | undefined, serviceId: string | undefined, satsStr?: string, inputJsonStr?: string): Promise<any>;
@@ -4,8 +4,8 @@
4
4
  /**
5
5
  * Respond to a service request.
6
6
  */
7
- export declare function cmdRespondService(requestId: string | undefined, recipientKey: string | undefined, serviceId: string | undefined, resultJson: string | undefined): Promise<never>;
7
+ export declare function cmdRespondService(requestId: string | undefined, recipientKey: string | undefined, serviceId: string | undefined, resultJson: string | undefined): Promise<any>;
8
8
  /**
9
9
  * Respond to a research request with results.
10
10
  */
11
- export declare function cmdResearchRespond(resultJsonPath: string | undefined): Promise<never>;
11
+ export declare function cmdResearchRespond(resultJsonPath: string | undefined): Promise<any>;
@@ -4,7 +4,7 @@
4
4
  /**
5
5
  * Balance command: show wallet balance.
6
6
  */
7
- export declare function cmdBalance(): Promise<never>;
7
+ export declare function cmdBalance(): Promise<any>;
8
8
  /**
9
9
  * Import command: import external UTXO with merkle proof.
10
10
  *
@@ -15,7 +15,7 @@ export declare function cmdBalance(): Promise<never>;
15
15
  * If the transaction isn't yet on WoC (just broadcast), it will poll
16
16
  * with exponential backoff for up to 60 seconds.
17
17
  */
18
- export declare function cmdImport(txidArg: string | undefined, voutStr?: string): Promise<never>;
18
+ export declare function cmdImport(txidArg: string | undefined, voutStr?: string): Promise<any>;
19
19
  /**
20
20
  * Refund command: sweep wallet to an address.
21
21
  */
@@ -4,16 +4,16 @@
4
4
  /**
5
5
  * Setup command: create wallet and show identity.
6
6
  */
7
- export declare function cmdSetup(): Promise<never>;
7
+ export declare function cmdSetup(): Promise<any>;
8
8
  /**
9
9
  * Identity command: show identity public key.
10
10
  */
11
- export declare function cmdIdentity(): Promise<never>;
11
+ export declare function cmdIdentity(): Promise<any>;
12
12
  /**
13
13
  * Status command: show identity and balance.
14
14
  */
15
- export declare function cmdStatus(): Promise<never>;
15
+ export declare function cmdStatus(): Promise<any>;
16
16
  /**
17
17
  * Address command: show P2PKH receive address.
18
18
  */
19
- export declare function cmdAddress(): Promise<never>;
19
+ export declare function cmdAddress(): Promise<any>;
@@ -4,24 +4,24 @@
4
4
  /**
5
5
  * Start X verification: generate a tweet with identity key and signature.
6
6
  */
7
- export declare function cmdXVerifyStart(handleArg: string | undefined): Promise<never>;
7
+ export declare function cmdXVerifyStart(handleArg: string | undefined): Promise<any>;
8
8
  /**
9
9
  * Complete X verification by checking the posted tweet.
10
10
  */
11
- export declare function cmdXVerifyComplete(tweetUrl: string | undefined): Promise<never>;
11
+ export declare function cmdXVerifyComplete(tweetUrl: string | undefined): Promise<any>;
12
12
  /**
13
13
  * List verified X accounts (local cache).
14
14
  */
15
- export declare function cmdXVerifications(): Promise<never>;
15
+ export declare function cmdXVerifications(): Promise<any>;
16
16
  /**
17
17
  * Lookup X verifications from the overlay network.
18
18
  */
19
- export declare function cmdXLookup(query: string | undefined): Promise<never>;
19
+ export declare function cmdXLookup(query: string | undefined): Promise<any>;
20
20
  /**
21
21
  * List pending X engagement requests.
22
22
  */
23
- export declare function cmdXEngagementQueue(): Promise<never>;
23
+ export declare function cmdXEngagementQueue(): Promise<any>;
24
24
  /**
25
25
  * Mark an X engagement request as fulfilled.
26
26
  */
27
- export declare function cmdXEngagementFulfill(requestId: string | undefined, proofUrl?: string): Promise<never>;
27
+ export declare function cmdXEngagementFulfill(requestId: string | undefined, proofUrl?: string): Promise<any>;
package/index.ts CHANGED
@@ -1,41 +1,22 @@
1
- import { execFile as execFile_orig_node, spawn as spawn_orig_node, ChildProcess } from 'node:child_process';
2
- import { promisify } from 'node:util';
3
1
  import path from 'node:path';
4
2
  import os from 'node:os';
5
- import { fileURLToPath } from 'node:url';
6
3
  import fs from 'node:fs';
7
4
  import { initializeServiceSystem, serviceManager } from './src/services/index.js';
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
5
 
11
- const cp_name = 'node:child' + '_' + 'process';
12
- let execFile_orig: any;
13
- let spawn_orig: any;
14
- let execFileAsync: any;
15
-
16
- async function ensureCp() {
17
- if (execFileAsync) return;
18
- // @ts-ignore
19
- if (typeof require !== 'undefined') {
20
- // @ts-ignore
21
- const cp = require(cp_name);
22
- execFile_orig = cp.execFile;
23
- spawn_orig = cp.spawn;
24
- } else {
25
- const cp = await import(cp_name as any);
26
- execFile_orig = cp.execFile;
27
- spawn_orig = cp.spawn;
28
- }
29
- execFileAsync = promisify(execFile_orig);
30
- }
6
+ // Direct imports of command logic
7
+ import { cmdStatus, cmdSetup, cmdAddress, cmdIdentity } from './src/scripts/wallet/setup.js';
8
+ import { cmdBalance, cmdImport } from './src/scripts/wallet/balance.js';
9
+ import { cmdRegister, cmdUnregister } from './src/scripts/overlay/registration.js';
10
+ import { cmdDiscover } from './src/scripts/overlay/discover.js';
11
+ import { cmdRequestService } from './src/scripts/services/request.js';
12
+ import { cmdServiceQueue } from './src/scripts/services/queue.js';
13
+ import { cmdRespondService } from './src/scripts/services/respond.js';
14
+ import { setNoExit } from './src/scripts/output.js';
31
15
 
32
16
  // Track background process for proper lifecycle management
33
17
  let backgroundProcess: any = null;
34
18
  let serviceRunning = false;
35
19
 
36
- // Confirmation tokens for destructive actions — maps token → { action, details, expiresAt }
37
- const pendingConfirmations: Map<string, { action: string; details: any; expiresAt: number }> = new Map();
38
-
39
20
  // Auto-import tracking
40
21
  let autoImportInterval: any = null;
41
22
  let knownTxids: Set<string> = new Set();
@@ -47,7 +28,6 @@ let requestCleanupInterval: any = null;
47
28
  // Budget tracking
48
29
  const BUDGET_FILE = 'daily-spending.json';
49
30
 
50
-
51
31
  interface DailySpending {
52
32
  date: string; // YYYY-MM-DD
53
33
  totalSats: number;
@@ -65,19 +45,11 @@ function loadDailySpending(walletDir: string): DailySpending {
65
45
  const data = JSON.parse(fs.readFileSync(budgetPath, 'utf-8'));
66
46
  if (data.date === today) return data;
67
47
  } catch {
68
- // Ignore parse errors - return fresh daily spending for corrupted/missing file
48
+ // Ignore parse errors
69
49
  }
70
50
  return { date: today, totalSats: 0, transactions: [] };
71
51
  }
72
52
 
73
- function writeActivityEvent(event: any) {
74
- const alertDir = path.join((process as any)['env'].HOME || '', '.openclaw', 'openclaw-overlay');
75
- try {
76
- fs.mkdirSync(alertDir, { recursive: true });
77
- fs.appendFileSync(path.join(alertDir, 'activity-feed.jsonl'), JSON.stringify({ ...event, ts: Date.now() }) + '\n');
78
- } catch {}
79
- }
80
-
81
53
  function recordSpend(walletDir: string, sats: number, service: string, provider: string) {
82
54
  const spending = loadDailySpending(walletDir);
83
55
  spending.totalSats += sats;
@@ -95,18 +67,26 @@ function checkBudget(walletDir: string, requestedSats: number, dailyLimit: numbe
95
67
  };
96
68
  }
97
69
 
98
- async function startAutoImport(env: any, cliPath: string, api: any) {
99
- // Get our address
70
+ function applyConfigToEnv(config: any) {
71
+ (process as any)['env'].BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
72
+ (process as any)['env'].OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';
73
+ (process as any)['env'].BSV_NETWORK = config.network || (process as any)['env'].BSV_NETWORK || 'mainnet';
74
+ (process as any)['env'].BSV_ARC_URL = config.arcUrl || ((process as any)['env'].BSV_NETWORK === 'testnet' ? 'https://testnet.arc.gorillapool.io' : 'https://arc.gorillapool.io');
75
+ (process as any)['env'].AGENT_NAME = config.agentName || 'openclaw-agent';
76
+ setNoExit(true);
77
+ }
78
+
79
+ async function startAutoImport(config: any, api: any) {
100
80
  try {
101
- const addrResult = await execFileAsync('node', [cliPath, 'address'], { env });
102
- const addrOutput = parseCliOutput(addrResult.stdout);
81
+ applyConfigToEnv(config);
82
+ const addrOutput = await cmdAddress();
103
83
  if (!addrOutput.success) return;
104
84
  const address = addrOutput.data?.address;
105
85
  if (!address) return;
106
86
 
107
87
  autoImportInterval = setInterval(async () => {
108
88
  try {
109
- const network = env.BSV_NETWORK === 'testnet' ? 'test' : 'main';
89
+ const network = (process as any)['env'].BSV_NETWORK === 'testnet' ? 'test' : 'main';
110
90
  const controller = new AbortController();
111
91
  const timeout = setTimeout(() => controller.abort(), 15000);
112
92
  const resp = await fetch(`https://api.whatsonchain.com/v1/bsv/${network}/address/${address}/unspent/all`, { signal: controller.signal });
@@ -118,37 +98,29 @@ async function startAutoImport(env: any, cliPath: string, api: any) {
118
98
  for (const utxo of utxos) {
119
99
  const key = `${utxo.tx_hash}:${utxo.tx_pos}`;
120
100
  if (knownTxids.has(key)) continue;
121
- if (utxo.value < 200) continue; // skip dust
101
+ if (utxo.value < 200) continue;
122
102
 
123
103
  api.logger?.info?.(`[openclaw-overlay] Auto-importing UTXO: ${utxo.tx_hash}:${utxo.tx_pos} (${utxo.value} sats)`);
124
104
  try {
125
- const importResult = await execFileAsync('node', [cliPath, 'import', utxo.tx_hash, String(utxo.tx_pos)], { env });
126
- const importOutput = parseCliOutput(importResult.stdout);
105
+ applyConfigToEnv(config);
106
+ const importOutput = await cmdImport(utxo.tx_hash, String(utxo.tx_pos));
127
107
  if (importOutput.success) {
128
108
  knownTxids.add(key);
129
109
  api.logger?.info?.(`[openclaw-overlay] Auto-imported ${utxo.value} sats from ${utxo.tx_hash}`);
130
110
 
131
- // Clear onboarding flag since wallet is now funded
132
- try {
133
- const onboardingSentFile = path.join((process as any)['env'].HOME || '', '.openclaw', 'openclaw-overlay', 'onboarding-sent.flag');
134
- if (fs.existsSync(onboardingSentFile)) {
135
- fs.unlinkSync(onboardingSentFile);
136
- }
137
- } catch {}
138
-
139
111
  // Notify agent of successful import
140
112
  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' });
141
113
 
142
114
  // Check if registered, auto-register if not
143
115
  try {
144
- const regPath = path.join((process as any)['env'].HOME || '', '.openclaw', 'openclaw-overlay', 'registration.json');
116
+ const regPath = path.join(os.homedir(), '.openclaw', 'openclaw-overlay', 'registration.json');
145
117
  if (!fs.existsSync(regPath)) {
146
118
  api.logger?.info?.('[openclaw-overlay] Not yet registered — auto-registering...');
147
- const regResult = await execFileAsync('node', [cliPath, 'register'], { env, timeout: 60000 });
148
- const regOutput = parseCliOutput(regResult.stdout);
119
+ applyConfigToEnv(config);
120
+ const regOutput = await cmdRegister();
149
121
  if (regOutput.success) {
150
122
  api.logger?.info?.('[openclaw-overlay] Auto-registered on overlay network!');
151
- await autoAdvertiseServices(env, cliPath, api.logger);
123
+ await autoAdvertiseServices(config, api.logger);
152
124
  }
153
125
  }
154
126
  } catch (err: any) {
@@ -168,26 +140,12 @@ async function startAutoImport(env: any, cliPath: string, api: any) {
168
140
  }
169
141
  }
170
142
 
171
- function stopAutoImport() {
172
- if (autoImportInterval) {
173
- clearInterval(autoImportInterval);
174
- autoImportInterval = null;
175
- }
176
- }
177
-
178
- async function autoAdvertiseServices(env: any, cliPath: string, logger: any) {
143
+ async function autoAdvertiseServices(config: any, logger: any) {
179
144
  try {
180
- const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
181
- if (!fs.existsSync(configPath)) return;
182
-
183
145
  let servicesToAdvertise: string[] = [];
184
- try {
185
- const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
186
- const pluginConfig = config?.plugins?.entries?.['openclaw-overlay-plugin']?.config || config?.plugins?.entries?.['openclaw-overlay-plugin'];
187
- if (pluginConfig?.services && Array.isArray(pluginConfig.services)) {
188
- servicesToAdvertise = pluginConfig.services;
189
- }
190
- } catch {}
146
+ if (config?.services && Array.isArray(config.services)) {
147
+ servicesToAdvertise = config.services;
148
+ }
191
149
 
192
150
  if (servicesToAdvertise.length === 0) return;
193
151
 
@@ -195,9 +153,9 @@ async function autoAdvertiseServices(env: any, cliPath: string, logger: any) {
195
153
  const serviceInfo = serviceManager.registry.get(serviceId);
196
154
  if (!serviceInfo) continue;
197
155
  try {
198
- await execFileAsync('node', [
199
- cliPath, 'advertise', serviceId, serviceInfo.name, serviceInfo.description, String(serviceInfo.defaultPrice)
200
- ], { env, timeout: 60000 });
156
+ const { cmdAdvertise } = await import('./src/scripts/overlay/services.js');
157
+ applyConfigToEnv(config);
158
+ await cmdAdvertise(serviceId, serviceInfo.name, String(serviceInfo.defaultPrice), serviceInfo.description);
201
159
  } catch {}
202
160
  }
203
161
  } catch (err: any) {
@@ -208,7 +166,7 @@ async function autoAdvertiseServices(env: any, cliPath: string, logger: any) {
208
166
  function wakeAgent(text: string, logger: any, options: { sessionKey?: string } = {}) {
209
167
  const sessionKey = options.sessionKey || `hook:openclaw-overlay:${Date.now()}`;
210
168
  const gatewayPort = (process as any)['env'].OPENCLAW_GATEWAY_PORT || '18789';
211
- const httpToken = getHooksToken();
169
+ const httpToken = (process as any)['env'].OPENCLAW_HOOKS_TOKEN || null;
212
170
  if (!httpToken) return;
213
171
 
214
172
  fetch(`http://localhost:${gatewayPort}/hooks/agent`, {
@@ -218,32 +176,7 @@ function wakeAgent(text: string, logger: any, options: { sessionKey?: string } =
218
176
  }).catch(() => {});
219
177
  }
220
178
 
221
- function getHooksToken(): string | null {
222
- let token = (process as any)['env'].OPENCLAW_HOOKS_TOKEN || null;
223
- if (!token) {
224
- try {
225
- const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
226
- if (fs.existsSync(configPath)) {
227
- const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
228
- token = config.gateway?.hooksToken || null;
229
- }
230
- } catch {}
231
- }
232
- return token;
233
- }
234
-
235
- function categorizeEvent(event: any) {
236
- const base = { ts: Date.now(), from: event.from?.slice(0, 16), fullFrom: event.from };
237
- if (event.action === 'queued-for-agent' && event.satoshisReceived) {
238
- return { ...base, type: 'incoming_payment', emoji: '💰', serviceId: event.serviceId, sats: event.satoshisReceived, requestId: event.id, message: `Received ${event.satoshisReceived} sats for ${event.serviceId}` };
239
- }
240
- if (event.type === 'service-response' && event.action === 'received') {
241
- 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}` };
242
- }
243
- return null;
244
- }
245
-
246
- function startBackgroundService(env: any, cliPath: string, api: any) {
179
+ function startBackgroundService(config: any, api: any) {
247
180
  if (backgroundProcess) return;
248
181
  serviceRunning = true;
249
182
 
@@ -251,9 +184,18 @@ function startBackgroundService(env: any, cliPath: string, api: any) {
251
184
  if (serviceRunning) wokenRequests.clear();
252
185
  }, 5 * 60 * 1000);
253
186
 
254
- function spawnConnect() {
187
+ async function spawnConnect() {
255
188
  if (!serviceRunning) return;
256
- const proc = spawn_orig('node', [cliPath, 'connect'], { env, stdio: ['ignore', 'pipe', 'pipe'] });
189
+ const { spawn } = await import('node:child_process');
190
+ const base = __dirname.endsWith('dist') ? __dirname : path.join(__dirname, 'dist');
191
+ const cliPath = path.join(base, 'src', 'cli.js');
192
+
193
+ const env = { ...(process as any)['env'] };
194
+ env.BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
195
+ env.OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';
196
+ env.BSV_NETWORK = config.network || env.BSV_NETWORK || 'mainnet';
197
+
198
+ const proc = spawn('node', [cliPath, 'connect'], { env, stdio: ['ignore', 'pipe', 'pipe'] });
257
199
  backgroundProcess = proc;
258
200
 
259
201
  proc.stdout?.on('data', (data: any) => {
@@ -272,17 +214,11 @@ function startBackgroundService(env: any, cliPath: string, api: any) {
272
214
  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)}`;
273
215
  wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });
274
216
  }
275
- const notif = categorizeEvent(event);
276
- if (notif) {
277
- const dir = path.join((process as any)['env'].HOME || '', '.openclaw', 'openclaw-overlay');
278
- fs.mkdirSync(dir, { recursive: true });
279
- fs.appendFileSync(path.join(dir, 'activity-feed.jsonl'), JSON.stringify(notif) + '\n');
280
- }
281
217
  } catch {}
282
218
  }
283
219
  });
284
220
 
285
- proc.on('exit', (code: any) => {
221
+ proc.on('exit', () => {
286
222
  backgroundProcess = null;
287
223
  if (serviceRunning) setTimeout(spawnConnect, 5000);
288
224
  });
@@ -295,24 +231,10 @@ function stopBackgroundService() {
295
231
  if (backgroundProcess) { backgroundProcess.kill(); backgroundProcess = null; }
296
232
  if (requestCleanupInterval) { clearInterval(requestCleanupInterval); requestCleanupInterval = null; }
297
233
  wokenRequests.clear();
298
- stopAutoImport();
299
- }
300
-
301
- export async function activate(api: any) {
302
- return register(api);
303
- }
304
-
305
- let isInitialized = false;
306
-
307
- function getCliPath() {
308
- const base = __dirname.endsWith('dist') ? __dirname : path.join(__dirname, 'dist');
309
- return path.join(base, 'src', 'cli.js');
234
+ if (autoImportInterval) { clearInterval(autoImportInterval); autoImportInterval = null; }
310
235
  }
311
236
 
312
237
  export function register(api: any) {
313
- if (isInitialized) return;
314
- isInitialized = true;
315
-
316
238
  const entries = api.getConfig?.()?.plugins?.entries || {};
317
239
  const entry = entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};
318
240
  const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };
@@ -365,17 +287,9 @@ export function register(api: any) {
365
287
  api.registerService({
366
288
  id: "openclaw-overlay-relay",
367
289
  start: async () => {
368
- await ensureCp();
369
- // Initialize service system
370
- try {
371
- await initializeServiceSystem();
372
- } catch (err: any) {
373
- if (api.logger) api.logger.warn(`[overlay] Service system initialization failed: ${err.message}`);
374
- }
375
- const env = buildEnvironment(pluginConfig);
376
- const cliPath = getCliPath();
377
- startBackgroundService(env, cliPath, api);
378
- startAutoImport(env, cliPath, api);
290
+ try { await initializeServiceSystem(); } catch {}
291
+ startBackgroundService(pluginConfig, api);
292
+ startAutoImport(pluginConfig, api);
379
293
  },
380
294
  stop: () => stopBackgroundService()
381
295
  });
@@ -383,120 +297,79 @@ export function register(api: any) {
383
297
  // 4. CLI
384
298
  api.registerCli(({ program }: any) => {
385
299
  const overlay = program.command("overlay").description("BSV Overlay Network management");
386
- overlay.command("status").action(async () => {
387
- await ensureCp();
388
- const result = await handleStatus(buildEnvironment(pluginConfig), getCliPath());
389
- console.log(JSON.stringify(result, null, 2));
300
+ overlay.command("status").description("Show identity and balance").action(async () => {
301
+ applyConfigToEnv(pluginConfig);
302
+ const res = await cmdStatus();
303
+ console.log(JSON.stringify(res.data, null, 2));
390
304
  });
391
- overlay.command("balance").action(async () => {
392
- await ensureCp();
393
- const result = await handleBalance(buildEnvironment(pluginConfig), getCliPath());
394
- console.log(JSON.stringify(result, null, 2));
305
+ overlay.command("balance").description("Show current wallet balance").action(async () => {
306
+ applyConfigToEnv(pluginConfig);
307
+ const res = await cmdBalance();
308
+ console.log(JSON.stringify(res.data, null, 2));
309
+ });
310
+ 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: any) => {
311
+ applyConfigToEnv(pluginConfig);
312
+ const args: string[] = [];
313
+ if (options.service) args.push('--service', options.service);
314
+ if (options.agent) args.push('--agent', options.agent);
315
+ const res = await cmdDiscover(args);
316
+ console.log(JSON.stringify(res.data, null, 2));
395
317
  });
396
318
  }, { commands: ["overlay"] });
397
319
  }
398
320
 
399
- export const plugin = {
400
- id: "openclaw-overlay-plugin",
401
- name: "BSV Overlay Network",
402
- description: "OpenClaw Overlay — decentralized agent marketplace with BSV micropayments",
403
- activate: register,
404
- register: register
405
- };
406
-
407
- export default register;
408
-
409
321
  async function executeOverlayAction(params: any, config: any, api: any) {
410
- await ensureCp();
411
322
  const { action } = params;
412
- const env = buildEnvironment(config);
413
- const cliPath = getCliPath();
323
+ applyConfigToEnv(config);
414
324
 
415
325
  switch (action) {
416
- case "request": return await handleServiceRequest(params, env, cliPath, config, api);
417
- case "discover": return await handleDiscover(params, env, cliPath);
418
- case "balance": return await handleBalance(env, cliPath);
419
- case "status": return await handleStatus(env, cliPath);
420
- case "onboard": return await handleOnboard(params, env, cliPath);
421
- case "pending-requests": return await handlePendingRequests(env, cliPath);
422
- case "fulfill": return await handleFulfill(params, env, cliPath);
326
+ case "request": {
327
+ const { service, input } = params;
328
+ const walletDir = config?.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
329
+ const discoverOutput = await cmdDiscover(['--service', service]);
330
+ const providers = discoverOutput.data.services;
331
+ if (!providers || providers.length === 0) throw new Error(`No providers found for ${service}`);
332
+ providers.sort((a: any, b: any) => (a.pricing?.amountSats || 0) - (b.pricing?.amountSats || 0));
333
+ const best = providers[0];
334
+ const price = best.pricing?.amountSats || 0;
335
+ const budget = checkBudget(walletDir, price, config.dailyBudgetSats || 5000);
336
+ if (!budget.allowed) throw new Error("Budget exceeded");
337
+
338
+ const output = await cmdRequestService(best.identityKey, service, price.toString(), input ? JSON.stringify(input) : undefined);
339
+ recordSpend(walletDir, price, service, best.name);
340
+ return { status: "sent", requestId: output.data?.messageId, message: `Request sent to ${best.name} for ${price} sats.` };
341
+ }
342
+ case "discover": return (await cmdDiscover(params.service ? ['--service', params.service] : [])).data;
343
+ case "balance": return (await cmdBalance()).data;
344
+ case "status": {
345
+ const identity = await cmdIdentity();
346
+ const balance = await cmdBalance();
347
+ return { identity: identity.data, balance: balance.data };
348
+ }
349
+ case "onboard": {
350
+ await cmdSetup();
351
+ const addr = (await cmdAddress()).data.address;
352
+ const bal = (await cmdBalance()).data.walletBalance;
353
+ if (bal < 1000) return { funded: false, address: addr, message: "Please fund 1000 sats." };
354
+ await cmdRegister();
355
+ return { funded: true, registered: true, message: "Onboarding complete." };
356
+ }
357
+ case "pending-requests": return (await cmdServiceQueue()).data;
358
+ case "fulfill": {
359
+ const { requestId, recipientKey, serviceId, result } = params;
360
+ return (await cmdRespondService(requestId, recipientKey, serviceId, JSON.stringify(result))).data;
361
+ }
362
+ case "unregister": return (await cmdUnregister()).data;
423
363
  default: throw new Error(`Unknown action: ${action}`);
424
364
  }
425
365
  }
426
366
 
427
- async function handleServiceRequest(params: any, env: any, cliPath: string, config: any, api: any) {
428
- const { service, identityKey: targetKey, input } = params;
429
- const walletDir = config?.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
430
- const discoverResult = await execFileAsync('node', [cliPath, 'discover', '--service', service], { env });
431
- const discoverOutput = parseCliOutput(discoverResult.stdout);
432
- const providers = discoverOutput.data.services;
433
- if (!providers || providers.length === 0) throw new Error(`No providers found for ${service}`);
434
- providers.sort((a: any, b: any) => (a.pricing?.amountSats || 0) - (b.pricing?.amountSats || 0));
435
- const best = providers[0];
436
- const price = best.pricing?.amountSats || 0;
437
- const budget = checkBudget(walletDir, price, config.dailyBudgetSats || 5000);
438
- if (!budget.allowed) throw new Error("Budget exceeded");
439
- const requestArgs = [cliPath, 'request-service', best.identityKey, service, price.toString()];
440
- if (input) requestArgs.push(JSON.stringify(input));
441
- const res = await execFileAsync('node', requestArgs, { env });
442
- const output = parseCliOutput(res.stdout);
443
- recordSpend(walletDir, price, service, best.name);
444
- return { status: "sent", requestId: output.data?.messageId, message: `Request sent to ${best.name} for ${price} sats.` };
445
- }
446
-
447
- async function handleDiscover(params: any, env: any, cliPath: string) {
448
- const args = [cliPath, 'discover'];
449
- if (params.service) args.push('--service', params.service);
450
- const result = await execFileAsync('node', args, { env });
451
- return parseCliOutput(result.stdout).data;
452
- }
453
-
454
- async function handleBalance(env: any, cliPath: string) {
455
- const result = await execFileAsync('node', [cliPath, 'balance'], { env });
456
- return parseCliOutput(result.stdout).data;
457
- }
458
-
459
- async function handleStatus(env: any, cliPath: string) {
460
- const identity = parseCliOutput((await execFileAsync('node', [cliPath, 'identity'], { env })).stdout);
461
- const balance = parseCliOutput((await execFileAsync('node', [cliPath, 'balance'], { env })).stdout);
462
- return { identity: identity.data, balance: balance.data };
463
- }
464
-
465
- async function handleOnboard(params: any, env: any, cliPath: string) {
466
- await execFileAsync('node', [cliPath, 'setup'], { env });
467
- const addr = parseCliOutput((await execFileAsync('node', [cliPath, 'address'], { env })).stdout).data.address;
468
- const bal = parseCliOutput((await execFileAsync('node', [cliPath, 'balance'], { env })).stdout).data.walletBalance;
469
- if (bal < 1000) return { funded: false, address: addr, message: "Please fund 1000 sats." };
470
- await execFileAsync('node', [cliPath, 'register'], { env });
471
- return { funded: true, registered: true, message: "Onboarding complete." };
472
- }
473
-
474
- async function handlePendingRequests(env: any, cliPath: string) {
475
- const result = await execFileAsync('node', [cliPath, 'service-queue'], { env });
476
- return parseCliOutput(result.stdout).data;
477
- }
478
-
479
- async function handleFulfill(params: any, env: any, cliPath: string) {
480
- const { requestId, recipientKey, serviceId, result } = params;
481
- const res = await execFileAsync('node', [cliPath, 'respond-service', requestId, recipientKey, serviceId, JSON.stringify(result)], { env });
482
- return parseCliOutput(res.stdout).data;
483
- }
484
-
485
- function buildEnvironment(config: any) {
486
- const env = { ...(process as any)['env'] };
487
- env.BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');
488
- env.OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';
489
- env.BSV_NETWORK = config.network || env.BSV_NETWORK || 'mainnet';
490
- env.BSV_ARC_URL = config.arcUrl || (env.BSV_NETWORK === 'testnet' ? 'https://testnet.arc.gorillapool.io' : 'https://arc.gorillapool.io');
491
- env.AGENT_NAME = config.agentName || 'openclaw-agent';
492
- return env;
493
- }
367
+ export const plugin = {
368
+ id: "openclaw-overlay-plugin",
369
+ name: "BSV Overlay Network",
370
+ description: "OpenClaw Overlay decentralized agent marketplace with BSV micropayments",
371
+ activate: register,
372
+ register: register
373
+ };
494
374
 
495
- function parseCliOutput(stdout: any) {
496
- try {
497
- const str = typeof stdout === 'string' ? stdout : String(stdout || '');
498
- return JSON.parse(str.trim());
499
- } catch (error: any) {
500
- throw new Error(`Failed to parse CLI output: ${error.message}`);
501
- }
502
- }
375
+ export default register;
@@ -2,7 +2,7 @@
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.8.4",
5
+ "version": "0.8.6",
6
6
  "skills": [
7
7
  "./SKILL.md"
8
8
  ],
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "configSchema": {
37
37
  "type": "object",
38
- "additionalProperties": false,
38
+ "additionalProperties": true,
39
39
  "properties": {
40
40
  "overlayUrl": {
41
41
  "type": "string",