openclaw-overlay-plugin 0.7.51 → 0.7.53

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/dist/index.js CHANGED
@@ -7,17 +7,6 @@ import fs from 'node:fs';
7
7
  import { initializeServiceSystem, serviceManager } from './src/services/index.js';
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
10
- /**
11
- * Helper to correctly locate the CLI script.
12
- * If running from dist/index.js, CLI is in ./src/cli.js
13
- * If running from index.ts, CLI is in ./dist/src/cli.js
14
- */
15
- function getCliPath() {
16
- const localCli = path.join(__dirname, 'src', 'cli.js');
17
- if (fs.existsSync(localCli))
18
- return localCli;
19
- return path.join(__dirname, 'dist', 'src', 'cli.js');
20
- }
21
10
  const execFileAsync = promisify(execFile);
22
11
  // Track background process for proper lifecycle management
23
12
  let backgroundProcess = null;
@@ -173,7 +162,7 @@ async function autoAdvertiseServices(env, cliPath, logger) {
173
162
  continue;
174
163
  try {
175
164
  const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
176
- const pluginConfig = config?.plugins?.entries?.['openclaw-overlay']?.config;
165
+ const pluginConfig = config?.plugins?.entries?.['openclaw-overlay-plugin']?.config || config?.plugins?.entries?.['openclaw-overlay-plugin'];
177
166
  if (pluginConfig?.services && Array.isArray(pluginConfig.services)) {
178
167
  servicesToAdvertise = pluginConfig.services;
179
168
  break;
@@ -419,18 +408,16 @@ export async function activate(api) {
419
408
  return register(api);
420
409
  }
421
410
  let isInitialized = false;
411
+ function getCliPath() {
412
+ return path.join(__dirname, 'dist', 'src', 'cli.js');
413
+ }
422
414
  export default function register(api) {
423
415
  if (isInitialized)
424
416
  return;
425
417
  isInitialized = true;
426
418
  // Capture config at registration time
427
- // Check all possible IDs for backward compatibility and various installation methods
428
419
  const entries = api.getConfig?.()?.plugins?.entries || {};
429
- const entry = entries['openclaw-overlay']
430
- || entries['openclaw-overlay-plugin']
431
- || entries['bsv-overlay']
432
- || {};
433
- // Merge: gateway-level api.config > nested entry.config > flat entry properties
420
+ const entry = entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || entries['bsv-overlay-skill'] || {};
434
421
  const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };
435
422
  // Register the overlay agent tool
436
423
  api.registerTool({
@@ -563,6 +550,44 @@ export default function register(api) {
563
550
  }
564
551
  }
565
552
  });
553
+ // Register the /overlay slash command for direct chat interaction (Autoreply)
554
+ api.registerCommand({
555
+ name: "overlay",
556
+ description: "BSV Overlay Marketplace commands",
557
+ acceptsArgs: true,
558
+ requireAuth: true,
559
+ handler: async (ctx) => {
560
+ try {
561
+ const args = ctx.args || [];
562
+ const action = args[0] || 'status';
563
+ // Map slash command args to tool params
564
+ const params = { action };
565
+ if (action === 'request' && args[1])
566
+ params.service = args[1];
567
+ if (action === 'discover' && args[1])
568
+ params.service = args[1];
569
+ if (action === 'pay' && args[1])
570
+ params.identityKey = args[1];
571
+ if (action === 'pay' && args[2])
572
+ params.sats = parseInt(args[2], 10);
573
+ const result = await executeOverlayAction(params, pluginConfig, api);
574
+ let text = `**Overlay: ${action.toUpperCase()}**\n\n`;
575
+ if (typeof result === 'string') {
576
+ text += result;
577
+ }
578
+ else if (result.message) {
579
+ text += result.message;
580
+ }
581
+ else {
582
+ text += JSON.stringify(result, null, 2);
583
+ }
584
+ return { text };
585
+ }
586
+ catch (error) {
587
+ return { text: `❌ Overlay Error: ${error.message}` };
588
+ }
589
+ }
590
+ });
566
591
  // Register background relay service
567
592
  api.registerService({
568
593
  id: "openclaw-overlay-relay",
@@ -571,13 +596,11 @@ export default function register(api) {
571
596
  api.logger.info("Starting BSV overlay WebSocket relay...");
572
597
  const env = buildEnvironment(pluginConfig);
573
598
  const cliPath = getCliPath();
574
- // Initialize the service system if available
575
599
  try {
576
600
  await initializeServiceSystem();
577
601
  }
578
602
  catch { }
579
603
  startBackgroundService(env, cliPath, api.logger);
580
- // Start auto-import
581
604
  startAutoImport(env, cliPath, api.logger);
582
605
  // Auto-check for registration on startup
583
606
  (async () => {
@@ -587,11 +610,9 @@ export default function register(api) {
587
610
  if (!fs.existsSync(regPath) && !fs.existsSync(onboardSentFile)) {
588
611
  const walletPath = path.join(pluginConfig.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet'), 'wallet-identity.json');
589
612
  if (fs.existsSync(walletPath)) {
590
- // Wallet exists but not registered.
591
613
  const balResult = await execFileAsync('node', [cliPath, 'balance'], { env });
592
614
  const balance = parseCliOutput(balResult.stdout);
593
615
  if ((balance.data?.walletBalance || 0) >= 1000) {
594
- // Funded but not registered. Auto-register.
595
616
  const regResult = await execFileAsync('node', [cliPath, 'register'], { env, timeout: 60000 });
596
617
  if (parseCliOutput(regResult.stdout).success) {
597
618
  api.logger.info('[openclaw-overlay] Agent auto-registered on startup');
@@ -734,7 +755,7 @@ export default function register(api) {
734
755
  console.error("Error:", error.message);
735
756
  }
736
757
  });
737
- });
758
+ }, { commands: ["overlay"] });
738
759
  }
739
760
  async function executeOverlayAction(params, config, api) {
740
761
  const { action } = params;
@@ -801,7 +822,6 @@ async function handleServiceRequest(params, env, cliPath, config, api) {
801
822
  if (!discoverOutput.success) {
802
823
  throw new Error(`Discovery failed: ${discoverOutput.error}`);
803
824
  }
804
- // FIX: Use discoverOutput.data.services instead of treating data as flat array
805
825
  const providers = discoverOutput.data.services;
806
826
  if (!providers || providers.length === 0) {
807
827
  throw new Error(`No providers found for service: ${service}`);
@@ -822,7 +842,7 @@ async function handleServiceRequest(params, env, cliPath, config, api) {
822
842
  }
823
843
  externalProviders = targeted;
824
844
  }
825
- // 3. Sort by price - FIX: Use pricing.amountSats instead of pricingSats
845
+ // 3. Sort by price
826
846
  externalProviders.sort((a, b) => (a.pricing?.amountSats || 0) - (b.pricing?.amountSats || 0));
827
847
  const bestProvider = externalProviders[0];
828
848
  const price = bestProvider.pricing?.amountSats || 0;
@@ -849,10 +869,6 @@ async function handleServiceRequest(params, env, cliPath, config, api) {
849
869
  if (!requestOutput.success) {
850
870
  throw new Error(`Service request failed: ${requestOutput.error}`);
851
871
  }
852
- // 7. Return immediately — no polling.
853
- // The WebSocket background service handles incoming responses
854
- // asynchronously and wakes the agent via /hooks/agent when a
855
- // response arrives. This avoids blocking for up to 120s.
856
872
  recordSpend(walletDir, price, service, bestProvider.name);
857
873
  writeActivityEvent({ type: 'outgoing_payment', emoji: '💸', sats: price, service, provider: bestProvider.name, message: `Paid ${price} sats to ${bestProvider.name} for ${service}` });
858
874
  return {
@@ -1149,7 +1165,7 @@ async function handleImport(params, env, cliPath) {
1149
1165
  // Check if we should auto-register after successful import
1150
1166
  const regPath = path.join(process.env.HOME || '', '.openclaw', 'openclaw-overlay', 'registration.json');
1151
1167
  const isRegistered = fs.existsSync(regPath);
1152
- if (!isRegistered && output.data?.balance >= 1000) {
1168
+ if (!isRegistered && (output.data?.balance || 0) >= 1000) {
1153
1169
  // Auto-register immediately after funding
1154
1170
  try {
1155
1171
  const regResult = await execFileAsync('node', [cliPath, 'register'], { env, timeout: 60000 });
@@ -1281,13 +1297,11 @@ async function handleRefund(params, env, cliPath) {
1281
1297
  async function handleOnboard(params, env, cliPath) {
1282
1298
  const { agentName, agentDescription } = params;
1283
1299
  const steps = [];
1284
- // Apply agent name/description to env if provided
1285
1300
  const onboardEnv = { ...env };
1286
1301
  if (agentName)
1287
1302
  onboardEnv.AGENT_NAME = agentName;
1288
1303
  if (agentDescription)
1289
1304
  onboardEnv.AGENT_DESCRIPTION = agentDescription;
1290
- // Step 1: Setup wallet
1291
1305
  try {
1292
1306
  const setup = await execFileAsync('node', [cliPath, 'setup'], { env: onboardEnv });
1293
1307
  const setupOutput = parseCliOutput(setup.stdout);
@@ -1297,7 +1311,6 @@ async function handleOnboard(params, env, cliPath) {
1297
1311
  steps.push({ step: 'setup', success: false, error: err.message });
1298
1312
  return { steps, nextStep: 'Fix wallet setup error and try again' };
1299
1313
  }
1300
- // Step 2: Get address
1301
1314
  try {
1302
1315
  const addr = await execFileAsync('node', [cliPath, 'address'], { env: onboardEnv });
1303
1316
  const addrOutput = parseCliOutput(addr.stdout);
@@ -1306,7 +1319,6 @@ async function handleOnboard(params, env, cliPath) {
1306
1319
  catch (err) {
1307
1320
  steps.push({ step: 'address', success: false, error: err.message });
1308
1321
  }
1309
- // Step 3: Check balance
1310
1322
  try {
1311
1323
  const bal = await execFileAsync('node', [cliPath, 'balance'], { env: onboardEnv });
1312
1324
  const balOutput = parseCliOutput(bal.stdout);
@@ -1323,7 +1335,6 @@ async function handleOnboard(params, env, cliPath) {
1323
1335
  catch (err) {
1324
1336
  steps.push({ step: 'balance', success: false, error: err.message });
1325
1337
  }
1326
- // Step 4: Register
1327
1338
  try {
1328
1339
  const reg = await execFileAsync('node', [cliPath, 'register'], { env: onboardEnv, timeout: 60000 });
1329
1340
  const regOutput = parseCliOutput(reg.stdout);
@@ -1350,7 +1361,6 @@ async function handleOnboard(params, env, cliPath) {
1350
1361
  };
1351
1362
  }
1352
1363
  async function handlePendingRequests(env, cliPath) {
1353
- // Clean up old queue entries before checking pending requests
1354
1364
  try {
1355
1365
  const { cleanupServiceQueue } = await import('./src/scripts/utils/storage.js');
1356
1366
  cleanupServiceQueue();
@@ -1362,7 +1372,6 @@ async function handlePendingRequests(env, cliPath) {
1362
1372
  const output = parseCliOutput(result.stdout);
1363
1373
  if (!output.success)
1364
1374
  throw new Error(`Queue check failed: ${output.error}`);
1365
- // Clear the alert file since we're checking now
1366
1375
  const alertPath = path.join(process.env.HOME || '', '.openclaw', 'openclaw-overlay', 'pending-alert.jsonl');
1367
1376
  try {
1368
1377
  if (fs.existsSync(alertPath))
@@ -1382,7 +1391,6 @@ function handleActivity() {
1382
1391
  catch {
1383
1392
  return null;
1384
1393
  } }).filter(Boolean);
1385
- // Clear the feed after reading
1386
1394
  fs.writeFileSync(feedPath, '');
1387
1395
  return { events, count: events.length };
1388
1396
  }
@@ -1397,7 +1405,6 @@ async function handleFulfill(params, env, cliPath) {
1397
1405
  const output = parseCliOutput(cliResult.stdout);
1398
1406
  if (!output.success)
1399
1407
  throw new Error(`Fulfill failed: ${output.error}`);
1400
- // Clean up the request ID from tracking since it's now fulfilled
1401
1408
  wokenRequests.delete(requestId);
1402
1409
  writeActivityEvent({ type: 'service_fulfilled', emoji: '✅', serviceId, recipientKey: recipientKey?.slice(0, 16), message: `Fulfilled ${serviceId} request — response sent` });
1403
1410
  return output.data;
@@ -1419,7 +1426,6 @@ function buildEnvironment(config) {
1419
1426
  if (config.arcUrl) {
1420
1427
  env.BSV_ARC_URL = config.arcUrl;
1421
1428
  }
1422
- // Set defaults
1423
1429
  env.BSV_NETWORK = env.BSV_NETWORK || 'mainnet';
1424
1430
  if (!env.BSV_ARC_URL) {
1425
1431
  env.BSV_ARC_URL = env.BSV_NETWORK === 'testnet'
@@ -1438,7 +1444,7 @@ function buildEnvironment(config) {
1438
1444
  else if (!env.AGENT_DESCRIPTION) {
1439
1445
  env.AGENT_DESCRIPTION = 'AI agent on the OpenClaw Overlay Network. Offers services for BSV micropayments.';
1440
1446
  }
1441
- env.AGENT_ROUTED = 'true'; // Route service requests through the agent
1447
+ env.AGENT_ROUTED = 'true';
1442
1448
  return env;
1443
1449
  }
1444
1450
  function parseCliOutput(stdout) {
@@ -1450,4 +1456,3 @@ function parseCliOutput(stdout) {
1450
1456
  throw new Error(`Failed to parse CLI output: ${error.message}`);
1451
1457
  }
1452
1458
  }
1453
- // sleep() removed — no longer needed since polling loop was removed
package/index.ts CHANGED
@@ -8,17 +8,6 @@ import { initializeServiceSystem, serviceManager } from './src/services/index.js
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
10
10
 
11
- /**
12
- * Helper to correctly locate the CLI script.
13
- * If running from dist/index.js, CLI is in ./src/cli.js
14
- * If running from index.ts, CLI is in ./dist/src/cli.js
15
- */
16
- function getCliPath(): string {
17
- const localCli = path.join(__dirname, 'src', 'cli.js');
18
- if (fs.existsSync(localCli)) return localCli;
19
- return path.join(__dirname, 'dist', 'src', 'cli.js');
20
- }
21
-
22
11
  const execFileAsync = promisify(execFile);
23
12
 
24
13
  // Track background process for proper lifecycle management
@@ -190,7 +179,7 @@ async function autoAdvertiseServices(env: any, cliPath: string, logger: any) {
190
179
  if (!fs.existsSync(configPath)) continue;
191
180
  try {
192
181
  const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
193
- const pluginConfig = config?.plugins?.entries?.['openclaw-overlay']?.config;
182
+ const pluginConfig = config?.plugins?.entries?.['openclaw-overlay-plugin']?.config || config?.plugins?.entries?.['openclaw-overlay-plugin'];
194
183
  if (pluginConfig?.services && Array.isArray(pluginConfig.services)) {
195
184
  servicesToAdvertise = pluginConfig.services;
196
185
  break;
@@ -464,19 +453,17 @@ export async function activate(api: any) {
464
453
 
465
454
  let isInitialized = false;
466
455
 
456
+ function getCliPath() {
457
+ return path.join(__dirname, 'dist', 'src', 'cli.js');
458
+ }
459
+
467
460
  export default function register(api: any) {
468
461
  if (isInitialized) return;
469
462
  isInitialized = true;
470
463
 
471
464
  // Capture config at registration time
472
- // Check all possible IDs for backward compatibility and various installation methods
473
465
  const entries = api.getConfig?.()?.plugins?.entries || {};
474
- const entry = entries['openclaw-overlay']
475
- || entries['openclaw-overlay-plugin']
476
- || entries['bsv-overlay']
477
- || {};
478
-
479
- // Merge: gateway-level api.config > nested entry.config > flat entry properties
466
+ const entry = entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || entries['bsv-overlay-skill'] || {};
480
467
  const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };
481
468
 
482
469
  // Register the overlay agent tool
@@ -610,6 +597,43 @@ export default function register(api: any) {
610
597
  }
611
598
  });
612
599
 
600
+ // Register the /overlay slash command for direct chat interaction (Autoreply)
601
+ api.registerCommand({
602
+ name: "overlay",
603
+ description: "BSV Overlay Marketplace commands",
604
+ acceptsArgs: true,
605
+ requireAuth: true,
606
+ handler: async (ctx: any) => {
607
+ try {
608
+ const args = ctx.args || [];
609
+ const action = args[0] || 'status';
610
+
611
+ // Map slash command args to tool params
612
+ const params: any = { action };
613
+
614
+ if (action === 'request' && args[1]) params.service = args[1];
615
+ if (action === 'discover' && args[1]) params.service = args[1];
616
+ if (action === 'pay' && args[1]) params.identityKey = args[1];
617
+ if (action === 'pay' && args[2]) params.sats = parseInt(args[2], 10);
618
+
619
+ const result = await executeOverlayAction(params, pluginConfig, api);
620
+
621
+ let text = `**Overlay: ${action.toUpperCase()}**\n\n`;
622
+ if (typeof result === 'string') {
623
+ text += result;
624
+ } else if (result.message) {
625
+ text += result.message;
626
+ } else {
627
+ text += JSON.stringify(result, null, 2);
628
+ }
629
+
630
+ return { text };
631
+ } catch (error: any) {
632
+ return { text: `❌ Overlay Error: ${error.message}` };
633
+ }
634
+ }
635
+ });
636
+
613
637
  // Register background relay service
614
638
  api.registerService({
615
639
  id: "openclaw-overlay-relay",
@@ -619,14 +643,11 @@ export default function register(api: any) {
619
643
  const env = buildEnvironment(pluginConfig);
620
644
  const cliPath = getCliPath();
621
645
 
622
- // Initialize the service system if available
623
646
  try {
624
647
  await initializeServiceSystem();
625
648
  } catch {}
626
649
 
627
650
  startBackgroundService(env, cliPath, api.logger);
628
-
629
- // Start auto-import
630
651
  startAutoImport(env, cliPath, api.logger);
631
652
 
632
653
  // Auto-check for registration on startup
@@ -639,12 +660,10 @@ export default function register(api: any) {
639
660
  const walletPath = path.join(pluginConfig.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet'), 'wallet-identity.json');
640
661
 
641
662
  if (fs.existsSync(walletPath)) {
642
- // Wallet exists but not registered.
643
663
  const balResult = await execFileAsync('node', [cliPath, 'balance'], { env });
644
664
  const balance = parseCliOutput(balResult.stdout);
645
665
 
646
666
  if ((balance.data?.walletBalance || 0) >= 1000) {
647
- // Funded but not registered. Auto-register.
648
667
  const regResult = await execFileAsync('node', [cliPath, 'register'], { env, timeout: 60000 });
649
668
  if (parseCliOutput(regResult.stdout).success) {
650
669
  api.logger.info('[openclaw-overlay] Agent auto-registered on startup');
@@ -789,7 +808,7 @@ export default function register(api: any) {
789
808
  console.error("Error:", error.message);
790
809
  }
791
810
  });
792
- });
811
+ }, { commands: ["overlay"] });
793
812
  }
794
813
 
795
814
  async function executeOverlayAction(params: any, config: any, api: any) {
@@ -885,7 +904,6 @@ async function handleServiceRequest(params: any, env: any, cliPath: string, conf
885
904
  throw new Error(`Discovery failed: ${discoverOutput.error}`);
886
905
  }
887
906
 
888
- // FIX: Use discoverOutput.data.services instead of treating data as flat array
889
907
  const providers = discoverOutput.data.services;
890
908
  if (!providers || providers.length === 0) {
891
909
  throw new Error(`No providers found for service: ${service}`);
@@ -910,7 +928,7 @@ async function handleServiceRequest(params: any, env: any, cliPath: string, conf
910
928
  externalProviders = targeted;
911
929
  }
912
930
 
913
- // 3. Sort by price - FIX: Use pricing.amountSats instead of pricingSats
931
+ // 3. Sort by price
914
932
  externalProviders.sort((a: any, b: any) => (a.pricing?.amountSats || 0) - (b.pricing?.amountSats || 0));
915
933
 
916
934
  const bestProvider = externalProviders[0];
@@ -946,10 +964,6 @@ async function handleServiceRequest(params: any, env: any, cliPath: string, conf
946
964
  throw new Error(`Service request failed: ${requestOutput.error}`);
947
965
  }
948
966
 
949
- // 7. Return immediately — no polling.
950
- // The WebSocket background service handles incoming responses
951
- // asynchronously and wakes the agent via /hooks/agent when a
952
- // response arrives. This avoids blocking for up to 120s.
953
967
  recordSpend(walletDir, price, service, bestProvider.name);
954
968
  writeActivityEvent({ type: 'outgoing_payment', emoji: '💸', sats: price, service, provider: bestProvider.name, message: `Paid ${price} sats to ${bestProvider.name} for ${service}` });
955
969
 
@@ -1294,7 +1308,7 @@ async function handleImport(params: any, env: any, cliPath: string) {
1294
1308
  const regPath = path.join(process.env.HOME || '', '.openclaw', 'openclaw-overlay', 'registration.json');
1295
1309
  const isRegistered = fs.existsSync(regPath);
1296
1310
 
1297
- if (!isRegistered && output.data?.balance >= 1000) {
1311
+ if (!isRegistered && (output.data?.balance || 0) >= 1000) {
1298
1312
  // Auto-register immediately after funding
1299
1313
  try {
1300
1314
  const regResult = await execFileAsync('node', [cliPath, 'register'], { env, timeout: 60000 });
@@ -1464,12 +1478,10 @@ async function handleOnboard(params: any, env: any, cliPath: string) {
1464
1478
  const { agentName, agentDescription } = params;
1465
1479
  const steps = [];
1466
1480
 
1467
- // Apply agent name/description to env if provided
1468
1481
  const onboardEnv = { ...env };
1469
1482
  if (agentName) onboardEnv.AGENT_NAME = agentName;
1470
1483
  if (agentDescription) onboardEnv.AGENT_DESCRIPTION = agentDescription;
1471
1484
 
1472
- // Step 1: Setup wallet
1473
1485
  try {
1474
1486
  const setup = await execFileAsync('node', [cliPath, 'setup'], { env: onboardEnv });
1475
1487
  const setupOutput = parseCliOutput(setup.stdout);
@@ -1479,7 +1491,6 @@ async function handleOnboard(params: any, env: any, cliPath: string) {
1479
1491
  return { steps, nextStep: 'Fix wallet setup error and try again' };
1480
1492
  }
1481
1493
 
1482
- // Step 2: Get address
1483
1494
  try {
1484
1495
  const addr = await execFileAsync('node', [cliPath, 'address'], { env: onboardEnv });
1485
1496
  const addrOutput = parseCliOutput(addr.stdout);
@@ -1488,7 +1499,6 @@ async function handleOnboard(params: any, env: any, cliPath: string) {
1488
1499
  steps.push({ step: 'address', success: false, error: err.message });
1489
1500
  }
1490
1501
 
1491
- // Step 3: Check balance
1492
1502
  try {
1493
1503
  const bal = await execFileAsync('node', [cliPath, 'balance'], { env: onboardEnv });
1494
1504
  const balOutput = parseCliOutput(bal.stdout);
@@ -1506,7 +1516,6 @@ async function handleOnboard(params: any, env: any, cliPath: string) {
1506
1516
  steps.push({ step: 'balance', success: false, error: err.message });
1507
1517
  }
1508
1518
 
1509
- // Step 4: Register
1510
1519
  try {
1511
1520
  const reg = await execFileAsync('node', [cliPath, 'register'], { env: onboardEnv, timeout: 60000 });
1512
1521
  const regOutput = parseCliOutput(reg.stdout);
@@ -1534,7 +1543,6 @@ async function handleOnboard(params: any, env: any, cliPath: string) {
1534
1543
  }
1535
1544
 
1536
1545
  async function handlePendingRequests(env: any, cliPath: string) {
1537
- // Clean up old queue entries before checking pending requests
1538
1546
  try {
1539
1547
  const { cleanupServiceQueue } = await import('./src/scripts/utils/storage.js');
1540
1548
  cleanupServiceQueue();
@@ -1546,7 +1554,6 @@ async function handlePendingRequests(env: any, cliPath: string) {
1546
1554
  const output = parseCliOutput(result.stdout);
1547
1555
  if (!output.success) throw new Error(`Queue check failed: ${output.error}`);
1548
1556
 
1549
- // Clear the alert file since we're checking now
1550
1557
  const alertPath = path.join(process.env.HOME || '', '.openclaw', 'openclaw-overlay', 'pending-alert.jsonl');
1551
1558
  try { if (fs.existsSync(alertPath)) fs.unlinkSync(alertPath); } catch {}
1552
1559
 
@@ -1560,7 +1567,6 @@ function handleActivity() {
1560
1567
  const lines = fs.readFileSync(feedPath, 'utf-8')?.trim().split('\n').filter(Boolean);
1561
1568
  const events = lines.map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
1562
1569
 
1563
- // Clear the feed after reading
1564
1570
  fs.writeFileSync(feedPath, '');
1565
1571
 
1566
1572
  return { events, count: events.length };
@@ -1578,7 +1584,6 @@ async function handleFulfill(params: any, env: any, cliPath: string) {
1578
1584
  const output = parseCliOutput(cliResult.stdout);
1579
1585
  if (!output.success) throw new Error(`Fulfill failed: ${output.error}`);
1580
1586
 
1581
- // Clean up the request ID from tracking since it's now fulfilled
1582
1587
  wokenRequests.delete(requestId);
1583
1588
 
1584
1589
  writeActivityEvent({ type: 'service_fulfilled', emoji: '✅', serviceId, recipientKey: recipientKey?.slice(0, 16), message: `Fulfilled ${serviceId} request — response sent` });
@@ -1604,7 +1609,6 @@ function buildEnvironment(config: any) {
1604
1609
  env.BSV_ARC_URL = config.arcUrl;
1605
1610
  }
1606
1611
 
1607
- // Set defaults
1608
1612
  env.BSV_NETWORK = env.BSV_NETWORK || 'mainnet';
1609
1613
 
1610
1614
  if (!env.BSV_ARC_URL) {
@@ -1622,7 +1626,7 @@ function buildEnvironment(config: any) {
1622
1626
  } else if (!env.AGENT_DESCRIPTION) {
1623
1627
  env.AGENT_DESCRIPTION = 'AI agent on the OpenClaw Overlay Network. Offers services for BSV micropayments.';
1624
1628
  }
1625
- env.AGENT_ROUTED = 'true'; // Route service requests through the agent
1629
+ env.AGENT_ROUTED = 'true';
1626
1630
 
1627
1631
  return env;
1628
1632
  }
@@ -1635,5 +1639,3 @@ function parseCliOutput(stdout: any) {
1635
1639
  throw new Error(`Failed to parse CLI output: ${error.message}`);
1636
1640
  }
1637
1641
  }
1638
-
1639
- // sleep() removed — no longer needed since polling loop was removed
@@ -6,9 +6,16 @@
6
6
  "skills": [
7
7
  "./SKILL.md"
8
8
  ],
9
+ "commands": [
10
+ {
11
+ "name": "overlay",
12
+ "description": "BSV Overlay Network management",
13
+ "isAutoreply": true
14
+ }
15
+ ],
9
16
  "configSchema": {
10
17
  "type": "object",
11
- "additionalProperties": false,
18
+ "additionalProperties": true,
12
19
  "properties": {
13
20
  "overlayUrl": {
14
21
  "type": "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-overlay-plugin",
3
- "version": "0.7.51",
3
+ "version": "0.7.53",
4
4
  "description": "Openclaw BSV Overlay — agent discovery, service marketplace, and micropayments on the BSV blockchain",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -12,7 +12,6 @@
12
12
  "src/",
13
13
  "dist/",
14
14
  "SKILL.md",
15
- "HOOK.md",
16
15
  "README.md"
17
16
  ],
18
17
  "bin": {
package/HOOK.md DELETED
@@ -1,9 +0,0 @@
1
- ---
2
- name: openclaw-overlay-hooks
3
- description: Background automation for the OpenClaw Overlay Network.
4
- metadata: '{"openclaw": {"events": ["gateway:start"]}}'
5
- ---
6
-
7
- # Overlay Hooks
8
- This file ensures compatibility with the OpenClaw hook pack installer.
9
- Background tasks are handled programmatically in index.ts.
@@ -1,225 +0,0 @@
1
- /**
2
- * @a2a-bsv/core — BSVAgentWallet
3
- *
4
- * High-level wallet class for AI agent-to-agent BSV payments.
5
- * Wraps @bsv/wallet-toolbox's Wallet + StorageKnex with a clean,
6
- * minimal API surface designed for automated agent use.
7
- */
8
- import { PrivateKey, CachedKeyDeriver } from '@bsv/sdk';
9
- import { Wallet, WalletStorageManager, Services, Monitor, StorageKnex, randomBytesHex, ChaintracksServiceClient, } from '@bsv/wallet-toolbox';
10
- import knexLib from 'knex';
11
- import * as path from 'node:path';
12
- import * as fs from 'node:fs';
13
- import { toChain, DEFAULT_TAAL_API_KEYS, DEFAULT_DB_NAME } from './config.js';
14
- import { buildPayment } from './payment.js';
15
- import { verifyPayment, acceptPayment } from './verify.js';
16
- /** Filename for the persisted wallet identity JSON. */
17
- const IDENTITY_FILE = 'wallet-identity.json';
18
- /**
19
- * BSVAgentWallet — the primary class for agent-to-agent BSV payments.
20
- *
21
- * Usage:
22
- * ```ts
23
- * // Create a new wallet (generates keys)
24
- * const wallet = await BSVAgentWallet.load({ network: 'testnet', storageDir: './agent-wallet' });
25
- *
26
- * // Load an existing wallet
27
- * const wallet = await BSVAgentWallet.load({ network: 'testnet', storageDir: './agent-wallet' });
28
- *
29
- * // Make a payment
30
- * const payment = await wallet.createPayment({ to: recipientPubKey, satoshis: 500 });
31
- *
32
- * // Verify and accept a payment
33
- * const verification = wallet.verifyPayment({ beef: payment.beef });
34
- * if (verification.valid) {
35
- * await wallet.acceptPayment({ beef: payment.beef, ...derivationInfo });
36
- * }
37
- * ```
38
- */
39
- export class BSVAgentWallet {
40
- /** @internal — exposed for advanced operations (e.g. direct internalizeAction) */
41
- _setup;
42
- constructor(setup) {
43
- this._setup = setup;
44
- }
45
- // ---------------------------------------------------------------------------
46
- // Factory methods
47
- // ---------------------------------------------------------------------------
48
- /**
49
- * Create a new agent wallet. Generates a fresh root key and persists it.
50
- * The SQLite database and identity file are written to `config.storageDir`.
51
- */
52
- static async create(config) {
53
- // Generate a new root key (or use one provided in config)
54
- const rootKeyHex = config.rootKeyHex ?? PrivateKey.fromRandom().toHex();
55
- const rootKey = PrivateKey.fromHex(rootKeyHex);
56
- const identityKey = rootKey.toPublicKey().toString();
57
- // Ensure the storage directory exists
58
- fs.mkdirSync(config.storageDir, { recursive: true });
59
- // Persist identity for later loading
60
- const identity = {
61
- rootKeyHex,
62
- identityKey,
63
- network: config.network,
64
- };
65
- const identityPath = path.join(config.storageDir, IDENTITY_FILE);
66
- fs.writeFileSync(identityPath, JSON.stringify(identity, null, 2), 'utf-8');
67
- // Build the wallet
68
- const setup = await BSVAgentWallet.buildSetup(config, rootKeyHex);
69
- return new BSVAgentWallet(setup);
70
- }
71
- /**
72
- * Load an existing agent wallet from its storage directory.
73
- * Reads the persisted identity file and re-initializes the wallet.
74
- */
75
- static async load(config) {
76
- const identityPath = path.join(config.storageDir, IDENTITY_FILE);
77
- if (!fs.existsSync(identityPath)) {
78
- return this.create(config);
79
- }
80
- const identity = JSON.parse(fs.readFileSync(identityPath, 'utf-8'));
81
- const rootKeyHex = config.rootKeyHex ?? identity.rootKeyHex;
82
- const setup = await BSVAgentWallet.buildSetup(config, rootKeyHex);
83
- return new BSVAgentWallet(setup);
84
- }
85
- // ---------------------------------------------------------------------------
86
- // Wallet lifecycle
87
- // ---------------------------------------------------------------------------
88
- /**
89
- * Get this wallet's public identity key (compressed hex, 33 bytes).
90
- * This is the key other agents use to send payments to you.
91
- */
92
- async getIdentityKey() {
93
- return this._setup.identityKey;
94
- }
95
- /**
96
- * Get the wallet's current balance in satoshis.
97
- *
98
- * Uses the BRC-100 wallet's balance method which sums spendable outputs
99
- * in the default basket.
100
- */
101
- async getBalance() {
102
- return await this._setup.wallet.balance();
103
- }
104
- /**
105
- * Cleanly shut down the wallet, releasing database connections and
106
- * stopping the background monitor.
107
- */
108
- async destroy() {
109
- await this._setup.wallet.destroy();
110
- }
111
- // ---------------------------------------------------------------------------
112
- // Payment creation (sender/payer side)
113
- // ---------------------------------------------------------------------------
114
- /**
115
- * Build a BRC-29 payment to another agent.
116
- *
117
- * The transaction is created with `noSend: true` — the sender does NOT
118
- * broadcast it. Instead, the Atomic BEEF and derivation metadata are
119
- * returned so they can be transmitted to the recipient, who will
120
- * verify and internalize (broadcast) the payment.
121
- *
122
- * @param params.to — Recipient's compressed public key (hex).
123
- * @param params.satoshis — Amount in satoshis.
124
- * @param params.description — Optional human-readable note.
125
- */
126
- async createPayment(params) {
127
- return buildPayment(this._setup, params);
128
- }
129
- // ---------------------------------------------------------------------------
130
- // Payment verification & acceptance (receiver/merchant side)
131
- // ---------------------------------------------------------------------------
132
- /**
133
- * Verify an incoming Atomic BEEF payment.
134
- *
135
- * This performs structural validation and SPV verification via tx.verify().
136
- */
137
- async verifyPayment(params) {
138
- return await verifyPayment(params);
139
- }
140
- /**
141
- * Accept (internalize) a verified payment into this wallet.
142
- *
143
- * Uses the BRC-29 wallet payment protocol to derive the correct key
144
- * and claim the output. This triggers SPV verification and, if the
145
- * transaction hasn't been broadcast yet, broadcasts it.
146
- */
147
- async acceptPayment(params) {
148
- return acceptPayment(this._setup, params);
149
- }
150
- // ---------------------------------------------------------------------------
151
- // Access to underlying toolbox objects (for advanced use)
152
- // ---------------------------------------------------------------------------
153
- /** Get the underlying wallet-toolbox SetupWallet for advanced operations. */
154
- getSetup() {
155
- return this._setup;
156
- }
157
- // ---------------------------------------------------------------------------
158
- // Private helpers
159
- // ---------------------------------------------------------------------------
160
- /**
161
- * Internal: manually construct a BRC-100 wallet backed by SQLite.
162
- *
163
- * We build this by hand instead of using Setup.createWalletSQLite because
164
- * the toolbox has a bug where its internal randomBytesHex is a stub.
165
- * We use the same components but wire them up correctly.
166
- */
167
- static async buildSetup(config, rootKeyHex) {
168
- const chain = toChain(config.network);
169
- const taalApiKey = config.taalApiKey ?? DEFAULT_TAAL_API_KEYS[chain];
170
- const rootKey = PrivateKey.fromHex(rootKeyHex);
171
- const identityKey = rootKey.toPublicKey().toString();
172
- // 1. Key derivation
173
- const keyDeriver = new CachedKeyDeriver(rootKey);
174
- // 2. Storage manager (empty initially)
175
- const storage = new WalletStorageManager(identityKey);
176
- // 3. Network services (ARC broadcasting, chain tracking, etc.)
177
- const serviceOptions = Services.createDefaultOptions(chain);
178
- const chaintracksUrl = process.env.BSV_CHAINTRACKS_URL || 'https://chaintracks-us-1.bsvb.tech';
179
- const arcUrl = process.env.BSV_ARC_URL;
180
- serviceOptions.chaintracks = new ChaintracksServiceClient(chain, chaintracksUrl);
181
- if (arcUrl) {
182
- serviceOptions.arcUrl = arcUrl;
183
- }
184
- serviceOptions.taalApiKey = taalApiKey;
185
- const services = new Services(serviceOptions);
186
- // 4. Background monitor
187
- const monopts = Monitor.createDefaultWalletMonitorOptions(chain, storage, services);
188
- const monitor = new Monitor(monopts);
189
- monitor.addDefaultTasks();
190
- // 5. The BRC-100 Wallet
191
- const wallet = new Wallet({ chain, keyDeriver, storage, services, monitor });
192
- // 6. SQLite storage via knex
193
- const filePath = path.join(config.storageDir, `${DEFAULT_DB_NAME}.sqlite`);
194
- const knex = knexLib({
195
- client: 'sqlite3',
196
- connection: { filename: filePath },
197
- useNullAsDefault: true,
198
- });
199
- // Fee model: configurable via BSV_FEE_MODEL env var (default: 100 sat/KB)
200
- const feeModelValue = config.feeModel ??
201
- (process.env.BSV_FEE_MODEL ? parseInt(process.env.BSV_FEE_MODEL, 10) : 100);
202
- const activeStorage = new StorageKnex({
203
- chain,
204
- knex,
205
- commissionSatoshis: 0,
206
- commissionPubKeyHex: undefined,
207
- feeModel: { model: 'sat/kb', value: feeModelValue },
208
- });
209
- await activeStorage.migrate(DEFAULT_DB_NAME, randomBytesHex(33));
210
- await activeStorage.makeAvailable();
211
- await storage.addWalletStorageProvider(activeStorage);
212
- await activeStorage.findOrInsertUser(identityKey);
213
- return {
214
- rootKey,
215
- identityKey,
216
- keyDeriver,
217
- chain,
218
- storage,
219
- services,
220
- monitor,
221
- wallet,
222
- };
223
- }
224
- }
225
- //# sourceMappingURL=wallet.js.map