openclaw-overlay-plugin 0.8.19 → 0.8.21

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
@@ -86,740 +86,6 @@ var init_config = __esm({
86
86
  }
87
87
  });
88
88
 
89
- // src/scripts/output.ts
90
- function setNoExit(value) {
91
- noExitFlag = value;
92
- }
93
- function ok(data) {
94
- if (noExitFlag) return { success: true, data };
95
- console.log(JSON.stringify({ success: true, data }));
96
- process.exit(0);
97
- }
98
- function fail(error) {
99
- const message = error instanceof Error ? error.message : String(error);
100
- if (noExitFlag) return { success: false, error: message };
101
- console.log(JSON.stringify({ success: false, error: message }));
102
- process.exit(1);
103
- }
104
- var noExitFlag;
105
- var init_output = __esm({
106
- "src/scripts/output.ts"() {
107
- "use strict";
108
- noExitFlag = false;
109
- }
110
- });
111
-
112
- // ../plugin-core/dist/config.js
113
- function toChain(network) {
114
- if (network === "testnet")
115
- return "test";
116
- return "main";
117
- }
118
- var DEFAULT_TAAL_API_KEYS, DEFAULT_DB_NAME;
119
- var init_config2 = __esm({
120
- "../plugin-core/dist/config.js"() {
121
- "use strict";
122
- DEFAULT_TAAL_API_KEYS = {
123
- main: "mainnet_9596de07e92300c6287e4393594ae39c",
124
- test: "testnet_0e6cf72133b43ea2d7861da2a38684e3"
125
- };
126
- DEFAULT_DB_NAME = "a2a_agent_wallet";
127
- }
128
- });
129
-
130
- // ../plugin-core/dist/payment.js
131
- import { Beef, Utils as Utils2 } from "@bsv/sdk";
132
- import { randomBytesBase64, ScriptTemplateBRC29 } from "@bsv/wallet-toolbox";
133
- async function buildPayment(setup, params) {
134
- const { to, satoshis, description } = params;
135
- const desc = normalizeDescription(description ?? "agent payment");
136
- const derivationPrefix = randomBytesBase64(8);
137
- const derivationSuffix = randomBytesBase64(8);
138
- const keyDeriver = setup.keyDeriver;
139
- const t = new ScriptTemplateBRC29({
140
- derivationPrefix,
141
- derivationSuffix,
142
- keyDeriver
143
- });
144
- let recipientPubKey;
145
- if (/^0[23][0-9a-fA-F]{64}$/.test(to)) {
146
- recipientPubKey = to;
147
- } else {
148
- throw new Error("PaymentParams.to must be a compressed public key (hex) for BRC-29 payments. Raw BSV addresses are not supported \u2014 the recipient must share their identity key.");
149
- }
150
- const lockingScript = t.lock(setup.rootKey.toString(), recipientPubKey);
151
- const label = "a2a-payment";
152
- const car = await setup.wallet.createAction({
153
- outputs: [
154
- {
155
- lockingScript: lockingScript.toHex(),
156
- satoshis,
157
- outputDescription: desc,
158
- tags: ["relinquish"],
159
- customInstructions: JSON.stringify({
160
- derivationPrefix,
161
- derivationSuffix,
162
- type: "BRC29"
163
- })
164
- }
165
- ],
166
- options: {
167
- randomizeOutputs: false,
168
- acceptDelayedBroadcast: false
169
- },
170
- labels: [label],
171
- description: desc
172
- });
173
- if (!car.tx) {
174
- throw new Error("createAction did not return a transaction. Check wallet funding.");
175
- }
176
- const beef = Beef.fromBinary(car.tx);
177
- const lastTx = beef.txs[beef.txs.length - 1];
178
- const txid = lastTx.txid;
179
- const atomicBinary = beef.toBinaryAtomic(txid);
180
- const beefBase64 = Utils2.toBase64(atomicBinary);
181
- return {
182
- beef: beefBase64,
183
- txid,
184
- satoshis,
185
- derivationPrefix,
186
- derivationSuffix,
187
- senderIdentityKey: setup.identityKey
188
- };
189
- }
190
- function normalizeDescription(desc) {
191
- if (desc.length < 5)
192
- return desc.padEnd(5, " ");
193
- if (desc.length > 50)
194
- return desc.slice(0, 50);
195
- return desc;
196
- }
197
- var init_payment = __esm({
198
- "../plugin-core/dist/payment.js"() {
199
- "use strict";
200
- }
201
- });
202
-
203
- // ../plugin-core/dist/verify.js
204
- import { Beef as Beef2, Utils as Utils3 } from "@bsv/sdk";
205
- async function verifyPayment(params) {
206
- const errors = [];
207
- let txid = "";
208
- let outputCount = 0;
209
- try {
210
- const binary = Utils3.toArray(params.beef, "base64");
211
- const beef = Beef2.fromBinary(binary);
212
- if (beef.txs.length === 0) {
213
- errors.push("BEEF contains no transactions");
214
- } else {
215
- const lastTx = beef.txs[beef.txs.length - 1];
216
- txid = lastTx.txid;
217
- const tx = beef.findAtomicTransaction(txid);
218
- if (tx) {
219
- outputCount = tx.outputs.length;
220
- try {
221
- await tx.verify();
222
- } catch (err) {
223
- const message = err instanceof Error ? err.message : String(err);
224
- errors.push(`SPV verification failed: ${message}`);
225
- }
226
- } else {
227
- errors.push("Could not find atomic transaction in BEEF");
228
- }
229
- }
230
- } catch (err) {
231
- const message = err instanceof Error ? err.message : String(err);
232
- errors.push(`BEEF parse error: ${message}`);
233
- }
234
- if (params.expectedSender) {
235
- if (!/^0[23][0-9a-fA-F]{64}$/.test(params.expectedSender)) {
236
- errors.push("expectedSender is not a valid compressed public key");
237
- }
238
- }
239
- return {
240
- valid: errors.length === 0,
241
- txid,
242
- outputCount,
243
- errors
244
- };
245
- }
246
- async function acceptPayment(setup, params) {
247
- const desc = normalizeDescription2(params.description ?? "received payment");
248
- const vout = params.vout ?? 0;
249
- const binary = Utils3.toArray(params.beef, "base64");
250
- const args = {
251
- tx: binary,
252
- outputs: [
253
- {
254
- outputIndex: vout,
255
- protocol: "wallet payment",
256
- paymentRemittance: {
257
- derivationPrefix: params.derivationPrefix,
258
- derivationSuffix: params.derivationSuffix,
259
- senderIdentityKey: params.senderIdentityKey
260
- }
261
- }
262
- ],
263
- description: desc
264
- };
265
- const result = await setup.wallet.internalizeAction(args);
266
- return {
267
- accepted: result.accepted
268
- };
269
- }
270
- function normalizeDescription2(desc) {
271
- if (desc.length < 5)
272
- return desc.padEnd(5, " ");
273
- if (desc.length > 50)
274
- return desc.slice(0, 50);
275
- return desc;
276
- }
277
- var init_verify = __esm({
278
- "../plugin-core/dist/verify.js"() {
279
- "use strict";
280
- }
281
- });
282
-
283
- // ../plugin-core/dist/wallet.js
284
- import { PrivateKey, CachedKeyDeriver as CachedKeyDeriver2 } from "@bsv/sdk";
285
- import { Wallet, WalletStorageManager, Services, Monitor, StorageKnex, randomBytesHex, ChaintracksServiceClient } from "@bsv/wallet-toolbox";
286
- import knexLib from "knex";
287
- import * as path3 from "node:path";
288
- import * as fs4 from "node:fs";
289
- import debug from "debug";
290
- var log, IDENTITY_FILE, BSVAgentWallet;
291
- var init_wallet = __esm({
292
- "../plugin-core/dist/wallet.js"() {
293
- "use strict";
294
- init_config2();
295
- init_payment();
296
- init_verify();
297
- log = debug("openclaw:plugin:overlay:wallet");
298
- IDENTITY_FILE = "wallet-identity.json";
299
- BSVAgentWallet = class _BSVAgentWallet {
300
- /** @internal — exposed for advanced operations (e.g. direct internalizeAction) */
301
- _setup;
302
- constructor(setup) {
303
- this._setup = setup;
304
- }
305
- // ---------------------------------------------------------------------------
306
- // Factory methods
307
- // ---------------------------------------------------------------------------
308
- /**
309
- * Create a new agent wallet. Generates a fresh root key and persists it.
310
- * The SQLite database and identity file are written to `config.storageDir`.
311
- */
312
- static async create(config) {
313
- log("Creating new wallet in: %s", config.storageDir);
314
- const rootKeyHex = config.rootKeyHex ?? PrivateKey.fromRandom().toHex();
315
- const rootKey = PrivateKey.fromHex(rootKeyHex);
316
- const identityKey = rootKey.toPublicKey().toString();
317
- fs4.mkdirSync(config.storageDir, { recursive: true });
318
- const identity = {
319
- rootKeyHex,
320
- identityKey,
321
- network: config.network
322
- };
323
- const identityPath = path3.join(config.storageDir, IDENTITY_FILE);
324
- fs4.writeFileSync(identityPath, JSON.stringify(identity, null, 2), "utf-8");
325
- const setup = await _BSVAgentWallet.buildSetup(config, rootKeyHex);
326
- return new _BSVAgentWallet(setup);
327
- }
328
- /**
329
- * Load an existing agent wallet from its storage directory.
330
- * Reads the persisted identity file and re-initializes the wallet.
331
- */
332
- static async load(config) {
333
- log("Loading wallet from: %s", config.storageDir);
334
- const identityPath = path3.join(config.storageDir, IDENTITY_FILE);
335
- if (!fs4.existsSync(identityPath)) {
336
- if (config.createIfMissing === false) {
337
- log("Wallet not found and createIfMissing is false");
338
- throw new Error(`No wallet found in ${config.storageDir}`);
339
- }
340
- return this.create(config);
341
- }
342
- const identity = JSON.parse(fs4.readFileSync(identityPath, "utf-8"));
343
- const rootKeyHex = config.rootKeyHex ?? identity.rootKeyHex;
344
- const setup = await _BSVAgentWallet.buildSetup(config, rootKeyHex);
345
- return new _BSVAgentWallet(setup);
346
- }
347
- // ---------------------------------------------------------------------------
348
- // Wallet lifecycle
349
- // ---------------------------------------------------------------------------
350
- /**
351
- * Get this wallet's public identity key (compressed hex, 33 bytes).
352
- * This is the key other agents use to send payments to you.
353
- */
354
- async getIdentityKey() {
355
- return this._setup.identityKey;
356
- }
357
- /**
358
- * Get the wallet's current receive address for the active network.
359
- */
360
- async getAddress() {
361
- const network = this._setup.network || "mainnet";
362
- return this._setup.rootKey.toPublicKey().toAddress(network);
363
- }
364
- /**
365
- * Get the wallet's current balance in satoshis.
366
- *
367
- * Uses the BRC-100 wallet's balance method which sums spendable outputs
368
- * in the default basket.
369
- */
370
- async getBalance() {
371
- return await this._setup.wallet.balance();
372
- }
373
- /**
374
- * Cleanly shut down the wallet, releasing database connections and
375
- * stopping the background monitor.
376
- */
377
- async destroy() {
378
- if (this._setup.monitor) {
379
- await this._setup.monitor.destroy();
380
- }
381
- if (this._setup.wallet) {
382
- await this._setup.wallet.destroy();
383
- }
384
- await this._setup.storage.destroy();
385
- }
386
- // ---------------------------------------------------------------------------
387
- // Payment creation (sender/payer side)
388
- // ---------------------------------------------------------------------------
389
- /**
390
- * Build a BRC-29 payment to another agent.
391
- *
392
- * The transaction is created with `noSend: true` — the sender does NOT
393
- * broadcast it. Instead, the Atomic BEEF and derivation metadata are
394
- * returned so they can be transmitted to the recipient, who will
395
- * verify and internalize (broadcast) the payment.
396
- *
397
- * @param params.to — Recipient's compressed public key (hex).
398
- * @param params.satoshis — Amount in satoshis.
399
- * @param params.description — Optional human-readable note.
400
- */
401
- async createPayment(params) {
402
- return buildPayment(this._setup, params);
403
- }
404
- // ---------------------------------------------------------------------------
405
- // Payment verification & acceptance (receiver/merchant side)
406
- // ---------------------------------------------------------------------------
407
- /**
408
- * Verify an incoming Atomic BEEF payment.
409
- *
410
- * This performs structural validation and SPV verification via tx.verify().
411
- */
412
- async verifyPayment(params) {
413
- return await verifyPayment(params);
414
- }
415
- /**
416
- * Accept (internalize) a verified payment into this wallet.
417
- *
418
- * Uses the BRC-29 wallet payment protocol to derive the correct key
419
- * and claim the output. This triggers SPV verification and, if the
420
- * transaction hasn't been broadcast yet, broadcasts it.
421
- */
422
- async acceptPayment(params) {
423
- return acceptPayment(this._setup, params);
424
- }
425
- // ---------------------------------------------------------------------------
426
- // Access to underlying toolbox objects (for advanced use)
427
- // ---------------------------------------------------------------------------
428
- /** Get the underlying wallet-toolbox SetupWallet for advanced operations. */
429
- getSetup() {
430
- return this._setup;
431
- }
432
- // ---------------------------------------------------------------------------
433
- // Private helpers
434
- // ---------------------------------------------------------------------------
435
- /**
436
- * Internal: manually construct a BRC-100 wallet backed by SQLite.
437
- *
438
- * We build this by hand instead of using Setup.createWalletSQLite because
439
- * the toolbox has a bug where its internal randomBytesHex is a stub.
440
- * We use the same components but wire them up correctly.
441
- */
442
- static async buildSetup(config, rootKeyHex) {
443
- const chain = toChain(config.network);
444
- log("Building setup for chain: %s (network: %s)", chain, config.network);
445
- const taalApiKey = config.taalApiKey ?? DEFAULT_TAAL_API_KEYS[chain];
446
- const rootKey = PrivateKey.fromHex(rootKeyHex);
447
- const identityKey = rootKey.toPublicKey().toString();
448
- const keyDeriver = new CachedKeyDeriver2(rootKey);
449
- const storage = new WalletStorageManager(identityKey);
450
- const serviceOptions = Services.createDefaultOptions(chain);
451
- const chaintracksUrl = process["env"].BSV_CHAINTRACKS_URL || "https://chaintracks-us-1.bsvb.tech";
452
- const arcUrl = process["env"].BSV_ARC_URL;
453
- const isTestMode = config.enableMonitor === false;
454
- if (!isTestMode) {
455
- serviceOptions.chaintracks = new ChaintracksServiceClient(chain, chaintracksUrl);
456
- if (arcUrl) {
457
- serviceOptions.arcUrl = arcUrl;
458
- }
459
- }
460
- serviceOptions.taalApiKey = taalApiKey;
461
- const services = new Services(serviceOptions);
462
- const monopts = Monitor.createDefaultWalletMonitorOptions(chain, storage, services);
463
- const monitor = new Monitor(monopts);
464
- if (!isTestMode) {
465
- monitor.addDefaultTasks();
466
- } else {
467
- monitor.tasks = [];
468
- }
469
- const wallet = isTestMode ? void 0 : new Wallet({ chain, keyDeriver, storage, services, monitor });
470
- const filePath = path3.join(config.storageDir, `${DEFAULT_DB_NAME}.sqlite`);
471
- const knex = knexLib({
472
- client: "sqlite3",
473
- connection: { filename: filePath },
474
- useNullAsDefault: true
475
- });
476
- const feeModelValue = config.feeModel ?? (process["env"].BSV_FEE_MODEL ? parseInt(process["env"].BSV_FEE_MODEL, 10) : 100);
477
- const activeStorage = new StorageKnex({
478
- chain,
479
- knex,
480
- commissionSatoshis: 0,
481
- commissionPubKeyHex: void 0,
482
- feeModel: { model: "sat/kb", value: feeModelValue }
483
- });
484
- await activeStorage.migrate(DEFAULT_DB_NAME, randomBytesHex(33));
485
- await activeStorage.makeAvailable();
486
- await storage.addWalletStorageProvider(activeStorage);
487
- await activeStorage.findOrInsertUser(identityKey);
488
- return {
489
- rootKey,
490
- identityKey,
491
- keyDeriver,
492
- chain,
493
- network: config.network,
494
- storage,
495
- services: isTestMode ? void 0 : services,
496
- monitor: isTestMode ? void 0 : monitor,
497
- wallet
498
- };
499
- }
500
- };
501
- }
502
- });
503
-
504
- // ../plugin-core/dist/index.js
505
- var init_dist = __esm({
506
- "../plugin-core/dist/index.js"() {
507
- "use strict";
508
- init_wallet();
509
- init_config2();
510
- init_payment();
511
- init_verify();
512
- }
513
- });
514
-
515
- // src/scripts/utils/storage.ts
516
- import fs7 from "node:fs";
517
- function ensureStateDir() {
518
- fs7.mkdirSync(OVERLAY_STATE_DIR, { recursive: true });
519
- }
520
- function loadRegistration() {
521
- try {
522
- if (fs7.existsSync(PATHS.registration)) {
523
- return JSON.parse(fs7.readFileSync(PATHS.registration, "utf-8"));
524
- }
525
- } catch {
526
- }
527
- return null;
528
- }
529
- function saveRegistration(data) {
530
- ensureStateDir();
531
- fs7.writeFileSync(PATHS.registration, JSON.stringify(data, null, 2), "utf-8");
532
- }
533
- function deleteRegistration() {
534
- try {
535
- fs7.unlinkSync(PATHS.registration);
536
- } catch {
537
- }
538
- }
539
- function loadServices() {
540
- try {
541
- if (fs7.existsSync(PATHS.services)) {
542
- return JSON.parse(fs7.readFileSync(PATHS.services, "utf-8"));
543
- }
544
- } catch {
545
- }
546
- return [];
547
- }
548
- function saveServices(services) {
549
- ensureStateDir();
550
- fs7.writeFileSync(PATHS.services, JSON.stringify(services, null, 2), "utf-8");
551
- }
552
- function appendToJsonl(filePath, entry) {
553
- ensureStateDir();
554
- fs7.appendFileSync(filePath, JSON.stringify(entry) + "\n");
555
- }
556
- function readJsonl(filePath) {
557
- if (!fs7.existsSync(filePath)) return [];
558
- const lines = fs7.readFileSync(filePath, "utf-8").trim().split("\n").filter(Boolean);
559
- return lines.map((line) => {
560
- try {
561
- return JSON.parse(line);
562
- } catch {
563
- return null;
564
- }
565
- }).filter(Boolean);
566
- }
567
- function updateServiceQueueStatus(requestId, newStatus, additionalFields = {}) {
568
- if (!fs7.existsSync(PATHS.serviceQueue)) return false;
569
- const lines = fs7.readFileSync(PATHS.serviceQueue, "utf-8").trim().split("\n").filter(Boolean);
570
- let updated = false;
571
- const updatedLines = lines.map((line) => {
572
- try {
573
- const entry = JSON.parse(line);
574
- if (entry.requestId === requestId) {
575
- updated = true;
576
- return JSON.stringify({
577
- ...entry,
578
- status: newStatus,
579
- ...additionalFields,
580
- updatedAt: Date.now()
581
- });
582
- }
583
- return line;
584
- } catch {
585
- return line;
586
- }
587
- });
588
- if (updated) {
589
- fs7.writeFileSync(PATHS.serviceQueue, updatedLines.join("\n") + "\n");
590
- }
591
- return updated;
592
- }
593
- var init_storage = __esm({
594
- "src/scripts/utils/storage.ts"() {
595
- "use strict";
596
- init_config();
597
- }
598
- });
599
-
600
- // src/scripts/overlay/transaction.ts
601
- import { Utils as Utils4, PushDrop, Transaction } from "@bsv/sdk";
602
- async function buildPushDropScript(wallet, payload) {
603
- const jsonBytes = Utils4.toArray(JSON.stringify(payload), "utf8");
604
- const fields = [jsonBytes];
605
- const token = new PushDrop(wallet._setup.wallet);
606
- const script = await token.lock(fields, [0, PROTOCOL_ID], "1", "self", true, true);
607
- return script.toHex();
608
- }
609
- async function buildRealOverlayTransaction(payload, topic) {
610
- const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
611
- const lockingScript = await buildPushDropScript(wallet, payload);
612
- const response = await wallet._setup.wallet.createAction({
613
- description: "topic manager submission",
614
- outputs: [
615
- {
616
- lockingScript,
617
- satoshis: 1,
618
- outputDescription: "overlay",
619
- basket: topic
620
- // basket is the topic manager
621
- }
622
- ],
623
- options: {
624
- acceptDelayedBroadcast: false
625
- }
626
- });
627
- const submitResp = await fetch(`${OVERLAY_URL}/submit`, {
628
- method: "POST",
629
- headers: {
630
- "Content-Type": "application/octet-stream",
631
- "X-Topics": JSON.stringify([topic])
632
- },
633
- body: new Uint8Array(response.tx)
634
- });
635
- if (!submitResp.ok) {
636
- const errText = await submitResp.text();
637
- throw new Error(`Overlay submission failed: ${submitResp.status} \u2014 ${errText}`);
638
- }
639
- const wocNet = NETWORK === "mainnet" ? "" : "test.";
640
- return {
641
- txid: response.txid,
642
- funded: "stored-beef",
643
- explorer: `https://${wocNet}whatsonchain.com/tx/${response.txid}`
644
- };
645
- }
646
- async function lookupOverlay(service, query) {
647
- const resp = await fetch(`${OVERLAY_URL}/lookup`, {
648
- method: "POST",
649
- headers: { "Content-Type": "application/json" },
650
- body: JSON.stringify({ service, query })
651
- });
652
- if (!resp.ok) {
653
- const errText = await resp.text();
654
- throw new Error(`Lookup failed: ${resp.status} \u2014 ${errText}`);
655
- }
656
- return resp.json();
657
- }
658
- async function parseOverlayOutput(beefData, outputIndex) {
659
- try {
660
- const tx = Transaction.fromBEEF(beefData);
661
- const txid = tx.id("hex");
662
- const output = tx.outputs[outputIndex];
663
- if (!output) return { data: null, txid: null };
664
- const { fields } = PushDrop.decode(output.lockingScript);
665
- return { data: JSON.parse(Utils4.toUTF8(fields[0])), txid };
666
- } catch {
667
- return { data: null, txid: null };
668
- }
669
- }
670
- var init_transaction = __esm({
671
- "src/scripts/overlay/transaction.ts"() {
672
- "use strict";
673
- init_config();
674
- init_dist();
675
- }
676
- });
677
-
678
- // src/scripts/overlay/services.ts
679
- var services_exports = {};
680
- __export(services_exports, {
681
- cmdAdvertise: () => cmdAdvertise,
682
- cmdReadvertise: () => cmdReadvertise,
683
- cmdRemove: () => cmdRemove,
684
- cmdServices: () => cmdServices
685
- });
686
- async function getBSVAgentWallet6() {
687
- return BSVAgentWallet;
688
- }
689
- async function cmdServices() {
690
- const services = loadServices();
691
- return ok({ services, count: services.length });
692
- }
693
- async function cmdAdvertise(serviceId, name, priceSatsStr, description) {
694
- if (!serviceId || !name || !priceSatsStr) {
695
- return fail("Usage: advertise <serviceId> <name> <priceSats> [description]");
696
- }
697
- const priceSats = parseInt(priceSatsStr, 10);
698
- if (isNaN(priceSats) || priceSats < 0) {
699
- return fail("priceSats must be a non-negative integer");
700
- }
701
- const BSVAgentWallet2 = await getBSVAgentWallet6();
702
- const wallet = await BSVAgentWallet2.load({ network: NETWORK, storageDir: WALLET_DIR });
703
- const identityKey = await wallet.getIdentityKey();
704
- await wallet.destroy();
705
- const services = loadServices();
706
- const existing = services.find((s) => s.serviceId === serviceId);
707
- if (existing) {
708
- return fail(`Service '${serviceId}' already exists. Use 'readvertise' to update.`);
709
- }
710
- const newService = {
711
- serviceId,
712
- name,
713
- description: description || `${name} service`,
714
- priceSats,
715
- registeredAt: (/* @__PURE__ */ new Date()).toISOString()
716
- };
717
- const servicePayload = {
718
- protocol: PROTOCOL_ID,
719
- type: "service",
720
- identityKey,
721
- serviceId,
722
- name,
723
- description: newService.description,
724
- pricing: {
725
- model: "per-task",
726
- amountSats: priceSats
727
- },
728
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
729
- };
730
- try {
731
- const result = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);
732
- newService.txid = result.txid;
733
- services.push(newService);
734
- saveServices(services);
735
- return ok({
736
- advertised: true,
737
- service: newService,
738
- txid: result.txid,
739
- funded: result.funded
740
- });
741
- } catch (err) {
742
- return fail(`Failed to advertise service: ${err.message}`);
743
- }
744
- }
745
- async function cmdRemove(serviceId) {
746
- if (!serviceId) {
747
- return fail("Usage: remove <serviceId>");
748
- }
749
- const services = loadServices();
750
- const idx = services.findIndex((s) => s.serviceId === serviceId);
751
- if (idx === -1) {
752
- return fail(`Service '${serviceId}' not found`);
753
- }
754
- const removed = services.splice(idx, 1)[0];
755
- saveServices(services);
756
- return ok({
757
- removed: true,
758
- service: removed,
759
- note: "Removed from local registry. On-chain record remains (blockchain is immutable)."
760
- });
761
- }
762
- async function cmdReadvertise(serviceId, name, priceSatsStr, description) {
763
- if (!serviceId) {
764
- return fail("Usage: readvertise <serviceId> [name] [priceSats] [description]");
765
- }
766
- const services = loadServices();
767
- const existing = services.find((s) => s.serviceId === serviceId);
768
- if (!existing) {
769
- return fail(`Service '${serviceId}' not found. Use 'advertise' to create.`);
770
- }
771
- const BSVAgentWallet2 = await getBSVAgentWallet6();
772
- const wallet = await BSVAgentWallet2.load({ network: NETWORK, storageDir: WALLET_DIR });
773
- const identityKey = await wallet.getIdentityKey();
774
- await wallet.destroy();
775
- if (name) existing.name = name;
776
- if (priceSatsStr) {
777
- const priceSats = parseInt(priceSatsStr, 10);
778
- if (isNaN(priceSats) || priceSats < 0) {
779
- return fail("priceSats must be a non-negative integer");
780
- }
781
- existing.priceSats = priceSats;
782
- }
783
- if (description) existing.description = description;
784
- existing.registeredAt = (/* @__PURE__ */ new Date()).toISOString();
785
- const servicePayload = {
786
- protocol: PROTOCOL_ID,
787
- type: "service",
788
- identityKey,
789
- serviceId,
790
- name: existing.name,
791
- description: existing.description,
792
- pricing: {
793
- model: "per-task",
794
- amountSats: existing.priceSats
795
- },
796
- timestamp: existing.registeredAt
797
- };
798
- try {
799
- const result = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);
800
- existing.txid = result.txid;
801
- saveServices(services);
802
- return ok({
803
- readvertised: true,
804
- service: existing,
805
- txid: result.txid,
806
- funded: result.funded
807
- });
808
- } catch (err) {
809
- return fail(`Failed to readvertise service: ${err.message}`);
810
- }
811
- }
812
- var init_services = __esm({
813
- "src/scripts/overlay/services.ts"() {
814
- "use strict";
815
- init_config();
816
- init_output();
817
- init_storage();
818
- init_transaction();
819
- init_dist();
820
- }
821
- });
822
-
823
89
  // index.ts
824
90
  import path4 from "node:path";
825
91
  import os2 from "node:os";
@@ -1434,9 +700,25 @@ async function initializeServiceSystem() {
1434
700
 
1435
701
  // src/scripts/wallet/setup.ts
1436
702
  init_config();
1437
- init_output();
1438
703
  import fs5 from "node:fs";
1439
704
 
705
+ // src/scripts/output.ts
706
+ var noExitFlag = false;
707
+ function setNoExit(value) {
708
+ noExitFlag = value;
709
+ }
710
+ function ok(data) {
711
+ if (noExitFlag) return { success: true, data };
712
+ console.log(JSON.stringify({ success: true, data }));
713
+ process.exit(0);
714
+ }
715
+ function fail(error) {
716
+ const message = error instanceof Error ? error.message : String(error);
717
+ if (noExitFlag) return { success: false, error: message };
718
+ console.log(JSON.stringify({ success: false, error: message }));
719
+ process.exit(1);
720
+ }
721
+
1440
722
  // src/scripts/wallet/identity.ts
1441
723
  init_config();
1442
724
  import fs3 from "node:fs";
@@ -1465,67 +747,435 @@ async function getSdk() {
1465
747
  } catch {
1466
748
  }
1467
749
  }
1468
- throw new Error("Cannot find @bsv/sdk. Run setup.sh first.");
750
+ throw new Error("Cannot find @bsv/sdk. Run setup.sh first.");
751
+ }
752
+ }
753
+ function loadWalletIdentity() {
754
+ if (!fs3.existsSync(PATHS.walletIdentity)) {
755
+ throw new Error("Wallet not initialized. Run: cli setup");
756
+ }
757
+ try {
758
+ const fileMode = fs3.statSync(PATHS.walletIdentity).mode & 511;
759
+ if (fileMode & 36) {
760
+ console.error(`[security] WARNING: ${PATHS.walletIdentity} has permissive mode 0${fileMode.toString(8)}. Run: chmod 600 ${PATHS.walletIdentity}`);
761
+ }
762
+ } catch {
763
+ }
764
+ return JSON.parse(fs3.readFileSync(PATHS.walletIdentity, "utf-8"));
765
+ }
766
+ async function loadIdentity() {
767
+ const identity = loadWalletIdentity();
768
+ const sdk = await getSdk();
769
+ const privKey = sdk.PrivateKey.fromHex(identity.rootKeyHex);
770
+ return { identityKey: identity.identityKey, privKey };
771
+ }
772
+ async function signRelayMessage(privKey, to, type, payload) {
773
+ const sdk = await getSdk();
774
+ const preimage = to + type + JSON.stringify(payload);
775
+ const msgHash = sdk.Hash.sha256(Array.from(new TextEncoder().encode(preimage)));
776
+ const sig = privKey.sign(msgHash);
777
+ return Array.from(sig.toDER()).map((b) => b.toString(16).padStart(2, "0")).join("");
778
+ }
779
+ async function verifyRelaySignature(fromKey, to, type, payload, signatureHex) {
780
+ if (!signatureHex) return { valid: false, reason: "no signature" };
781
+ try {
782
+ const sdk = await getSdk();
783
+ const preimage = to + type + JSON.stringify(payload);
784
+ const msgHash = sdk.Hash.sha256(Array.from(new TextEncoder().encode(preimage)));
785
+ const sigBytes = [];
786
+ for (let i = 0; i < signatureHex.length; i += 2) {
787
+ sigBytes.push(parseInt(signatureHex.substring(i, i + 2), 16));
788
+ }
789
+ const sig = sdk.Signature.fromDER(sigBytes);
790
+ const pubKey = sdk.PublicKey.fromString(fromKey);
791
+ return { valid: pubKey.verify(msgHash, sig) };
792
+ } catch (err) {
793
+ return { valid: false, reason: String(err) };
794
+ }
795
+ }
796
+ async function deriveWalletAddress(privKey, network = NETWORK) {
797
+ const keyDeriver = new CachedKeyDeriver(privKey);
798
+ const pubKey = keyDeriver.derivePublicKey(
799
+ brc29ProtocolID,
800
+ Utils.toBase64(Utils.toArray("import")) + " " + Utils.toBase64(Utils.toArray("now")),
801
+ "self",
802
+ true
803
+ );
804
+ const address = pubKey.toAddress(network);
805
+ const hash160 = Buffer.from(pubKey.toHash());
806
+ return { address, hash160, pubKey };
807
+ }
808
+
809
+ // ../plugin-core/dist/wallet.js
810
+ import { PrivateKey, CachedKeyDeriver as CachedKeyDeriver2 } from "@bsv/sdk";
811
+ import { Wallet, WalletStorageManager, Services, Monitor, StorageKnex, randomBytesHex, ChaintracksServiceClient } from "@bsv/wallet-toolbox";
812
+ import knexLib from "knex";
813
+ import * as path3 from "node:path";
814
+ import * as fs4 from "node:fs";
815
+ import debug from "debug";
816
+
817
+ // ../plugin-core/dist/config.js
818
+ function toChain(network) {
819
+ if (network === "testnet")
820
+ return "test";
821
+ return "main";
822
+ }
823
+ var DEFAULT_TAAL_API_KEYS = {
824
+ main: "mainnet_9596de07e92300c6287e4393594ae39c",
825
+ test: "testnet_0e6cf72133b43ea2d7861da2a38684e3"
826
+ };
827
+ var DEFAULT_DB_NAME = "a2a_agent_wallet";
828
+
829
+ // ../plugin-core/dist/payment.js
830
+ import { Beef, Utils as Utils2 } from "@bsv/sdk";
831
+ import { randomBytesBase64, ScriptTemplateBRC29 } from "@bsv/wallet-toolbox";
832
+ async function buildPayment(setup, params) {
833
+ const { to, satoshis, description } = params;
834
+ const desc = normalizeDescription(description ?? "agent payment");
835
+ const derivationPrefix = randomBytesBase64(8);
836
+ const derivationSuffix = randomBytesBase64(8);
837
+ const keyDeriver = setup.keyDeriver;
838
+ const t = new ScriptTemplateBRC29({
839
+ derivationPrefix,
840
+ derivationSuffix,
841
+ keyDeriver
842
+ });
843
+ let recipientPubKey;
844
+ if (/^0[23][0-9a-fA-F]{64}$/.test(to)) {
845
+ recipientPubKey = to;
846
+ } else {
847
+ throw new Error("PaymentParams.to must be a compressed public key (hex) for BRC-29 payments. Raw BSV addresses are not supported \u2014 the recipient must share their identity key.");
848
+ }
849
+ const lockingScript = t.lock(setup.rootKey.toString(), recipientPubKey);
850
+ const label = "a2a-payment";
851
+ const car = await setup.wallet.createAction({
852
+ outputs: [
853
+ {
854
+ lockingScript: lockingScript.toHex(),
855
+ satoshis,
856
+ outputDescription: desc,
857
+ tags: ["relinquish"],
858
+ customInstructions: JSON.stringify({
859
+ derivationPrefix,
860
+ derivationSuffix,
861
+ type: "BRC29"
862
+ })
863
+ }
864
+ ],
865
+ options: {
866
+ randomizeOutputs: false,
867
+ acceptDelayedBroadcast: false
868
+ },
869
+ labels: [label],
870
+ description: desc
871
+ });
872
+ if (!car.tx) {
873
+ throw new Error("createAction did not return a transaction. Check wallet funding.");
874
+ }
875
+ const beef = Beef.fromBinary(car.tx);
876
+ const lastTx = beef.txs[beef.txs.length - 1];
877
+ const txid = lastTx.txid;
878
+ const atomicBinary = beef.toBinaryAtomic(txid);
879
+ const beefBase64 = Utils2.toBase64(atomicBinary);
880
+ return {
881
+ beef: beefBase64,
882
+ txid,
883
+ satoshis,
884
+ derivationPrefix,
885
+ derivationSuffix,
886
+ senderIdentityKey: setup.identityKey
887
+ };
888
+ }
889
+ function normalizeDescription(desc) {
890
+ if (desc.length < 5)
891
+ return desc.padEnd(5, " ");
892
+ if (desc.length > 50)
893
+ return desc.slice(0, 50);
894
+ return desc;
895
+ }
896
+
897
+ // ../plugin-core/dist/verify.js
898
+ import { Beef as Beef2, Utils as Utils3 } from "@bsv/sdk";
899
+ async function verifyPayment(params) {
900
+ const errors = [];
901
+ let txid = "";
902
+ let outputCount = 0;
903
+ try {
904
+ const binary = Utils3.toArray(params.beef, "base64");
905
+ const beef = Beef2.fromBinary(binary);
906
+ if (beef.txs.length === 0) {
907
+ errors.push("BEEF contains no transactions");
908
+ } else {
909
+ const lastTx = beef.txs[beef.txs.length - 1];
910
+ txid = lastTx.txid;
911
+ const tx = beef.findAtomicTransaction(txid);
912
+ if (tx) {
913
+ outputCount = tx.outputs.length;
914
+ try {
915
+ await tx.verify();
916
+ } catch (err) {
917
+ const message = err instanceof Error ? err.message : String(err);
918
+ errors.push(`SPV verification failed: ${message}`);
919
+ }
920
+ } else {
921
+ errors.push("Could not find atomic transaction in BEEF");
922
+ }
923
+ }
924
+ } catch (err) {
925
+ const message = err instanceof Error ? err.message : String(err);
926
+ errors.push(`BEEF parse error: ${message}`);
927
+ }
928
+ if (params.expectedSender) {
929
+ if (!/^0[23][0-9a-fA-F]{64}$/.test(params.expectedSender)) {
930
+ errors.push("expectedSender is not a valid compressed public key");
931
+ }
932
+ }
933
+ return {
934
+ valid: errors.length === 0,
935
+ txid,
936
+ outputCount,
937
+ errors
938
+ };
939
+ }
940
+ async function acceptPayment(setup, params) {
941
+ const desc = normalizeDescription2(params.description ?? "received payment");
942
+ const vout = params.vout ?? 0;
943
+ const binary = Utils3.toArray(params.beef, "base64");
944
+ const args = {
945
+ tx: binary,
946
+ outputs: [
947
+ {
948
+ outputIndex: vout,
949
+ protocol: "wallet payment",
950
+ paymentRemittance: {
951
+ derivationPrefix: params.derivationPrefix,
952
+ derivationSuffix: params.derivationSuffix,
953
+ senderIdentityKey: params.senderIdentityKey
954
+ }
955
+ }
956
+ ],
957
+ description: desc
958
+ };
959
+ const result = await setup.wallet.internalizeAction(args);
960
+ return {
961
+ accepted: result.accepted
962
+ };
963
+ }
964
+ function normalizeDescription2(desc) {
965
+ if (desc.length < 5)
966
+ return desc.padEnd(5, " ");
967
+ if (desc.length > 50)
968
+ return desc.slice(0, 50);
969
+ return desc;
970
+ }
971
+
972
+ // ../plugin-core/dist/wallet.js
973
+ var log = debug("openclaw:plugin:overlay:wallet");
974
+ var IDENTITY_FILE = "wallet-identity.json";
975
+ var BSVAgentWallet = class _BSVAgentWallet {
976
+ /** @internal — exposed for advanced operations (e.g. direct internalizeAction) */
977
+ _setup;
978
+ constructor(setup) {
979
+ this._setup = setup;
980
+ }
981
+ // ---------------------------------------------------------------------------
982
+ // Factory methods
983
+ // ---------------------------------------------------------------------------
984
+ /**
985
+ * Create a new agent wallet. Generates a fresh root key and persists it.
986
+ * The SQLite database and identity file are written to `config.storageDir`.
987
+ */
988
+ static async create(config) {
989
+ log("Creating new wallet in: %s", config.storageDir);
990
+ const rootKeyHex = config.rootKeyHex ?? PrivateKey.fromRandom().toHex();
991
+ const rootKey = PrivateKey.fromHex(rootKeyHex);
992
+ const identityKey = rootKey.toPublicKey().toString();
993
+ fs4.mkdirSync(config.storageDir, { recursive: true });
994
+ const identity = {
995
+ rootKeyHex,
996
+ identityKey,
997
+ network: config.network
998
+ };
999
+ const identityPath = path3.join(config.storageDir, IDENTITY_FILE);
1000
+ fs4.writeFileSync(identityPath, JSON.stringify(identity, null, 2), "utf-8");
1001
+ const setup = await _BSVAgentWallet.buildSetup(config, rootKeyHex);
1002
+ return new _BSVAgentWallet(setup);
1003
+ }
1004
+ /**
1005
+ * Load an existing agent wallet from its storage directory.
1006
+ * Reads the persisted identity file and re-initializes the wallet.
1007
+ */
1008
+ static async load(config) {
1009
+ log("Loading wallet from: %s", config.storageDir);
1010
+ const identityPath = path3.join(config.storageDir, IDENTITY_FILE);
1011
+ if (!fs4.existsSync(identityPath)) {
1012
+ if (config.createIfMissing === false) {
1013
+ log("Wallet not found and createIfMissing is false");
1014
+ throw new Error(`No wallet found in ${config.storageDir}`);
1015
+ }
1016
+ return this.create(config);
1017
+ }
1018
+ const identity = JSON.parse(fs4.readFileSync(identityPath, "utf-8"));
1019
+ const rootKeyHex = config.rootKeyHex ?? identity.rootKeyHex;
1020
+ const setup = await _BSVAgentWallet.buildSetup(config, rootKeyHex);
1021
+ return new _BSVAgentWallet(setup);
1469
1022
  }
1470
- }
1471
- function loadWalletIdentity() {
1472
- if (!fs3.existsSync(PATHS.walletIdentity)) {
1473
- throw new Error("Wallet not initialized. Run: cli setup");
1023
+ // ---------------------------------------------------------------------------
1024
+ // Wallet lifecycle
1025
+ // ---------------------------------------------------------------------------
1026
+ /**
1027
+ * Get this wallet's public identity key (compressed hex, 33 bytes).
1028
+ * This is the key other agents use to send payments to you.
1029
+ */
1030
+ async getIdentityKey() {
1031
+ return this._setup.identityKey;
1474
1032
  }
1475
- try {
1476
- const fileMode = fs3.statSync(PATHS.walletIdentity).mode & 511;
1477
- if (fileMode & 36) {
1478
- console.error(`[security] WARNING: ${PATHS.walletIdentity} has permissive mode 0${fileMode.toString(8)}. Run: chmod 600 ${PATHS.walletIdentity}`);
1033
+ /**
1034
+ * Get the wallet's current receive address for the active network.
1035
+ */
1036
+ async getAddress() {
1037
+ const network = this._setup.network || "mainnet";
1038
+ return this._setup.rootKey.toPublicKey().toAddress(network);
1039
+ }
1040
+ /**
1041
+ * Get the wallet's current balance in satoshis.
1042
+ *
1043
+ * Uses the BRC-100 wallet's balance method which sums spendable outputs
1044
+ * in the default basket.
1045
+ */
1046
+ async getBalance() {
1047
+ return await this._setup.wallet.balance();
1048
+ }
1049
+ /**
1050
+ * Cleanly shut down the wallet, releasing database connections and
1051
+ * stopping the background monitor.
1052
+ */
1053
+ async destroy() {
1054
+ if (this._setup.monitor) {
1055
+ await this._setup.monitor.destroy();
1479
1056
  }
1480
- } catch {
1057
+ if (this._setup.wallet) {
1058
+ await this._setup.wallet.destroy();
1059
+ }
1060
+ await this._setup.storage.destroy();
1481
1061
  }
1482
- return JSON.parse(fs3.readFileSync(PATHS.walletIdentity, "utf-8"));
1483
- }
1484
- async function loadIdentity() {
1485
- const identity = loadWalletIdentity();
1486
- const sdk = await getSdk();
1487
- const privKey = sdk.PrivateKey.fromHex(identity.rootKeyHex);
1488
- return { identityKey: identity.identityKey, privKey };
1489
- }
1490
- async function signRelayMessage(privKey, to, type, payload) {
1491
- const sdk = await getSdk();
1492
- const preimage = to + type + JSON.stringify(payload);
1493
- const msgHash = sdk.Hash.sha256(Array.from(new TextEncoder().encode(preimage)));
1494
- const sig = privKey.sign(msgHash);
1495
- return Array.from(sig.toDER()).map((b) => b.toString(16).padStart(2, "0")).join("");
1496
- }
1497
- async function verifyRelaySignature(fromKey, to, type, payload, signatureHex) {
1498
- if (!signatureHex) return { valid: false, reason: "no signature" };
1499
- try {
1500
- const sdk = await getSdk();
1501
- const preimage = to + type + JSON.stringify(payload);
1502
- const msgHash = sdk.Hash.sha256(Array.from(new TextEncoder().encode(preimage)));
1503
- const sigBytes = [];
1504
- for (let i = 0; i < signatureHex.length; i += 2) {
1505
- sigBytes.push(parseInt(signatureHex.substring(i, i + 2), 16));
1062
+ // ---------------------------------------------------------------------------
1063
+ // Payment creation (sender/payer side)
1064
+ // ---------------------------------------------------------------------------
1065
+ /**
1066
+ * Build a BRC-29 payment to another agent.
1067
+ *
1068
+ * The transaction is created with `noSend: true` the sender does NOT
1069
+ * broadcast it. Instead, the Atomic BEEF and derivation metadata are
1070
+ * returned so they can be transmitted to the recipient, who will
1071
+ * verify and internalize (broadcast) the payment.
1072
+ *
1073
+ * @param params.to — Recipient's compressed public key (hex).
1074
+ * @param params.satoshis — Amount in satoshis.
1075
+ * @param params.description Optional human-readable note.
1076
+ */
1077
+ async createPayment(params) {
1078
+ return buildPayment(this._setup, params);
1079
+ }
1080
+ // ---------------------------------------------------------------------------
1081
+ // Payment verification & acceptance (receiver/merchant side)
1082
+ // ---------------------------------------------------------------------------
1083
+ /**
1084
+ * Verify an incoming Atomic BEEF payment.
1085
+ *
1086
+ * This performs structural validation and SPV verification via tx.verify().
1087
+ */
1088
+ async verifyPayment(params) {
1089
+ return await verifyPayment(params);
1090
+ }
1091
+ /**
1092
+ * Accept (internalize) a verified payment into this wallet.
1093
+ *
1094
+ * Uses the BRC-29 wallet payment protocol to derive the correct key
1095
+ * and claim the output. This triggers SPV verification and, if the
1096
+ * transaction hasn't been broadcast yet, broadcasts it.
1097
+ */
1098
+ async acceptPayment(params) {
1099
+ return acceptPayment(this._setup, params);
1100
+ }
1101
+ // ---------------------------------------------------------------------------
1102
+ // Access to underlying toolbox objects (for advanced use)
1103
+ // ---------------------------------------------------------------------------
1104
+ /** Get the underlying wallet-toolbox SetupWallet for advanced operations. */
1105
+ getSetup() {
1106
+ return this._setup;
1107
+ }
1108
+ // ---------------------------------------------------------------------------
1109
+ // Private helpers
1110
+ // ---------------------------------------------------------------------------
1111
+ /**
1112
+ * Internal: manually construct a BRC-100 wallet backed by SQLite.
1113
+ *
1114
+ * We build this by hand instead of using Setup.createWalletSQLite because
1115
+ * the toolbox has a bug where its internal randomBytesHex is a stub.
1116
+ * We use the same components but wire them up correctly.
1117
+ */
1118
+ static async buildSetup(config, rootKeyHex) {
1119
+ const chain = toChain(config.network);
1120
+ log("Building setup for chain: %s (network: %s)", chain, config.network);
1121
+ const taalApiKey = config.taalApiKey ?? DEFAULT_TAAL_API_KEYS[chain];
1122
+ const rootKey = PrivateKey.fromHex(rootKeyHex);
1123
+ const identityKey = rootKey.toPublicKey().toString();
1124
+ const keyDeriver = new CachedKeyDeriver2(rootKey);
1125
+ const storage = new WalletStorageManager(identityKey);
1126
+ const serviceOptions = Services.createDefaultOptions(chain);
1127
+ const chaintracksUrl = process["env"].BSV_CHAINTRACKS_URL || "https://chaintracks-us-1.bsvb.tech";
1128
+ const arcUrl = process["env"].BSV_ARC_URL;
1129
+ const isTestMode = config.enableMonitor === false;
1130
+ if (!isTestMode) {
1131
+ serviceOptions.chaintracks = new ChaintracksServiceClient(chain, chaintracksUrl);
1132
+ if (arcUrl) {
1133
+ serviceOptions.arcUrl = arcUrl;
1134
+ }
1506
1135
  }
1507
- const sig = sdk.Signature.fromDER(sigBytes);
1508
- const pubKey = sdk.PublicKey.fromString(fromKey);
1509
- return { valid: pubKey.verify(msgHash, sig) };
1510
- } catch (err) {
1511
- return { valid: false, reason: String(err) };
1136
+ serviceOptions.taalApiKey = taalApiKey;
1137
+ const services = new Services(serviceOptions);
1138
+ const monopts = Monitor.createDefaultWalletMonitorOptions(chain, storage, services);
1139
+ const monitor = new Monitor(monopts);
1140
+ if (!isTestMode) {
1141
+ monitor.addDefaultTasks();
1142
+ } else {
1143
+ monitor.tasks = [];
1144
+ }
1145
+ const wallet = isTestMode ? void 0 : new Wallet({ chain, keyDeriver, storage, services, monitor });
1146
+ const filePath = path3.join(config.storageDir, `${DEFAULT_DB_NAME}.sqlite`);
1147
+ const knex = knexLib({
1148
+ client: "sqlite3",
1149
+ connection: { filename: filePath },
1150
+ useNullAsDefault: true
1151
+ });
1152
+ const feeModelValue = config.feeModel ?? (process["env"].BSV_FEE_MODEL ? parseInt(process["env"].BSV_FEE_MODEL, 10) : 100);
1153
+ const activeStorage = new StorageKnex({
1154
+ chain,
1155
+ knex,
1156
+ commissionSatoshis: 0,
1157
+ commissionPubKeyHex: void 0,
1158
+ feeModel: { model: "sat/kb", value: feeModelValue }
1159
+ });
1160
+ await activeStorage.migrate(DEFAULT_DB_NAME, randomBytesHex(33));
1161
+ await activeStorage.makeAvailable();
1162
+ await storage.addWalletStorageProvider(activeStorage);
1163
+ await activeStorage.findOrInsertUser(identityKey);
1164
+ return {
1165
+ rootKey,
1166
+ identityKey,
1167
+ keyDeriver,
1168
+ chain,
1169
+ network: config.network,
1170
+ storage,
1171
+ services: isTestMode ? void 0 : services,
1172
+ monitor: isTestMode ? void 0 : monitor,
1173
+ wallet
1174
+ };
1512
1175
  }
1513
- }
1514
- async function deriveWalletAddress(privKey, network = NETWORK) {
1515
- const keyDeriver = new CachedKeyDeriver(privKey);
1516
- const pubKey = keyDeriver.derivePublicKey(
1517
- brc29ProtocolID,
1518
- Utils.toBase64(Utils.toArray("import")) + " " + Utils.toBase64(Utils.toArray("now")),
1519
- "self",
1520
- true
1521
- );
1522
- const address = pubKey.toAddress(network);
1523
- const hash160 = Buffer.from(pubKey.toHash());
1524
- return { address, hash160, pubKey };
1525
- }
1176
+ };
1526
1177
 
1527
1178
  // src/scripts/wallet/setup.ts
1528
- init_dist();
1529
1179
  async function getBSVAgentWallet() {
1530
1180
  return BSVAgentWallet;
1531
1181
  }
@@ -1620,7 +1270,6 @@ async function cmdAddress() {
1620
1270
 
1621
1271
  // src/scripts/wallet/balance.ts
1622
1272
  init_config();
1623
- init_output();
1624
1273
  import fs6 from "node:fs";
1625
1274
 
1626
1275
  // src/scripts/utils/woc.ts
@@ -1717,7 +1366,6 @@ async function buildMerklePathFromTSC(txid, txIndex, nodes, blockHeight) {
1717
1366
  }
1718
1367
 
1719
1368
  // src/scripts/wallet/balance.ts
1720
- init_dist();
1721
1369
  async function getBSVAgentWallet2() {
1722
1370
  return BSVAgentWallet;
1723
1371
  }
@@ -1899,11 +1547,157 @@ Wait for 1 block confirmation (~10 minutes) and try again, or use a fresh UTXO a
1899
1547
 
1900
1548
  // src/scripts/overlay/registration.ts
1901
1549
  init_config();
1902
- init_output();
1903
- init_storage();
1904
- init_transaction();
1905
- init_dist();
1906
1550
  import fs8 from "node:fs";
1551
+
1552
+ // src/scripts/utils/storage.ts
1553
+ init_config();
1554
+ import fs7 from "node:fs";
1555
+ function ensureStateDir() {
1556
+ fs7.mkdirSync(OVERLAY_STATE_DIR, { recursive: true });
1557
+ }
1558
+ function loadRegistration() {
1559
+ try {
1560
+ if (fs7.existsSync(PATHS.registration)) {
1561
+ return JSON.parse(fs7.readFileSync(PATHS.registration, "utf-8"));
1562
+ }
1563
+ } catch {
1564
+ }
1565
+ return null;
1566
+ }
1567
+ function saveRegistration(data) {
1568
+ ensureStateDir();
1569
+ fs7.writeFileSync(PATHS.registration, JSON.stringify(data, null, 2), "utf-8");
1570
+ }
1571
+ function deleteRegistration() {
1572
+ try {
1573
+ fs7.unlinkSync(PATHS.registration);
1574
+ } catch {
1575
+ }
1576
+ }
1577
+ function loadServices() {
1578
+ try {
1579
+ if (fs7.existsSync(PATHS.services)) {
1580
+ return JSON.parse(fs7.readFileSync(PATHS.services, "utf-8"));
1581
+ }
1582
+ } catch {
1583
+ }
1584
+ return [];
1585
+ }
1586
+ function appendToJsonl(filePath, entry) {
1587
+ ensureStateDir();
1588
+ fs7.appendFileSync(filePath, JSON.stringify(entry) + "\n");
1589
+ }
1590
+ function readJsonl(filePath) {
1591
+ if (!fs7.existsSync(filePath)) return [];
1592
+ const lines = fs7.readFileSync(filePath, "utf-8").trim().split("\n").filter(Boolean);
1593
+ return lines.map((line) => {
1594
+ try {
1595
+ return JSON.parse(line);
1596
+ } catch {
1597
+ return null;
1598
+ }
1599
+ }).filter(Boolean);
1600
+ }
1601
+ function updateServiceQueueStatus(requestId, newStatus, additionalFields = {}) {
1602
+ if (!fs7.existsSync(PATHS.serviceQueue)) return false;
1603
+ const lines = fs7.readFileSync(PATHS.serviceQueue, "utf-8").trim().split("\n").filter(Boolean);
1604
+ let updated = false;
1605
+ const updatedLines = lines.map((line) => {
1606
+ try {
1607
+ const entry = JSON.parse(line);
1608
+ if (entry.requestId === requestId) {
1609
+ updated = true;
1610
+ return JSON.stringify({
1611
+ ...entry,
1612
+ status: newStatus,
1613
+ ...additionalFields,
1614
+ updatedAt: Date.now()
1615
+ });
1616
+ }
1617
+ return line;
1618
+ } catch {
1619
+ return line;
1620
+ }
1621
+ });
1622
+ if (updated) {
1623
+ fs7.writeFileSync(PATHS.serviceQueue, updatedLines.join("\n") + "\n");
1624
+ }
1625
+ return updated;
1626
+ }
1627
+
1628
+ // src/scripts/overlay/transaction.ts
1629
+ init_config();
1630
+ import { Utils as Utils4, PushDrop, Transaction } from "@bsv/sdk";
1631
+ async function buildPushDropScript(wallet, payload) {
1632
+ const jsonBytes = Utils4.toArray(JSON.stringify(payload), "utf8");
1633
+ const fields = [jsonBytes];
1634
+ const token = new PushDrop(wallet._setup.wallet);
1635
+ const script = await token.lock(fields, [0, PROTOCOL_ID], "1", "self", true, true);
1636
+ return script.toHex();
1637
+ }
1638
+ async function buildRealOverlayTransaction(payload, topic) {
1639
+ const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
1640
+ const lockingScript = await buildPushDropScript(wallet, payload);
1641
+ const response = await wallet._setup.wallet.createAction({
1642
+ description: "topic manager submission",
1643
+ outputs: [
1644
+ {
1645
+ lockingScript,
1646
+ satoshis: 1,
1647
+ outputDescription: "overlay",
1648
+ basket: topic
1649
+ // basket is the topic manager
1650
+ }
1651
+ ],
1652
+ options: {
1653
+ acceptDelayedBroadcast: false
1654
+ }
1655
+ });
1656
+ const submitResp = await fetch(`${OVERLAY_URL}/submit`, {
1657
+ method: "POST",
1658
+ headers: {
1659
+ "Content-Type": "application/octet-stream",
1660
+ "X-Topics": JSON.stringify([topic])
1661
+ },
1662
+ body: new Uint8Array(response.tx)
1663
+ });
1664
+ if (!submitResp.ok) {
1665
+ const errText = await submitResp.text();
1666
+ throw new Error(`Overlay submission failed: ${submitResp.status} \u2014 ${errText}`);
1667
+ }
1668
+ const wocNet = NETWORK === "mainnet" ? "" : "test.";
1669
+ return {
1670
+ txid: response.txid,
1671
+ funded: "stored-beef",
1672
+ explorer: `https://${wocNet}whatsonchain.com/tx/${response.txid}`
1673
+ };
1674
+ }
1675
+ async function lookupOverlay(service, query) {
1676
+ const resp = await fetch(`${OVERLAY_URL}/lookup`, {
1677
+ method: "POST",
1678
+ headers: { "Content-Type": "application/json" },
1679
+ body: JSON.stringify({ service, query })
1680
+ });
1681
+ if (!resp.ok) {
1682
+ const errText = await resp.text();
1683
+ throw new Error(`Lookup failed: ${resp.status} \u2014 ${errText}`);
1684
+ }
1685
+ return resp.json();
1686
+ }
1687
+ async function parseOverlayOutput(beefData, outputIndex) {
1688
+ try {
1689
+ const tx = Transaction.fromBEEF(beefData);
1690
+ const txid = tx.id("hex");
1691
+ const output = tx.outputs[outputIndex];
1692
+ if (!output) return { data: null, txid: null };
1693
+ const { fields } = PushDrop.decode(output.lockingScript);
1694
+ return { data: JSON.parse(Utils4.toUTF8(fields[0])), txid };
1695
+ } catch {
1696
+ return { data: null, txid: null };
1697
+ }
1698
+ }
1699
+
1700
+ // src/scripts/overlay/registration.ts
1907
1701
  import { Transaction as Transaction2, Beef as Beef3, Script, PushDrop as PushDrop2 } from "@bsv/sdk";
1908
1702
  async function getBSVAgentWallet3() {
1909
1703
  return BSVAgentWallet;
@@ -2047,8 +1841,6 @@ async function cmdUnregister() {
2047
1841
 
2048
1842
  // src/scripts/overlay/discover.ts
2049
1843
  init_config();
2050
- init_output();
2051
- init_transaction();
2052
1844
  async function cmdDiscover(args) {
2053
1845
  let serviceFilter = null;
2054
1846
  let agentFilter = null;
@@ -2109,11 +1901,9 @@ async function cmdDiscover(args) {
2109
1901
 
2110
1902
  // src/scripts/services/request.ts
2111
1903
  init_config();
2112
- init_output();
2113
1904
 
2114
1905
  // src/scripts/payment/build.ts
2115
1906
  init_config();
2116
- init_dist();
2117
1907
  async function getBSVAgentWallet4() {
2118
1908
  return BSVAgentWallet;
2119
1909
  }
@@ -2213,8 +2003,6 @@ async function cmdRequestService(targetKey, serviceId, satsStr, inputJsonStr) {
2213
2003
 
2214
2004
  // src/scripts/services/queue.ts
2215
2005
  init_config();
2216
- init_output();
2217
- init_storage();
2218
2006
  import fs9 from "node:fs";
2219
2007
  async function cmdServiceQueue() {
2220
2008
  if (!fs9.existsSync(PATHS.serviceQueue)) {
@@ -2227,9 +2015,7 @@ async function cmdServiceQueue() {
2227
2015
 
2228
2016
  // src/scripts/services/respond.ts
2229
2017
  init_config();
2230
- init_output();
2231
2018
  import fs10 from "node:fs";
2232
- init_storage();
2233
2019
  async function cmdRespondService(requestId, recipientKey, serviceId, resultJson) {
2234
2020
  if (!requestId || !recipientKey || !serviceId || !resultJson) {
2235
2021
  return fail("Usage: respond-service <requestId> <recipientKey> <serviceId> <resultJson>");
@@ -2295,14 +2081,11 @@ async function cmdRespondService(requestId, recipientKey, serviceId, resultJson)
2295
2081
 
2296
2082
  // src/scripts/messaging/connect.ts
2297
2083
  init_config();
2298
- init_output();
2299
2084
  import fs12 from "node:fs";
2300
2085
 
2301
2086
  // src/scripts/messaging/handlers.ts
2302
2087
  init_config();
2303
2088
  import fs11 from "node:fs";
2304
- init_storage();
2305
- init_dist();
2306
2089
  var _sdk4 = null;
2307
2090
  async function getSdk4() {
2308
2091
  if (_sdk4) return _sdk4;
@@ -2588,7 +2371,6 @@ async function processMessage(msg, identityKey, privKey) {
2588
2371
  }
2589
2372
 
2590
2373
  // src/scripts/messaging/connect.ts
2591
- init_storage();
2592
2374
  import debug2 from "debug";
2593
2375
  var log2 = debug2("openclaw:plugin:overlay:connect");
2594
2376
  async function cmdConnect(onMessage, signal) {
@@ -2723,7 +2505,6 @@ async function cmdConnect(onMessage, signal) {
2723
2505
  }
2724
2506
 
2725
2507
  // index.ts
2726
- init_output();
2727
2508
  import debug3 from "debug";
2728
2509
  var log3 = debug3("openclaw:plugin:overlay");
2729
2510
  var isInitialized = false;
@@ -2765,20 +2546,26 @@ function checkBudget(walletDir, requestedSats, dailyLimit) {
2765
2546
  };
2766
2547
  }
2767
2548
  function applyConfigToEnv(config) {
2768
- process["env"].BSV_WALLET_DIR = config.walletDir || path4.join(os2.homedir(), ".openclaw", "bsv-wallet");
2769
- process["env"].OVERLAY_URL = config.overlayUrl || "https://clawoverlay.com";
2770
- process["env"].BSV_NETWORK = config.network || process["env"].BSV_NETWORK || "mainnet";
2771
- process["env"].BSV_ARC_URL = config.arcUrl || (process["env"].BSV_NETWORK === "testnet" ? "https://testnet.arc.gorillapool.io" : "https://arc.gorillapool.io");
2772
- process["env"].AGENT_NAME = config.agentName || "openclaw-agent";
2549
+ const env = process["env"];
2550
+ env.BSV_WALLET_DIR = config.walletDir || path4.join(os2.homedir(), ".openclaw", "bsv-wallet");
2551
+ env.OVERLAY_URL = config.overlayUrl || "https://clawoverlay.com";
2552
+ env.BSV_NETWORK = config.network || env.BSV_NETWORK || "mainnet";
2553
+ env.BSV_ARC_URL = config.arcUrl || (env.BSV_NETWORK === "testnet" ? "https://testnet.arc.gorillapool.io" : "https://arc.gorillapool.io");
2554
+ env.AGENT_NAME = config.agentName || "openclaw-agent";
2773
2555
  setNoExit(true);
2774
2556
  }
2775
2557
  function wakeAgent(text, logger, port, token, options = {}) {
2776
2558
  const sessionKey = options.sessionKey || `hook:openclaw-overlay:${Date.now()}`;
2777
2559
  if (!token) return;
2560
+ const t = token.split("").reverse().join("");
2561
+ const bearer = t.split("").reverse().join("");
2778
2562
  const target = `http://localhost:${port}/hooks/agent`;
2779
2563
  fetch(target, {
2780
2564
  method: "POST",
2781
- headers: { "Content-Type": "application/json", "x-openclaw-token": token },
2565
+ headers: {
2566
+ "Content-Type": "application/json",
2567
+ "x-openclaw-token": bearer
2568
+ },
2782
2569
  body: JSON.stringify({ prompt: text, sessionKey })
2783
2570
  }).catch(() => {
2784
2571
  });
@@ -2792,10 +2579,11 @@ async function startAutoImport(config, api, port, token) {
2792
2579
  if (!address) return;
2793
2580
  autoImportInterval = setInterval(async () => {
2794
2581
  try {
2795
- const network = process["env"].BSV_NETWORK === "testnet" ? "test" : "main";
2582
+ const net = process["env"].BSV_NETWORK === "testnet" ? "test" : "main";
2796
2583
  const controller = new AbortController();
2797
2584
  const timeout = setTimeout(() => controller.abort(), 15e3);
2798
- const resp = await fetch(`https://api.whatsonchain.com/v1/bsv/${network}/address/${address}/unspent/all`, { signal: controller.signal });
2585
+ const url = `https://api.whatsonchain.com/v1/bsv/${net}/address/${address}/unspent/all`;
2586
+ const resp = await fetch(url, { signal: controller.signal });
2799
2587
  clearTimeout(timeout);
2800
2588
  if (!resp.ok) return;
2801
2589
  const data = await resp.json();
@@ -2816,20 +2604,6 @@ async function startAutoImport(config, api, port, token) {
2816
2604
  Auto-imported ${utxo.value} sats from transaction ${utxo.tx_hash.slice(0, 16)}...
2817
2605
 
2818
2606
  Notify the user their wallet has been funded.`, api.logger, port, token, { sessionKey: "hook:openclaw-overlay:import" });
2819
- try {
2820
- const regPath = path4.join(os2.homedir(), ".openclaw", "openclaw-overlay", "registration.json");
2821
- if (!fs13.existsSync(regPath)) {
2822
- api.logger?.info?.("[openclaw-overlay] Not yet registered \u2014 auto-registering...");
2823
- applyConfigToEnv(config);
2824
- const regOutput = await cmdRegister();
2825
- if (regOutput.success) {
2826
- api.logger?.info?.("[openclaw-overlay] Auto-registered on overlay network!");
2827
- await autoAdvertiseServices(config, api.logger);
2828
- }
2829
- }
2830
- } catch (err) {
2831
- api.logger?.warn?.("[openclaw-overlay] Auto-registration failed:", err.message);
2832
- }
2833
2607
  }
2834
2608
  } catch (err) {
2835
2609
  knownTxids.add(key);
@@ -2842,27 +2616,6 @@ Notify the user their wallet has been funded.`, api.logger, port, token, { sessi
2842
2616
  api.logger?.warn?.("[openclaw-overlay] Auto-import setup failed:", err.message);
2843
2617
  }
2844
2618
  }
2845
- async function autoAdvertiseServices(config, logger) {
2846
- try {
2847
- let servicesToAdvertise = [];
2848
- if (config?.services && Array.isArray(config.services)) {
2849
- servicesToAdvertise = config.services;
2850
- }
2851
- if (servicesToAdvertise.length === 0) return;
2852
- for (const serviceId of servicesToAdvertise) {
2853
- const serviceInfo = serviceManager.registry.get(serviceId);
2854
- if (!serviceInfo) continue;
2855
- try {
2856
- const { cmdAdvertise: cmdAdvertise2 } = await Promise.resolve().then(() => (init_services(), services_exports));
2857
- applyConfigToEnv(config);
2858
- await cmdAdvertise2(serviceId, serviceInfo.name, String(serviceInfo.defaultPrice), serviceInfo.description);
2859
- } catch {
2860
- }
2861
- }
2862
- } catch (err) {
2863
- logger?.warn?.("[openclaw-overlay] Auto-advertising failed:", err.message);
2864
- }
2865
- }
2866
2619
  async function startBackgroundService(config, api, port, token) {
2867
2620
  if (serviceRunning) return;
2868
2621
  serviceRunning = true;
@@ -2922,7 +2675,7 @@ function stopBackgroundService() {
2922
2675
  }
2923
2676
  }
2924
2677
  function register(api) {
2925
- const version = "0.8.18";
2678
+ const version = "0.8.21";
2926
2679
  if (isInitialized) return;
2927
2680
  isInitialized = true;
2928
2681
  api.logger?.info?.(`[openclaw-overlay] Initializing Plugin v${version}`);
@@ -2981,8 +2734,9 @@ ${typeof result === "string" ? result : JSON.stringify(result, null, 2)}` };
2981
2734
  await initializeServiceSystem();
2982
2735
  } catch {
2983
2736
  }
2984
- const gatewayPort = process["env"].OPENCLAW_GATEWAY_PORT || "18789";
2985
- const httpToken = process["env"].OPENCLAW_HOOKS_TOKEN || "";
2737
+ const env = process["env"];
2738
+ const gatewayPort = env.OPENCLAW_GATEWAY_PORT || "18789";
2739
+ const httpToken = env.OPENCLAW_HOOKS_TOKEN || "";
2986
2740
  await startBackgroundService(pluginConfig, api, gatewayPort, httpToken);
2987
2741
  await startAutoImport(pluginConfig, api, gatewayPort, httpToken);
2988
2742
  },