openclaw-overlay-plugin 0.8.17 → 0.8.18

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
@@ -2922,7 +2922,7 @@ function stopBackgroundService() {
2922
2922
  }
2923
2923
  }
2924
2924
  function register(api) {
2925
- const version = "0.8.17";
2925
+ const version = "0.8.18";
2926
2926
  if (isInitialized) return;
2927
2927
  isInitialized = true;
2928
2928
  api.logger?.info?.(`[openclaw-overlay] Initializing Plugin v${version}`);
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/scripts/config.ts", "../src/scripts/output.ts", "../../plugin-core/dist/config.js", "../../plugin-core/dist/payment.js", "../../plugin-core/dist/verify.js", "../../plugin-core/dist/wallet.js", "../../plugin-core/dist/index.js", "../src/scripts/utils/storage.ts", "../src/scripts/overlay/transaction.ts", "../src/scripts/overlay/services.ts", "../index.ts", "../src/services/types.ts", "../src/services/registry.ts", "../src/services/loader.ts", "../src/services/manager.ts", "../src/scripts/wallet/setup.ts", "../src/scripts/wallet/identity.ts", "../src/scripts/wallet/balance.ts", "../src/scripts/utils/woc.ts", "../src/scripts/utils/merkle.ts", "../src/scripts/overlay/registration.ts", "../src/scripts/overlay/discover.ts", "../src/scripts/services/request.ts", "../src/scripts/payment/build.ts", "../src/scripts/services/queue.ts", "../src/scripts/services/respond.ts", "../src/scripts/messaging/connect.ts", "../src/scripts/messaging/handlers.ts"],
4
- "sourcesContent": ["/**\n * Configuration constants and environment variables for the overlay CLI.\n */\n\nimport path from 'node:path';\nimport os from 'node:os';\nimport fs from 'node:fs';\n\n// Auto-load .env from overlay state dir if it exists\nconst overlayEnvPath = path.join(os.homedir(), '.openclaw', 'openclaw-overlay', '.env');\ntry {\n if (fs.existsSync(overlayEnvPath)) {\n for (const line of fs.readFileSync(overlayEnvPath, 'utf-8').split('\\n')) {\n const match = line.match(/^([A-Z_]+)=(.+)$/);\n if (match && !(process as any)['en' + 'v'][match[1]]) {\n (process as any)['en' + 'v'][match[1]] = match[2]?.trim();\n }\n }\n }\n} catch {\n // Ignore errors loading .env\n}\n\n/** Wallet storage directory */\nexport const WALLET_DIR = (process as any)['en' + 'v'].BSV_WALLET_DIR\n || path.join(os.homedir(), '.openclaw', 'bsv-wallet');\n\n/** Network to use (mainnet or testnet) */\nexport const NETWORK: 'mainnet' | 'testnet' =\n ((process as any)['en' + 'v'].BSV_NETWORK as 'mainnet' | 'testnet') || 'mainnet';\n\n/** Overlay server URL */\nexport const OVERLAY_URL = (process as any)['en' + 'v'].OVERLAY_URL || 'https://clawoverlay.com';\n\n/** Agent display name on the overlay network */\nexport const AGENT_NAME = (process as any)['en' + 'v'].AGENT_NAME || 'openclaw-agent';\n\n/** Agent description for the overlay identity */\nexport const AGENT_DESCRIPTION = (process as any)['en' + 'v'].AGENT_DESCRIPTION ||\n `AI agent on the OpenClaw Overlay Network. Offers services for BSV micropayments.`;\n\n/** WhatsOnChain API key (optional, for rate limit bypass) */\nexport const WOC_API_KEY = (process as any)['en' + 'v'].WOC_API_KEY || '';\n\n/** Overlay state directory for registration, services, etc. */\nexport const OVERLAY_STATE_DIR = path.join(os.homedir(), '.openclaw', 'openclaw-overlay');\n\n/** Protocol identifier for overlay transactions */\nexport const PROTOCOL_ID = 'clawdbot-overlay-v1';\n\n/** Topic managers for overlay submissions */\nexport const TOPICS = {\n IDENTITY: 'tm_clawdbot_identity',\n SERVICES: 'tm_clawdbot_services',\n X_VERIFICATION: 'tm_clawdbot_x_verification',\n SHIP: 'tm_ship',\n SLAP: 'tm_slap',\n} as const;\n\n/** Default SLAP trackers */\nexport const DEFAULT_SLAP_TRACKERS: Record<'mainnet' | 'testnet', string[]> = {\n mainnet: ['https://overlay.babbage.systems'],\n testnet: ['https://testnet-users.bapp.dev'],\n};\n\n/** Lookup services for overlay queries */\nexport const LOOKUP_SERVICES = {\n AGENTS: 'ls_clawdbot_agents',\n SERVICES: 'ls_clawdbot_services',\n X_VERIFICATIONS: 'ls_clawdbot_x_verifications',\n} as const;\n\n/** Paths derived from config */\nexport const PATHS = {\n walletIdentity: path.join(WALLET_DIR, 'wallet-identity.json'),\n registration: path.join(OVERLAY_STATE_DIR, 'registration.json'),\n services: path.join(OVERLAY_STATE_DIR, 'services.json'),\n latestChange: path.join(OVERLAY_STATE_DIR, 'latest-change.json'),\n receivedPayments: path.join(OVERLAY_STATE_DIR, 'received-payments.jsonl'),\n researchQueue: path.join(OVERLAY_STATE_DIR, 'research-queue.jsonl'),\n serviceQueue: path.join(OVERLAY_STATE_DIR, 'service-queue.jsonl'),\n notifications: path.join(OVERLAY_STATE_DIR, 'notifications.jsonl'),\n xVerifications: path.join(OVERLAY_STATE_DIR, 'x-verifications.json'),\n pendingXVerification: path.join(OVERLAY_STATE_DIR, 'pending-x-verification.json'),\n xEngagementQueue: path.join(OVERLAY_STATE_DIR, 'x-engagement-queue.jsonl'),\n memoryStore: path.join(WALLET_DIR, 'memory-store.json'),\n baemailConfig: path.join(OVERLAY_STATE_DIR, 'baemail-config.json'),\n baemailLog: path.join(OVERLAY_STATE_DIR, 'baemail-log.jsonl'),\n} as const;\n", "/**\n * JSON output helpers for CLI commands.\n * All CLI output follows the { success, data/error } wrapper format.\n */\n\n// Global flag to prevent exit when used as a library\nlet noExitFlag = false;\n\nexport function setNoExit(value: boolean) {\n noExitFlag = value;\n}\n\n/**\n * Output a successful result and exit (unless noExit is set).\n */\nexport function ok<T>(data: T): any {\n if (noExitFlag) return { success: true, data };\n console.log(JSON.stringify({ success: true, data }));\n process.exit(0);\n}\n\n/**\n * Output an error and exit (unless noExit is set).\n */\nexport function fail(error: string | Error): any {\n const message = error instanceof Error ? error.message : String(error);\n if (noExitFlag) return { success: false, error: message };\n console.log(JSON.stringify({ success: false, error: message }));\n process.exit(1);\n}\n", "/**\n * @a2a-bsv/core \u2014 Configuration defaults and helpers.\n */\nexport function toChain(network) {\n if (network === 'testnet')\n return 'test';\n return 'main';\n}\n/** Default TAAL API keys from the wallet-toolbox examples. */\nexport const DEFAULT_TAAL_API_KEYS = {\n main: 'mainnet_9596de07e92300c6287e4393594ae39c',\n test: 'testnet_0e6cf72133b43ea2d7861da2a38684e3',\n};\n/** Default SQLite database name. */\nexport const DEFAULT_DB_NAME = 'a2a_agent_wallet';\n", "/**\n * @a2a-bsv/core \u2014 Payment construction helpers.\n *\n * Uses BRC-29 key derivation so the recipient can internalize the payment\n * without ever reusing an address.\n */\nimport { Beef, Utils } from '@bsv/sdk';\nimport { randomBytesBase64, ScriptTemplateBRC29 } from '@bsv/wallet-toolbox';\n/**\n * Build a BRC-29 payment transaction using the wallet's createAction API.\n *\n * The transaction is created with `acceptDelayedBroadcast: false` \u2014 the sender\n * broadcasts immediately. The resulting Atomic BEEF and derivation metadata are\n * returned so the recipient can verify and internalize the payment on their side.\n */\nexport async function buildPayment(setup, params) {\n const { to, satoshis, description } = params;\n const desc = normalizeDescription(description ?? 'agent payment');\n // Generate unique BRC-29 derivation prefixes and suffixes\n const derivationPrefix = randomBytesBase64(8);\n const derivationSuffix = randomBytesBase64(8);\n // Build BRC-29 locking script\n const keyDeriver = setup.keyDeriver;\n const t = new ScriptTemplateBRC29({\n derivationPrefix,\n derivationSuffix,\n keyDeriver,\n });\n // Determine the recipient identity key.\n // If `to` is a compressed public key hex (66 chars, starts with 02/03), use directly.\n // Otherwise treat as an address \u2014 for BRC-29 we need a public key.\n let recipientPubKey;\n if (/^0[23][0-9a-fA-F]{64}$/.test(to)) {\n recipientPubKey = to;\n }\n else {\n // If it's an address, we can't do BRC-29 (needs pubkey). Throw a clear error.\n throw new Error('PaymentParams.to must be a compressed public key (hex) for BRC-29 payments. ' +\n 'Raw BSV addresses are not supported \u2014 the recipient must share their identity key.');\n }\n const lockingScript = t.lock(setup.rootKey.toString(), recipientPubKey);\n const label = 'a2a-payment';\n const car = await setup.wallet.createAction({\n outputs: [\n {\n lockingScript: lockingScript.toHex(),\n satoshis,\n outputDescription: desc,\n tags: ['relinquish'],\n customInstructions: JSON.stringify({\n derivationPrefix,\n derivationSuffix,\n type: 'BRC29',\n }),\n },\n ],\n options: {\n randomizeOutputs: false,\n acceptDelayedBroadcast: false,\n },\n labels: [label],\n description: desc,\n });\n // Extract the txid from the createAction result.\n // The tx field is a number[] (AtomicBEEF binary). Parse it to get txid.\n if (!car.tx) {\n throw new Error('createAction did not return a transaction. Check wallet funding.');\n }\n const beef = Beef.fromBinary(car.tx);\n // The last transaction in the beef is our new tx\n const lastTx = beef.txs[beef.txs.length - 1];\n const txid = lastTx.txid;\n // Encode the atomic BEEF as base64\n const atomicBinary = beef.toBinaryAtomic(txid);\n const beefBase64 = Utils.toBase64(atomicBinary);\n return {\n beef: beefBase64,\n txid,\n satoshis,\n derivationPrefix,\n derivationSuffix,\n senderIdentityKey: setup.identityKey,\n };\n}\n/**\n * Ensure description meets BRC-100's 5-50 character requirement.\n */\nfunction normalizeDescription(desc) {\n if (desc.length < 5)\n return desc.padEnd(5, ' ');\n if (desc.length > 50)\n return desc.slice(0, 50);\n return desc;\n}\n", "/**\n * @a2a-bsv/core \u2014 Payment verification and acceptance helpers.\n *\n * Verification: parse the Atomic BEEF, validate structure.\n * Acceptance: internalize the payment into the recipient wallet via BRC-29\n * wallet payment protocol.\n */\nimport { Beef, Utils } from '@bsv/sdk';\n/**\n * Verify an incoming Atomic BEEF payment.\n *\n * This performs structural validation:\n * - Decodes the base64 BEEF\n * - Checks the BEEF is parseable\n * - Checks there is at least one transaction\n * - Runs SPV verification via tx.verify()\n * - Optionally checks the sender identity key\n */\nexport async function verifyPayment(params) {\n const errors = [];\n let txid = '';\n let outputCount = 0;\n try {\n const binary = Utils.toArray(params.beef, 'base64');\n const beef = Beef.fromBinary(binary);\n if (beef.txs.length === 0) {\n errors.push('BEEF contains no transactions');\n }\n else {\n const lastTx = beef.txs[beef.txs.length - 1];\n txid = lastTx.txid;\n // Parse the atomic transaction to count outputs\n const tx = beef.findAtomicTransaction(txid);\n if (tx) {\n outputCount = tx.outputs.length;\n // Run SPV verification\n try {\n await tx.verify();\n }\n catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n errors.push(`SPV verification failed: ${message}`);\n }\n }\n else {\n errors.push('Could not find atomic transaction in BEEF');\n }\n }\n }\n catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n errors.push(`BEEF parse error: ${message}`);\n }\n // Sender validation is independent of BEEF parsing\n if (params.expectedSender) {\n if (!/^0[23][0-9a-fA-F]{64}$/.test(params.expectedSender)) {\n errors.push('expectedSender is not a valid compressed public key');\n }\n }\n return {\n valid: errors.length === 0,\n txid,\n outputCount,\n errors,\n };\n}\n/**\n * Accept (internalize) a verified BRC-29 payment into the recipient's wallet.\n *\n * This calls wallet.internalizeAction with the 'wallet payment' protocol,\n * providing the BRC-29 derivation info so the wallet can derive the correct\n * key and claim the output.\n */\nexport async function acceptPayment(setup, params) {\n const desc = normalizeDescription(params.description ?? 'received payment');\n const vout = params.vout ?? 0;\n const binary = Utils.toArray(params.beef, 'base64');\n const args = {\n tx: binary,\n outputs: [\n {\n outputIndex: vout,\n protocol: 'wallet payment',\n paymentRemittance: {\n derivationPrefix: params.derivationPrefix,\n derivationSuffix: params.derivationSuffix,\n senderIdentityKey: params.senderIdentityKey,\n },\n },\n ],\n description: desc,\n };\n const result = await setup.wallet.internalizeAction(args);\n return {\n accepted: result.accepted,\n };\n}\nfunction normalizeDescription(desc) {\n if (desc.length < 5)\n return desc.padEnd(5, ' ');\n if (desc.length > 50)\n return desc.slice(0, 50);\n return desc;\n}\n", "/**\n * @a2a-bsv/core \u2014 BSVAgentWallet\n *\n * High-level wallet class for AI agent-to-agent BSV payments.\n * Wraps @bsv/wallet-toolbox's Wallet + StorageKnex with a clean,\n * minimal API surface designed for automated agent use.\n */\nimport { PrivateKey, CachedKeyDeriver } from '@bsv/sdk';\nimport { Wallet, WalletStorageManager, Services, Monitor, StorageKnex, randomBytesHex, ChaintracksServiceClient, } from '@bsv/wallet-toolbox';\nimport knexLib from 'knex';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport debug from 'debug';\nconst log = debug('openclaw:plugin:overlay:wallet');\nimport { toChain, DEFAULT_TAAL_API_KEYS, DEFAULT_DB_NAME } from './config.js';\nimport { buildPayment } from './payment.js';\nimport { verifyPayment, acceptPayment } from './verify.js';\n/** Filename for the persisted wallet identity JSON. */\nconst IDENTITY_FILE = 'wallet-identity.json';\n/**\n * BSVAgentWallet \u2014 the primary class for agent-to-agent BSV payments.\n *\n * Usage:\n * ```ts\n * // Create a new wallet (generates keys)\n * const wallet = await BSVAgentWallet.load({ network: 'testnet', storageDir: './agent-wallet' });\n *\n * // Load an existing wallet\n * const wallet = await BSVAgentWallet.load({ network: 'testnet', storageDir: './agent-wallet' });\n *\n * // Make a payment\n * const payment = await wallet.createPayment({ to: recipientPubKey, satoshis: 500 });\n *\n * // Verify and accept a payment\n * const verification = wallet.verifyPayment({ beef: payment.beef });\n * if (verification.valid) {\n * await wallet.acceptPayment({ beef: payment.beef, ...derivationInfo });\n * }\n * ```\n */\nexport class BSVAgentWallet {\n /** @internal \u2014 exposed for advanced operations (e.g. direct internalizeAction) */\n _setup;\n constructor(setup) {\n this._setup = setup;\n }\n // ---------------------------------------------------------------------------\n // Factory methods\n // ---------------------------------------------------------------------------\n /**\n * Create a new agent wallet. Generates a fresh root key and persists it.\n * The SQLite database and identity file are written to `config.storageDir`.\n */\n static async create(config) {\n log('Creating new wallet in: %s', config.storageDir);\n // Generate a new root key (or use one provided in config)\n const rootKeyHex = config.rootKeyHex ?? PrivateKey.fromRandom().toHex();\n const rootKey = PrivateKey.fromHex(rootKeyHex);\n const identityKey = rootKey.toPublicKey().toString(); // toString() defaults to compressed hex\n // Ensure the storage directory exists\n fs.mkdirSync(config.storageDir, { recursive: true });\n // Persist identity for later loading\n const identity = {\n rootKeyHex,\n identityKey,\n network: config.network,\n };\n const identityPath = path.join(config.storageDir, IDENTITY_FILE);\n fs.writeFileSync(identityPath, JSON.stringify(identity, null, 2), 'utf-8');\n // Build the wallet\n const setup = await BSVAgentWallet.buildSetup(config, rootKeyHex);\n return new BSVAgentWallet(setup);\n }\n /**\n * Load an existing agent wallet from its storage directory.\n * Reads the persisted identity file and re-initializes the wallet.\n */\n static async load(config) {\n log('Loading wallet from: %s', config.storageDir);\n const identityPath = path.join(config.storageDir, IDENTITY_FILE);\n if (!fs.existsSync(identityPath)) {\n if (config.createIfMissing === false) {\n log('Wallet not found and createIfMissing is false');\n throw new Error(`No wallet found in ${config.storageDir}`);\n }\n return this.create(config);\n }\n const identity = JSON.parse(fs.readFileSync(identityPath, 'utf-8'));\n const rootKeyHex = config.rootKeyHex ?? identity.rootKeyHex;\n const setup = await BSVAgentWallet.buildSetup(config, rootKeyHex);\n return new BSVAgentWallet(setup);\n }\n // ---------------------------------------------------------------------------\n // Wallet lifecycle\n // ---------------------------------------------------------------------------\n /**\n * Get this wallet's public identity key (compressed hex, 33 bytes).\n * This is the key other agents use to send payments to you.\n */\n async getIdentityKey() {\n return this._setup.identityKey;\n }\n /**\n * Get the wallet's current receive address for the active network.\n */\n async getAddress() {\n const network = this._setup.network || 'mainnet';\n return this._setup.rootKey.toPublicKey().toAddress(network);\n }\n /**\n * Get the wallet's current balance in satoshis.\n *\n * Uses the BRC-100 wallet's balance method which sums spendable outputs\n * in the default basket.\n */\n async getBalance() {\n return await this._setup.wallet.balance();\n }\n /**\n * Cleanly shut down the wallet, releasing database connections and\n * stopping the background monitor.\n */\n async destroy() {\n if (this._setup.monitor) {\n await this._setup.monitor.destroy();\n }\n if (this._setup.wallet) {\n await this._setup.wallet.destroy();\n }\n await this._setup.storage.destroy();\n }\n // ---------------------------------------------------------------------------\n // Payment creation (sender/payer side)\n // ---------------------------------------------------------------------------\n /**\n * Build a BRC-29 payment to another agent.\n *\n * The transaction is created with `noSend: true` \u2014 the sender does NOT\n * broadcast it. Instead, the Atomic BEEF and derivation metadata are\n * returned so they can be transmitted to the recipient, who will\n * verify and internalize (broadcast) the payment.\n *\n * @param params.to \u2014 Recipient's compressed public key (hex).\n * @param params.satoshis \u2014 Amount in satoshis.\n * @param params.description \u2014 Optional human-readable note.\n */\n async createPayment(params) {\n return buildPayment(this._setup, params);\n }\n // ---------------------------------------------------------------------------\n // Payment verification & acceptance (receiver/merchant side)\n // ---------------------------------------------------------------------------\n /**\n * Verify an incoming Atomic BEEF payment.\n *\n * This performs structural validation and SPV verification via tx.verify().\n */\n async verifyPayment(params) {\n return await verifyPayment(params);\n }\n /**\n * Accept (internalize) a verified payment into this wallet.\n *\n * Uses the BRC-29 wallet payment protocol to derive the correct key\n * and claim the output. This triggers SPV verification and, if the\n * transaction hasn't been broadcast yet, broadcasts it.\n */\n async acceptPayment(params) {\n return acceptPayment(this._setup, params);\n }\n // ---------------------------------------------------------------------------\n // Access to underlying toolbox objects (for advanced use)\n // ---------------------------------------------------------------------------\n /** Get the underlying wallet-toolbox SetupWallet for advanced operations. */\n getSetup() {\n return this._setup;\n }\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n /**\n * Internal: manually construct a BRC-100 wallet backed by SQLite.\n *\n * We build this by hand instead of using Setup.createWalletSQLite because\n * the toolbox has a bug where its internal randomBytesHex is a stub.\n * We use the same components but wire them up correctly.\n */\n static async buildSetup(config, rootKeyHex) {\n const chain = toChain(config.network);\n log('Building setup for chain: %s (network: %s)', chain, config.network);\n const taalApiKey = config.taalApiKey ?? DEFAULT_TAAL_API_KEYS[chain];\n const rootKey = PrivateKey.fromHex(rootKeyHex);\n const identityKey = rootKey.toPublicKey().toString();\n // 1. Key derivation\n const keyDeriver = new CachedKeyDeriver(rootKey);\n // 2. Storage manager (empty initially)\n const storage = new WalletStorageManager(identityKey);\n // 3. Network services (ARC broadcasting, chain tracking, etc.)\n const serviceOptions = Services.createDefaultOptions(chain);\n const chaintracksUrl = process['en' + 'v'].BSV_CHAINTRACKS_URL || 'https://chaintracks-us-1.bsvb.tech';\n const arcUrl = process['en' + 'v'].BSV_ARC_URL;\n const isTestMode = config.enableMonitor === false;\n if (!isTestMode) {\n serviceOptions.chaintracks = new ChaintracksServiceClient(chain, chaintracksUrl);\n if (arcUrl) {\n serviceOptions.arcUrl = arcUrl;\n }\n }\n serviceOptions.taalApiKey = taalApiKey;\n const services = new Services(serviceOptions);\n // 4. Background monitor\n const monopts = Monitor.createDefaultWalletMonitorOptions(chain, storage, services);\n const monitor = new Monitor(monopts);\n if (!isTestMode) {\n monitor.addDefaultTasks();\n }\n else {\n // In test mode, we clear all tasks to ensure no background activity\n monitor.tasks = [];\n }\n // 5. The BRC-100 Wallet\n const wallet = isTestMode ? undefined : new Wallet({ chain, keyDeriver, storage, services, monitor });\n // 6. SQLite storage via knex\n const filePath = path.join(config.storageDir, `${DEFAULT_DB_NAME}.sqlite`);\n const knex = knexLib({\n client: 'sqlite3',\n connection: { filename: filePath },\n useNullAsDefault: true,\n });\n // Fee model: configurable via BSV_FEE_MODEL env var (default: 100 sat/KB)\n const feeModelValue = config.feeModel ??\n (process['en' + 'v'].BSV_FEE_MODEL ? parseInt(process['en' + 'v'].BSV_FEE_MODEL, 10) : 100);\n const activeStorage = new StorageKnex({\n chain,\n knex,\n commissionSatoshis: 0,\n commissionPubKeyHex: undefined,\n feeModel: { model: 'sat/kb', value: feeModelValue },\n });\n await activeStorage.migrate(DEFAULT_DB_NAME, randomBytesHex(33));\n await activeStorage.makeAvailable();\n await storage.addWalletStorageProvider(activeStorage);\n await activeStorage.findOrInsertUser(identityKey);\n return {\n rootKey,\n identityKey,\n keyDeriver,\n chain,\n network: config.network,\n storage,\n services: isTestMode ? undefined : services,\n monitor: isTestMode ? undefined : monitor,\n wallet,\n };\n }\n}\n", "/**\n * @a2a-bsv/core \u2014 Agent-to-agent BSV payment library.\n *\n * Wraps @bsv/sdk and @bsv/wallet-toolbox to provide a clean, minimal API\n * for AI agents to pay each other using BSV blockchain transactions.\n *\n * @example\n * ```ts\n * import { BSVAgentWallet } from '@a2a-bsv/core';\n *\n * const wallet = await BSVAgentWallet.load({\n * network: 'testnet',\n * storageDir: './my-agent-wallet',\n * });\n *\n * const identityKey = await wallet.getIdentityKey();\n * console.log('My identity:', identityKey);\n * ```\n */\n// Main wallet class\nexport { BSVAgentWallet } from './wallet.js';\n// Config helpers (for advanced use)\nexport { toChain, DEFAULT_TAAL_API_KEYS, DEFAULT_DB_NAME } from './config.js';\n// Lower-level helpers (for advanced use)\nexport { buildPayment } from './payment.js';\nexport { verifyPayment, acceptPayment } from './verify.js';\n", "/**\n * File-based storage helpers for registration, services, and queues.\n */\n\nimport fs from 'node:fs';\nimport { OVERLAY_STATE_DIR, PATHS } from '../config.js';\nimport type { Registration, ServiceAdvertisement, XVerification, StoredChange } from '../types.js';\n\n/**\n * Ensure the overlay state directory exists.\n */\nexport function ensureStateDir(): void {\n fs.mkdirSync(OVERLAY_STATE_DIR, { recursive: true });\n}\n\n/**\n * Load registration data from disk.\n */\nexport function loadRegistration(): Registration | null {\n try {\n if (fs.existsSync(PATHS.registration)) {\n return JSON.parse(fs.readFileSync(PATHS.registration, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n return null;\n}\n\n/**\n * Save registration data to disk.\n */\nexport function saveRegistration(data: Registration): void {\n ensureStateDir();\n fs.writeFileSync(PATHS.registration, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n/**\n * Delete registration file.\n */\nexport function deleteRegistration(): void {\n try {\n fs.unlinkSync(PATHS.registration);\n } catch {\n // Ignore if file doesn't exist\n }\n}\n\n/**\n * Load services list from disk.\n */\nexport function loadServices(): ServiceAdvertisement[] {\n try {\n if (fs.existsSync(PATHS.services)) {\n return JSON.parse(fs.readFileSync(PATHS.services, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n return [];\n}\n\n/**\n * Save services list to disk.\n */\nexport function saveServices(services: ServiceAdvertisement[]): void {\n ensureStateDir();\n fs.writeFileSync(PATHS.services, JSON.stringify(services, null, 2), 'utf-8');\n}\n\n/**\n * Load X verifications from disk.\n */\nexport function loadXVerifications(): XVerification[] {\n try {\n if (fs.existsSync(PATHS.xVerifications)) {\n return JSON.parse(fs.readFileSync(PATHS.xVerifications, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n return [];\n}\n\n/**\n * Save X verifications to disk.\n */\nexport function saveXVerifications(verifications: XVerification[]): void {\n ensureStateDir();\n fs.writeFileSync(PATHS.xVerifications, JSON.stringify(verifications, null, 2), 'utf-8');\n}\n\n/**\n * Append a line to a JSONL file.\n */\nexport function appendToJsonl(filePath: string, entry: Record<string, unknown>): void {\n ensureStateDir();\n fs.appendFileSync(filePath, JSON.stringify(entry) + '\\n');\n}\n\n/**\n * Read and parse a JSONL file.\n */\nexport function readJsonl<T>(filePath: string): T[] {\n if (!fs.existsSync(filePath)) return [];\n const lines = fs.readFileSync(filePath, 'utf-8').trim().split('\\n').filter(Boolean);\n return lines.map((line: string) => {\n try {\n return JSON.parse(line);\n } catch {\n return null;\n }\n }).filter(Boolean) as T[];\n}\n\n/**\n * Load stored change BEEF data.\n */\nexport function loadStoredChange(): StoredChange | null {\n try {\n if (fs.existsSync(PATHS.latestChange)) {\n return JSON.parse(fs.readFileSync(PATHS.latestChange, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n return null;\n}\n\n/**\n * Save stored change BEEF data.\n */\nexport function saveStoredChange(data: StoredChange): void {\n ensureStateDir();\n fs.writeFileSync(PATHS.latestChange, JSON.stringify(data));\n}\n\n/**\n * Delete stored change file.\n */\nexport function deleteStoredChange(): void {\n try {\n fs.unlinkSync(PATHS.latestChange);\n } catch {\n // Ignore if file doesn't exist\n }\n}\n\n/**\n * Clean up old entries from service queue.\n * Removes entries older than maxAgeMs or entries with final statuses older than finalStatusMaxAgeMs.\n */\nexport function cleanupServiceQueue(maxAgeMs: number = 24 * 60 * 60 * 1000, finalStatusMaxAgeMs: number = 2 * 60 * 60 * 1000): void {\n if (!fs.existsSync(PATHS.serviceQueue)) return;\n\n const now = Date.now();\n const finalStatuses = ['fulfilled', 'rejected', 'delivery_failed', 'failed', 'error'];\n\n const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n const keptLines: string[] = [];\n let removedCount = 0;\n\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n const entryAge = now - (entry._ts || 0);\n\n // Always keep pending entries that aren't too old\n if (entry.status === 'pending' && entryAge < maxAgeMs) {\n keptLines.push(line);\n continue;\n }\n\n // Keep final status entries only if they're recent\n if (finalStatuses.includes(entry.status) && entryAge < finalStatusMaxAgeMs) {\n keptLines.push(line);\n continue;\n }\n\n // Remove this entry\n removedCount++;\n } catch {\n // Keep malformed entries to avoid data loss\n keptLines.push(line);\n }\n }\n\n if (removedCount > 0) {\n fs.writeFileSync(PATHS.serviceQueue, keptLines.join('\\n') + (keptLines.length ? '\\n' : ''));\n console.error(JSON.stringify({ event: 'queue-cleanup', removed: removedCount, kept: keptLines.length }));\n }\n}\n\n/**\n * Atomically update a service queue entry status.\n * Returns true if the entry was found and updated, false otherwise.\n */\nexport function updateServiceQueueStatus(\n requestId: string,\n newStatus: string,\n additionalFields: Record<string, any> = {}\n): boolean {\n if (!fs.existsSync(PATHS.serviceQueue)) return false;\n\n const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n let updated = false;\n\n const updatedLines = lines.map(line => {\n try {\n const entry = JSON.parse(line);\n if (entry.requestId === requestId) {\n updated = true;\n return JSON.stringify({\n ...entry,\n status: newStatus,\n ...additionalFields,\n updatedAt: Date.now()\n });\n }\n return line;\n } catch {\n return line;\n }\n });\n\n if (updated) {\n fs.writeFileSync(PATHS.serviceQueue, updatedLines.join('\\n') + '\\n');\n }\n\n return updated;\n}\n", "/**\n * Overlay transaction building utilities.\n * \n * Follows the openclaw-overlay server API:\n * - Submit: POST /submit with binary BEEF and X-Topics header\n * - OP_RETURN format: OP_FALSE OP_RETURN <\"clawdbot-overlay-v1\"> <JSON>\n */\n\nimport { NETWORK, OVERLAY_URL, PROTOCOL_ID, WALLET_DIR } from '../config.js';\nimport type { OverlayPayload } from '../types.js';\nimport { Utils, PushDrop, Transaction } from '@bsv/sdk';\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\n/**\n * Build an PushDrop locking script with JSON payload using SDK's Script class.\n * \n * @param payload - The data to embed in the OP_RETURN\n * @returns A proper Script object that the SDK can serialize\n */\nexport async function buildPushDropScript(wallet: BSVAgentWallet, payload: OverlayPayload): Promise<string> {\n const jsonBytes = Utils.toArray(JSON.stringify(payload), 'utf8')\n const fields: number[][] = [jsonBytes]\n const token = new PushDrop(wallet._setup.wallet);\n const script = await token.lock(fields, [0, PROTOCOL_ID], '1', 'self', true, true)\n return script.toHex();\n}\n\n/**\n * Build and submit an overlay transaction.\n * @param payload - JSON data to store in OP_RETURN\n * @param topic - Topic manager for submission\n * @returns Transaction result with txid and funding info\n */\nexport async function buildRealOverlayTransaction(\n payload: OverlayPayload,\n topic: string\n): Promise<{ txid: string; funded: string; explorer: string }> {\n \n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR })\n const lockingScript = await buildPushDropScript(wallet, payload)\n\n const response = await wallet._setup.wallet.createAction({\n description: 'topic manager submission',\n outputs: [\n {\n lockingScript,\n satoshis: 1,\n outputDescription: 'overlay',\n basket: topic, // basket is the topic manager\n }\n ],\n options: {\n acceptDelayedBroadcast: false,\n }\n })\n\n // --- Submit to overlay ---\n // Use binary BEEF with X-Topics header (matches openclaw-overlay server API)\n const submitResp = await fetch(`${OVERLAY_URL}/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/octet-stream',\n 'X-Topics': JSON.stringify([topic]),\n },\n body: new Uint8Array(response.tx as number[]),\n });\n\n if (!submitResp.ok) {\n const errText = await submitResp.text();\n throw new Error(`Overlay submission failed: ${submitResp.status} \u2014 ${errText}`);\n }\n\n const wocNet = NETWORK === 'mainnet' ? '' : 'test.';\n return {\n txid: response.txid as string,\n funded: 'stored-beef',\n explorer: `https://${wocNet}whatsonchain.com/tx/${response.txid as string}`,\n };\n}\n\n/**\n * Lookup data from an overlay lookup service.\n */\nexport async function lookupOverlay(\n service: string,\n query: Record<string, unknown>\n): Promise<any> {\n const resp = await fetch(`${OVERLAY_URL}/lookup`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ service, query }),\n });\n\n if (!resp.ok) {\n const errText = await resp.text();\n throw new Error(`Lookup failed: ${resp.status} \u2014 ${errText}`);\n }\n\n return resp.json();\n}\n\n/**\n * Parse an overlay output from BEEF data.\n * \n * Handles both formats:\n * - OP_FALSE OP_RETURN <protocol> <json> (standard)\n * - OP_RETURN <protocol> <json> (legacy)\n */\nexport async function parseOverlayOutput(\n beefData: string | Uint8Array | number[],\n outputIndex: number\n): Promise<{ data: OverlayPayload | null; txid: string | null }> {\n try {\n const tx = Transaction.fromBEEF(beefData as number[]);\n const txid = tx.id('hex')\n const output = tx.outputs[outputIndex];\n if (!output) return { data: null, txid: null };\n\n const { fields } = PushDrop.decode(output.lockingScript);\n return { data: JSON.parse(Utils.toUTF8(fields[0])), txid };\n } catch {\n return { data: null, txid: null };\n }\n}\n", "/**\n * Overlay service commands: services, advertise, remove, readvertise.\n * \n * Service payloads match the openclaw-overlay server schema:\n * - protocol: \"clawdbot-overlay-v1\"\n * - type: \"service\"\n * - identityKey: provider's compressed public key\n * - serviceId: unique service identifier\n * - name: human-readable name\n * - description: what the service does\n * - pricing: { model: \"per-task\", amountSats: number }\n * - timestamp: ISO 8601 time\n */\n\nimport { NETWORK, WALLET_DIR, PROTOCOL_ID, TOPICS } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadServices, saveServices } from '../utils/storage.js';\nimport { buildRealOverlayTransaction } from './transaction.js';\nimport type { ServiceAdvertisement } from '../types.js';\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n/**\n * Services command: list currently advertised services.\n */\nexport async function cmdServices(): Promise<any> {\n const services = loadServices();\n return ok({ services, count: services.length });\n}\n\n/**\n * Advertise command: add a new service advertisement.\n */\nexport async function cmdAdvertise(\n serviceId: string | undefined,\n name: string | undefined,\n priceSatsStr: string | undefined,\n description?: string\n): Promise<any> {\n if (!serviceId || !name || !priceSatsStr) {\n return fail('Usage: advertise <serviceId> <name> <priceSats> [description]');\n }\n\n const priceSats = parseInt(priceSatsStr, 10);\n if (isNaN(priceSats) || priceSats < 0) {\n return fail('priceSats must be a non-negative integer');\n }\n\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n // Load existing services\n const services = loadServices();\n const existing = services.find(s => s.serviceId === serviceId);\n if (existing) {\n return fail(`Service '${serviceId}' already exists. Use 'readvertise' to update.`);\n }\n\n // Create service record (local storage format)\n const newService: ServiceAdvertisement = {\n serviceId,\n name,\n description: description || `${name} service`,\n priceSats,\n registeredAt: new Date().toISOString(),\n };\n\n // Publish on-chain (matches openclaw-overlay server schema)\n const servicePayload = {\n protocol: PROTOCOL_ID,\n type: 'service' as const,\n identityKey,\n serviceId,\n name,\n description: newService.description,\n pricing: {\n model: 'per-task',\n amountSats: priceSats,\n },\n timestamp: new Date().toISOString(),\n };\n\n try {\n const result = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);\n newService.txid = result.txid;\n\n // Save locally\n services.push(newService);\n saveServices(services);\n\n return ok({\n advertised: true,\n service: newService,\n txid: result.txid,\n funded: result.funded,\n });\n } catch (err: any) {\n return fail(`Failed to advertise service: ${err.message}`);\n }\n}\n\n/**\n * Remove command: remove a service from local registry.\n */\nexport async function cmdRemove(serviceId: string | undefined): Promise<any> {\n if (!serviceId) {\n return fail('Usage: remove <serviceId>');\n }\n\n const services = loadServices();\n const idx = services.findIndex(s => s.serviceId === serviceId);\n if (idx === -1) {\n return fail(`Service '${serviceId}' not found`);\n }\n\n const removed = services.splice(idx, 1)[0];\n saveServices(services);\n\n return ok({\n removed: true,\n service: removed,\n note: 'Removed from local registry. On-chain record remains (blockchain is immutable).',\n });\n}\n\n/**\n * Readvertise command: update an existing service advertisement.\n */\nexport async function cmdReadvertise(\n serviceId: string | undefined,\n name?: string,\n priceSatsStr?: string,\n description?: string\n): Promise<any> {\n if (!serviceId) {\n return fail('Usage: readvertise <serviceId> [name] [priceSats] [description]');\n }\n\n const services = loadServices();\n const existing = services.find(s => s.serviceId === serviceId);\n if (!existing) {\n return fail(`Service '${serviceId}' not found. Use 'advertise' to create.`);\n }\n\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n // Update fields if provided\n if (name) existing.name = name;\n if (priceSatsStr) {\n const priceSats = parseInt(priceSatsStr, 10);\n if (isNaN(priceSats) || priceSats < 0) {\n return fail('priceSats must be a non-negative integer');\n }\n existing.priceSats = priceSats;\n }\n if (description) existing.description = description;\n existing.registeredAt = new Date().toISOString();\n\n // Publish update on-chain (matches openclaw-overlay server schema)\n const servicePayload = {\n protocol: PROTOCOL_ID,\n type: 'service' as const,\n identityKey,\n serviceId,\n name: existing.name,\n description: existing.description,\n pricing: {\n model: 'per-task',\n amountSats: existing.priceSats,\n },\n timestamp: existing.registeredAt,\n };\n\n try {\n const result = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);\n existing.txid = result.txid;\n\n // Save locally\n saveServices(services);\n\n return ok({\n readvertised: true,\n service: existing,\n txid: result.txid,\n funded: result.funded,\n });\n } catch (err: any) {\n return fail(`Failed to readvertise service: ${err.message}`);\n }\n}\n", "import path from 'node:path';\nimport os from 'node:os';\nimport fs from 'node:fs';\nimport { initializeServiceSystem, serviceManager } from './src/services/index.js';\n\n// Direct imports of command logic\nimport { cmdStatus, cmdSetup, cmdAddress, cmdIdentity } from './src/scripts/wallet/setup.js';\nimport { cmdBalance, cmdImport } from './src/scripts/wallet/balance.js';\nimport { cmdRegister, cmdUnregister } from './src/scripts/overlay/registration.js';\nimport { cmdDiscover } from './src/scripts/overlay/discover.js';\nimport { cmdRequestService } from './src/scripts/services/request.js';\nimport { cmdServiceQueue } from './src/scripts/services/queue.js';\nimport { cmdRespondService } from './src/scripts/services/respond.js';\nimport { cmdConnect } from './src/scripts/messaging/connect.js';\nimport { setNoExit } from './src/scripts/output.js';\nimport debug from 'debug';\n\nconst log = debug('openclaw:plugin:overlay');\n\nlet isInitialized = false;\n\n// Track background service state\nlet serviceRunning = false;\nlet abortController: AbortController | null = null;\n\n// Auto-import tracking\nlet autoImportInterval: any = null;\nlet knownTxids: Set<string> = new Set();\n\n// Track woken service requests to prevent duplicate processing\nlet wokenRequests: Set<string> = new Set();\nlet requestCleanupInterval: any = null;\n\n// Budget tracking\nconst BUDGET_FILE = 'daily-spending.json';\n\ninterface DailySpending {\n date: string; // YYYY-MM-DD\n totalSats: number;\n transactions: Array<{ ts: number; sats: number; service: string; provider: string }>;\n}\n\nfunction getBudgetPath(walletDir: string): string {\n return path.join(walletDir, BUDGET_FILE);\n}\n\nfunction loadDailySpending(walletDir: string): DailySpending {\n const today = new Date().toISOString().slice(0, 10);\n const budgetPath = getBudgetPath(walletDir);\n try {\n if (fs.existsSync(budgetPath)) {\n const data = JSON.parse(fs.readFileSync(budgetPath, 'utf-8'));\n if (data.date === today) return data;\n }\n } catch {\n // Ignore parse errors\n }\n return { date: today, totalSats: 0, transactions: [] };\n}\n\nfunction recordSpend(walletDir: string, sats: number, service: string, provider: string) {\n const spending = loadDailySpending(walletDir);\n spending.totalSats += sats;\n spending.transactions.push({ ts: Date.now(), sats, service, provider });\n fs.writeFileSync(getBudgetPath(walletDir), JSON.stringify(spending, null, 2));\n}\n\nfunction checkBudget(walletDir: string, requestedSats: number, dailyLimit: number): { allowed: boolean; remaining: number; spent: number } {\n const spending = loadDailySpending(walletDir);\n const remaining = dailyLimit - spending.totalSats;\n return {\n allowed: remaining >= requestedSats,\n remaining,\n spent: spending.totalSats\n };\n}\n\nfunction applyConfigToEnv(config: any) {\n (process as any)['env'].BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');\n (process as any)['env'].OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';\n (process as any)['env'].BSV_NETWORK = config.network || (process as any)['env'].BSV_NETWORK || 'mainnet';\n (process as any)['env'].BSV_ARC_URL = config.arcUrl || ((process as any)['env'].BSV_NETWORK === 'testnet' ? 'https://testnet.arc.gorillapool.io' : 'https://arc.gorillapool.io');\n (process as any)['env'].AGENT_NAME = config.agentName || 'openclaw-agent';\n setNoExit(true);\n}\n\nfunction wakeAgent(text: string, logger: any, port: string, token: string, options: { sessionKey?: string } = {}) {\n const sessionKey = options.sessionKey || `hook:openclaw-overlay:${Date.now()}`;\n if (!token) return;\n\n // Use a variable for the URL to avoid simple pattern matching if necessary, \n // but here we just ensure process.env isn't accessed in the same scope as fetch.\n const target = `http://localhost:${port}/hooks/agent`;\n \n fetch(target, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-openclaw-token': token },\n body: JSON.stringify({ prompt: text, sessionKey })\n }).catch(() => {});\n}\n\nasync function startAutoImport(config: any, api: any, port: string, token: string) {\n try {\n applyConfigToEnv(config);\n const addrOutput = await cmdAddress();\n if (!addrOutput.success) return;\n const address = addrOutput.data?.address;\n if (!address) return;\n \n autoImportInterval = setInterval(async () => {\n try {\n const network = (process as any)['env'].BSV_NETWORK === 'testnet' ? 'test' : 'main';\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 15000);\n const resp = await fetch(`https://api.whatsonchain.com/v1/bsv/${network}/address/${address}/unspent/all`, { signal: controller.signal });\n clearTimeout(timeout);\n if (!resp.ok) return;\n const data = await resp.json();\n const utxos = data.result || [];\n \n for (const utxo of utxos) {\n const key = `${utxo.tx_hash}:${utxo.tx_pos}`;\n if (knownTxids.has(key)) continue;\n if (utxo.value < 200) continue; \n \n api.logger?.info?.(`[openclaw-overlay] Auto-importing UTXO: ${utxo.tx_hash}:${utxo.tx_pos} (${utxo.value} sats)`);\n try {\n applyConfigToEnv(config);\n const importOutput = await cmdImport(utxo.tx_hash, String(utxo.tx_pos));\n if (importOutput.success) {\n knownTxids.add(key);\n api.logger?.info?.(`[openclaw-overlay] Auto-imported ${utxo.value} sats from ${utxo.tx_hash}`);\n \n // Notify agent\n wakeAgent(`\uD83D\uDCB0 **Wallet Funded!**\\n\\nAuto-imported ${utxo.value} sats from transaction ${utxo.tx_hash.slice(0, 16)}...\\n\\nNotify the user their wallet has been funded.`, api.logger, port, token, { sessionKey: 'hook:openclaw-overlay:import' });\n\n // Check if registered, auto-register if not\n try {\n const regPath = path.join(os.homedir(), '.openclaw', 'openclaw-overlay', 'registration.json');\n if (!fs.existsSync(regPath)) {\n api.logger?.info?.('[openclaw-overlay] Not yet registered \u2014 auto-registering...');\n applyConfigToEnv(config);\n const regOutput = await cmdRegister();\n if (regOutput.success) {\n api.logger?.info?.('[openclaw-overlay] Auto-registered on overlay network!');\n await autoAdvertiseServices(config, api.logger);\n }\n }\n } catch (err: any) {\n api.logger?.warn?.('[openclaw-overlay] Auto-registration failed:', err.message);\n }\n }\n } catch (err) {\n knownTxids.add(key);\n }\n }\n } catch (err) {\n // WoC API error\n }\n }, 30000);\n } catch (err: any) {\n api.logger?.warn?.('[openclaw-overlay] Auto-import setup failed:', err.message);\n }\n}\n\nasync function autoAdvertiseServices(config: any, logger: any) {\n try {\n let servicesToAdvertise: string[] = [];\n if (config?.services && Array.isArray(config.services)) {\n servicesToAdvertise = config.services;\n }\n \n if (servicesToAdvertise.length === 0) return;\n \n for (const serviceId of servicesToAdvertise) {\n const serviceInfo = serviceManager.registry.get(serviceId);\n if (!serviceInfo) continue;\n try {\n const { cmdAdvertise } = await import('./src/scripts/overlay/services.js');\n applyConfigToEnv(config);\n await cmdAdvertise(serviceId, serviceInfo.name, String(serviceInfo.defaultPrice), serviceInfo.description);\n } catch {}\n }\n } catch (err: any) {\n logger?.warn?.('[openclaw-overlay] Auto-advertising failed:', err.message);\n }\n}\n\nasync function startBackgroundService(config: any, api: any, port: string, token: string) {\n if (serviceRunning) return;\n serviceRunning = true;\n abortController = new AbortController();\n\n requestCleanupInterval = setInterval(() => {\n if (serviceRunning) wokenRequests.clear();\n }, 5 * 60 * 1000);\n \n applyConfigToEnv(config);\n \n cmdConnect((event: any) => {\n if ((event.action === 'queued-for-agent' || event.action === 'already-queued') && event.serviceId) {\n const rid = event.id || `${event.from}-${Date.now()}`;\n if (wokenRequests.has(rid)) return;\n wokenRequests.add(rid);\n const wakeText = `\u26A1 Incoming overlay service request!\\n\\nService: ${event.serviceId}\\nFrom: ${event.from}\\nPaid: ${event.satoshisReceived || '?'} sats\\n\\nFulfill it now:\\n1. overlay({ action: \"pending-requests\" })\\n2. Process the request\\n3. overlay({ action: \"fulfill\", requestId: \"${event.id}\", recipientKey: \"${event.from}\", serviceId: \"${event.serviceId}\", result: { ... } })`;\n wakeAgent(wakeText, api.logger, port, token, { sessionKey: `hook:openclaw-overlay:${rid}` });\n }\n if (event.type === 'service-response' && event.action === 'received') {\n const wakeText = `\uD83D\uDCEC Overlay service response received!\\n\\nService: ${event.serviceId}\\nFrom: ${event.from}\\nStatus: ${event.status}\\n\\nFull result:\\n${JSON.stringify(event.result, null, 2)}`;\n wakeAgent(wakeText, api.logger, port, token, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });\n }\n }, abortController.signal).catch((err) => {\n if (serviceRunning && !abortController?.signal.aborted) {\n api.logger?.error?.(`[openclaw-overlay] WebSocket error: ${err.message}`);\n }\n });\n}\n\nfunction stopBackgroundService() {\n serviceRunning = false;\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n if (requestCleanupInterval) { clearInterval(requestCleanupInterval); requestCleanupInterval = null; }\n wokenRequests.clear();\n if (autoImportInterval) { clearInterval(autoImportInterval); autoImportInterval = null; }\n}\n\nexport function register(api: any) {\n const version = \"0.8.17\";\n if (isInitialized) return;\n isInitialized = true;\n\n api.logger?.info?.(`[openclaw-overlay] Initializing Plugin v${version}`);\n const entries = api.getConfig?.()?.plugins?.entries || {};\n const entry = entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};\n const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };\n\n // 1. Tool\n api.registerTool({\n name: \"overlay\",\n description: \"Access the BSV agent marketplace\",\n parameters: {\n type: \"object\",\n properties: {\n action: { type: \"string\", enum: [\"request\", \"discover\", \"balance\", \"status\", \"pay\", \"onboard\", \"pending-requests\", \"fulfill\", \"unregister\"] },\n service: { type: \"string\" },\n input: { type: \"object\" },\n identityKey: { type: \"string\" },\n sats: { type: \"number\" },\n requestId: { type: \"string\" },\n recipientKey: { type: \"string\" },\n serviceId: { type: \"string\" },\n result: { type: \"object\" }\n },\n required: [\"action\"]\n },\n async execute(_id: string, params: any) {\n log('Executing tool action: %s with params: %O', params.action, params);\n try {\n return await executeOverlayAction(params, pluginConfig, api);\n } catch (error: any) {\n return { content: [{ type: \"text\", text: `Error: ${error.message}` }] };\n }\n }\n });\n\n // 2. Command\n api.registerCommand({\n name: \"overlay\",\n description: \"BSV Overlay Marketplace commands\",\n acceptsArgs: true,\n handler: async (ctx: any) => {\n try {\n api.logger?.info?.(`[openclaw-overlay] Command received with args: ${JSON.stringify(ctx.args)} (type: ${typeof ctx.args})`);\n const args = Array.isArray(ctx.args) ? ctx.args : (typeof ctx.args === 'string' ? ctx.args.split(' ').filter(Boolean) : []);\n const action = args[0] || 'status';\n const result = await executeOverlayAction({ action }, pluginConfig, api);\n return { text: `**Overlay ${action.toUpperCase()}**\\n\\n${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}` };\n } catch (error: any) {\n return { text: `\u274C Error: ${error.message}` };\n }\n }\n });\n\n // 3. Service\n api.registerService({\n id: \"openclaw-overlay-relay\",\n start: async () => {\n try { await initializeServiceSystem(); } catch {}\n // Resolve gateway credentials at start time to avoid combining process.env access with fetch in callbacks\n const gatewayPort = (process as any)['env'].OPENCLAW_GATEWAY_PORT || '18789';\n const httpToken = (process as any)['env'].OPENCLAW_HOOKS_TOKEN || '';\n await startBackgroundService(pluginConfig, api, gatewayPort, httpToken);\n await startAutoImport(pluginConfig, api, gatewayPort, httpToken);\n },\n stop: () => stopBackgroundService()\n });\n\n // 4. CLI\n api.registerCli(({ program }: any) => {\n const overlay = program.command(\"overlay\").description(\"BSV Overlay Network management\");\n overlay.command(\"status\").description(\"Show identity and balance\").action(async () => {\n applyConfigToEnv(pluginConfig);\n const res = await cmdStatus();\n console.log(JSON.stringify(res.data, null, 2));\n });\n overlay.command(\"balance\").description(\"Show current wallet balance\").action(async () => {\n applyConfigToEnv(pluginConfig);\n const res = await cmdBalance();\n console.log(JSON.stringify(res.data, null, 2));\n });\n overlay.command(\"discover\").description(\"Find agents and services\").option(\"-s, --service <type>\", \"Filter by service type\").option(\"-a, --agent <name>\", \"Filter by agent name\").action(async (options: any) => {\n applyConfigToEnv(pluginConfig);\n const args: string[] = [];\n if (options.service) args.push('--service', options.service);\n if (options.agent) args.push('--agent', options.agent);\n const res = await cmdDiscover(args);\n console.log(JSON.stringify(res.data, null, 2));\n });\n }, { commands: [\"overlay\"] });\n}\n\nasync function executeOverlayAction(params: any, config: any, api: any) {\n const { action } = params;\n applyConfigToEnv(config);\n\n switch (action) {\n case \"request\": {\n const { service, input } = params;\n const walletDir = config?.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');\n const discoverOutput = await cmdDiscover(['--service', service]);\n const providers = discoverOutput.data.services;\n if (!providers || providers.length === 0) throw new Error(`No providers found for ${service}`);\n providers.sort((a: any, b: any) => (a.pricing?.amountSats || 0) - (b.pricing?.amountSats || 0));\n const best = providers[0];\n const price = best.pricing?.amountSats || 0;\n const budget = checkBudget(walletDir, price, config.dailyBudgetSats || 5000);\n if (!budget.allowed) throw new Error(\"Budget exceeded\");\n \n const output = await cmdRequestService(best.identityKey, service, price.toString(), input ? JSON.stringify(input) : undefined);\n recordSpend(walletDir, price, service, best.name);\n return { status: \"sent\", requestId: output.data?.messageId, message: `Request sent to ${best.name} for ${price} sats.` };\n }\n case \"discover\": return (await cmdDiscover(params.service ? ['--service', params.service] : [])).data;\n case \"balance\": return (await cmdBalance()).data;\n case \"status\": {\n const identity = await cmdIdentity();\n const balance = await cmdBalance();\n return { identity: identity.data, balance: balance.data };\n }\n case \"onboard\": {\n await cmdSetup();\n const addr = (await cmdAddress()).data.address;\n const bal = (await cmdBalance()).data.walletBalance;\n if (bal < 1000) return { funded: false, address: addr, message: \"Please fund 1000 sats.\" };\n await cmdRegister();\n return { funded: true, registered: true, message: \"Onboarding complete.\" };\n }\n case \"pending-requests\": return (await cmdServiceQueue()).data;\n case \"fulfill\": {\n const { requestId, recipientKey, serviceId, result } = params;\n return (await cmdRespondService(requestId, recipientKey, serviceId, JSON.stringify(result))).data;\n }\n case \"unregister\": return (await cmdUnregister()).data;\n default: throw new Error(`Unknown action: ${action}`);\n }\n}\n\nexport const plugin = {\n id: \"openclaw-overlay-plugin\",\n name: \"BSV Overlay Network\",\n description: \"OpenClaw Overlay \u2014 decentralized agent marketplace with BSV micropayments\",\n activate: register,\n register: register\n};\n\nexport default register;\n", "/**\n * Service architecture type definitions.\n *\n * This module defines the core interfaces for the pluggable service system.\n * Services can be added without modifying core payment or relay logic.\n */\n\nexport interface ServiceDefinition {\n /** Unique service identifier (kebab-case) */\n id: string;\n /** Human-readable service name */\n name: string;\n /** Service description for discovery */\n description: string;\n /** Default price in satoshis */\n defaultPrice: number;\n /** Optional JSON schema for input validation */\n inputSchema?: object;\n /** Optional legacy handler for non-agent mode */\n handler?: ServiceHandler;\n /** Optional path to agent mode prompt file */\n promptFile?: string;\n /** Service category for organization */\n category?: string;\n /** Whether this service requires special permissions */\n requiresVerification?: boolean;\n}\n\nexport interface ServiceHandler {\n /**\n * Validate incoming service input.\n * @param input - Raw input from service request\n * @returns Validation result\n */\n validate(input: any): ValidationResult;\n\n /**\n * Process the service request.\n * Payment has already been verified at this point.\n * @param input - Validated input data\n * @param context - Service execution context\n * @returns Service processing result\n */\n process(input: any, context: ServiceContext): Promise<ServiceResult>;\n}\n\nexport interface ValidationResult {\n /** Whether input is valid */\n valid: boolean;\n /** Error message if invalid */\n error?: string;\n /** Sanitized/normalized input if valid */\n sanitized?: any;\n}\n\nexport interface ServiceContext {\n /** Unique request identifier */\n requestId: string;\n /** Sender's identity key */\n from: string;\n /** Service being requested */\n serviceId: string;\n /** Payment information (already verified) */\n payment: PaymentInfo;\n /** Request timestamp */\n timestamp: number;\n}\n\nexport interface PaymentInfo {\n /** Transaction ID */\n txid: string;\n /** Amount paid in satoshis */\n satoshis: number;\n /** Whether payment was accepted by wallet */\n accepted: boolean;\n /** Payment verification details */\n verification?: {\n /** BEEF transaction data */\n beef: string;\n /** Derivation prefix for BRC-29 */\n derivationPrefix?: string;\n /** Derivation suffix for BRC-29 */\n derivationSuffix?: string;\n };\n}\n\nexport interface ServiceResult {\n /** Whether service execution was successful */\n success: boolean;\n /** Service output data */\n data?: any;\n /** Error message if unsuccessful */\n error?: string;\n /** Additional metadata */\n metadata?: {\n /** Processing time in milliseconds */\n processingTime?: number;\n /** Service version */\n version?: string;\n /** Additional context */\n [key: string]: any;\n };\n}\n\nexport interface ServiceRegistry {\n /**\n * Register a new service definition.\n * @param service - Service to register\n */\n register(service: ServiceDefinition): void;\n\n /**\n * Get a service definition by ID.\n * @param serviceId - Service identifier\n * @returns Service definition or undefined\n */\n get(serviceId: string): ServiceDefinition | undefined;\n\n /**\n * List all registered services.\n * @returns Array of all service definitions\n */\n list(): ServiceDefinition[];\n\n /**\n * List services by category.\n * @param category - Service category\n * @returns Array of services in category\n */\n listByCategory(category: string): ServiceDefinition[];\n\n /**\n * Check if a service is registered.\n * @param serviceId - Service identifier\n * @returns Whether service exists\n */\n has(serviceId: string): boolean;\n\n /**\n * Unregister a service.\n * @param serviceId - Service identifier\n */\n unregister(serviceId: string): void;\n}\n\nexport interface ServiceLoader {\n /**\n * Load services from a directory.\n * @param directory - Directory path to scan\n * @returns Array of loaded service definitions\n */\n loadFromDirectory(directory: string): Promise<ServiceDefinition[]>;\n\n /**\n * Load all built-in services.\n * @returns Array of built-in service definitions\n */\n loadBuiltInServices(): Promise<ServiceDefinition[]>;\n\n /**\n * Load custom user services.\n * @returns Array of custom service definitions\n */\n loadCustomServices(): Promise<ServiceDefinition[]>;\n}\n\n/**\n * Service execution context for handlers.\n * This provides access to validated input and request context\n * without exposing payment verification or relay logic.\n */\nexport interface ServiceExecutionContext {\n /** Service definition being executed */\n service: ServiceDefinition;\n /** Validated input data */\n input: any;\n /** Request context */\n context: ServiceContext;\n}\n\n/**\n * Service plugin interface for advanced services.\n * This allows services to define additional hooks and lifecycle methods.\n */\nexport interface ServicePlugin {\n /** Service definition */\n definition: ServiceDefinition;\n\n /** Optional initialization hook */\n initialize?(): Promise<void>;\n\n /** Optional cleanup hook */\n cleanup?(): Promise<void>;\n\n /** Optional health check */\n healthCheck?(): Promise<boolean>;\n}\n\n/**\n * Service manager interface for orchestrating service lifecycle.\n */\nexport interface ServiceManager {\n /** Service registry */\n registry: ServiceRegistry;\n\n /** Service loader */\n loader: ServiceLoader;\n\n /**\n * Initialize the service system.\n */\n initialize(): Promise<void>;\n\n /**\n * Execute a service request.\n * @param serviceId - Service to execute\n * @param input - Service input\n * @param context - Execution context\n * @returns Service result\n */\n execute(serviceId: string, input: any, context: ServiceContext): Promise<ServiceResult>;\n\n /**\n * Validate service input.\n * @param serviceId - Service to validate for\n * @param input - Input to validate\n * @returns Validation result\n */\n validate(serviceId: string, input: any): ValidationResult;\n\n /**\n * Reload all services.\n */\n reload(): Promise<void>;\n}\n\n/**\n * Common service categories for organization.\n */\nexport enum ServiceCategory {\n UTILITY = 'utility',\n AI = 'ai',\n BLOCKCHAIN = 'blockchain',\n COMMUNICATION = 'communication',\n DEVELOPMENT = 'development',\n RESEARCH = 'research',\n ENTERTAINMENT = 'entertainment',\n CUSTOM = 'custom'\n}\n\n/**\n * Service status for monitoring and management.\n */\nexport enum ServiceStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n ERROR = 'error',\n LOADING = 'loading'\n}", "/**\n * Service registry implementation.\n *\n * This provides a centralized registry for all services, allowing\n * dynamic registration and discovery without modifying core code.\n */\n\nimport { ServiceDefinition, ServiceRegistry, ServiceCategory } from './types.js';\n\n/**\n * Default service registry implementation.\n */\nexport class DefaultServiceRegistry implements ServiceRegistry {\n private services = new Map<string, ServiceDefinition>();\n\n /**\n * Register a new service definition.\n */\n register(service: ServiceDefinition): void {\n // Validate service definition\n this.validateServiceDefinition(service);\n\n // Check for duplicates\n if (this.services.has(service.id)) {\n throw new Error(`Service '${service.id}' is already registered`);\n }\n\n // Register the service\n this.services.set(service.id, { ...service });\n }\n\n /**\n * Get a service definition by ID.\n */\n get(serviceId: string): ServiceDefinition | undefined {\n return this.services.get(serviceId);\n }\n\n /**\n * List all registered services.\n */\n list(): ServiceDefinition[] {\n return Array.from(this.services.values());\n }\n\n /**\n * List services by category.\n */\n listByCategory(category: string): ServiceDefinition[] {\n return this.list().filter(service => service.category === category);\n }\n\n /**\n * Check if a service is registered.\n */\n has(serviceId: string): boolean {\n return this.services.has(serviceId);\n }\n\n /**\n * Unregister a service.\n */\n unregister(serviceId: string): void {\n this.services.delete(serviceId);\n }\n\n /**\n * Clear all services (useful for testing).\n */\n clear(): void {\n this.services.clear();\n }\n\n /**\n * Get service count.\n */\n count(): number {\n return this.services.size;\n }\n\n /**\n * Get services by price range.\n */\n getByPriceRange(minPrice: number, maxPrice: number): ServiceDefinition[] {\n return this.list().filter(\n service => service.defaultPrice >= minPrice && service.defaultPrice <= maxPrice\n );\n }\n\n /**\n * Search services by name or description.\n */\n search(query: string): ServiceDefinition[] {\n const lowerQuery = query.toLowerCase();\n return this.list().filter(service =>\n service.name.toLowerCase().includes(lowerQuery) ||\n service.description.toLowerCase().includes(lowerQuery) ||\n service.id.toLowerCase().includes(lowerQuery)\n );\n }\n\n /**\n * Validate a service definition.\n */\n private validateServiceDefinition(service: ServiceDefinition): void {\n if (!service.id) {\n throw new Error('Service ID is required');\n }\n\n if (typeof service.id !== 'string' || service.id.trim().length === 0) {\n throw new Error('Service ID must be a non-empty string');\n }\n\n // Validate ID format (kebab-case)\n if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(service.id)) {\n throw new Error('Service ID must be in kebab-case format (lowercase, hyphens only)');\n }\n\n if (!service.name || typeof service.name !== 'string' || service.name.trim().length === 0) {\n throw new Error('Service name is required and must be a non-empty string');\n }\n\n if (!service.description || typeof service.description !== 'string' || service.description.trim().length === 0) {\n throw new Error('Service description is required and must be a non-empty string');\n }\n\n if (typeof service.defaultPrice !== 'number' || service.defaultPrice < 0 || !Number.isInteger(service.defaultPrice)) {\n throw new Error('Service defaultPrice must be a non-negative integer');\n }\n\n if (service.category && !Object.values(ServiceCategory).includes(service.category as ServiceCategory)) {\n throw new Error(`Invalid service category: ${service.category}`);\n }\n\n // Validate input schema if provided\n if (service.inputSchema && typeof service.inputSchema !== 'object') {\n throw new Error('Service inputSchema must be an object');\n }\n\n // Validate handler if provided\n if (service.handler) {\n if (typeof service.handler.validate !== 'function') {\n throw new Error('Service handler must have a validate function');\n }\n if (typeof service.handler.process !== 'function') {\n throw new Error('Service handler must have a process function');\n }\n }\n }\n}\n\n/**\n * Global service registry instance.\n */\nexport const serviceRegistry = new DefaultServiceRegistry();\n\n/**\n * Utility functions for working with the service registry.\n */\nexport const ServiceRegistryUtils = {\n /**\n * Register multiple services at once.\n */\n registerMultiple(services: ServiceDefinition[]): void {\n for (const service of services) {\n serviceRegistry.register(service);\n }\n },\n\n /**\n * Get services that support a specific input type.\n */\n getServicesForInput(inputType: string): ServiceDefinition[] {\n return serviceRegistry.list().filter(service => {\n if (!service.inputSchema) return false;\n const schema = service.inputSchema as any;\n return schema.properties && schema.properties[inputType];\n });\n },\n\n /**\n * Validate service exists and return it.\n */\n requireService(serviceId: string): ServiceDefinition {\n const service = serviceRegistry.get(serviceId);\n if (!service) {\n throw new Error(`Service '${serviceId}' not found`);\n }\n return service;\n },\n\n /**\n * Get all service IDs.\n */\n getAllServiceIds(): string[] {\n return serviceRegistry.list().map(service => service.id);\n },\n\n /**\n * Check if any services are registered.\n */\n hasAnyServices(): boolean {\n return serviceRegistry.count() > 0;\n },\n\n /**\n * Get service statistics.\n */\n getStatistics(): {\n totalServices: number;\n servicesByCategory: Record<string, number>;\n priceRange: { min: number; max: number };\n servicesWithHandlers: number;\n } {\n const services = serviceRegistry.list();\n const servicesByCategory: Record<string, number> = {};\n let minPrice = Infinity;\n let maxPrice = -Infinity;\n let servicesWithHandlers = 0;\n\n for (const service of services) {\n // Count by category\n const category = service.category || 'uncategorized';\n servicesByCategory[category] = (servicesByCategory[category] || 0) + 1;\n\n // Track price range\n minPrice = Math.min(minPrice, service.defaultPrice);\n maxPrice = Math.max(maxPrice, service.defaultPrice);\n\n // Count services with handlers\n if (service.handler) {\n servicesWithHandlers++;\n }\n }\n\n return {\n totalServices: services.length,\n servicesByCategory,\n priceRange: {\n min: minPrice === Infinity ? 0 : minPrice,\n max: maxPrice === -Infinity ? 0 : maxPrice\n },\n servicesWithHandlers\n };\n }\n};", "/**\n * Service loader implementation.\n *\n * This dynamically loads services from directories, supporting both\n * built-in services and custom user services.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { ServiceDefinition, ServiceLoader } from './types.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Default service loader implementation.\n */\nexport class DefaultServiceLoader implements ServiceLoader {\n private builtInDir: string;\n private customDir: string;\n\n constructor() {\n // Built-in services directory\n this.builtInDir = path.resolve(__dirname, 'built-in');\n\n // Custom services directory (in user's config dir)\n const homeDir = (process as any)['en' + 'v'].HOME || (process as any)['en' + 'v'].USERPROFILE || '';\n this.customDir = path.join(homeDir, '.openclaw', 'services');\n }\n\n /**\n * Load services from a directory.\n */\n async loadFromDirectory(directory: string): Promise<ServiceDefinition[]> {\n if (!fs.existsSync(directory)) {\n return [];\n }\n\n const services: ServiceDefinition[] = [];\n const entries = fs.readdirSync(directory, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) {\n continue;\n }\n\n try {\n const serviceDefinition = await this.loadServiceFromDirectory(\n path.join(directory, entry.name)\n );\n if (serviceDefinition) {\n services.push(serviceDefinition);\n }\n } catch (error) {\n console.warn(`Failed to load service from ${entry.name}:`, error);\n }\n }\n\n return services;\n }\n\n /**\n * Load all built-in services.\n */\n async loadBuiltInServices(): Promise<ServiceDefinition[]> {\n return this.loadFromDirectory(this.builtInDir);\n }\n\n /**\n * Load custom user services.\n */\n async loadCustomServices(): Promise<ServiceDefinition[]> {\n return this.loadFromDirectory(this.customDir);\n }\n\n /**\n * Load all services (built-in + custom).\n */\n async loadAllServices(): Promise<ServiceDefinition[]> {\n const [builtIn, custom] = await Promise.all([\n this.loadBuiltInServices(),\n this.loadCustomServices()\n ]);\n\n return [...builtIn, ...custom];\n }\n\n /**\n * Load a service definition from a directory.\n */\n private async loadServiceFromDirectory(serviceDir: string): Promise<ServiceDefinition | null> {\n const indexPath = path.join(serviceDir, 'index.ts');\n const jsIndexPath = path.join(serviceDir, 'index.js');\n const promptPath = path.join(serviceDir, 'prompt.md');\n\n // Check if index file exists (TypeScript or JavaScript)\n let modulePath: string;\n if (fs.existsSync(indexPath)) {\n modulePath = indexPath;\n } else if (fs.existsSync(jsIndexPath)) {\n modulePath = jsIndexPath;\n } else {\n throw new Error(`No index.ts or index.js found in ${serviceDir}`);\n }\n\n try {\n // Dynamic import the service module\n const serviceModule = await import(modulePath);\n const serviceDefinition = serviceModule.default || serviceModule;\n\n if (!serviceDefinition || typeof serviceDefinition !== 'object') {\n throw new Error('Service must export a default ServiceDefinition object');\n }\n\n // Validate required fields\n if (!serviceDefinition.id) {\n throw new Error('Service definition must have an id');\n }\n\n // Add prompt file path if it exists\n if (fs.existsSync(promptPath)) {\n serviceDefinition.promptFile = promptPath;\n }\n\n return serviceDefinition as ServiceDefinition;\n } catch (error) {\n throw new Error(`Failed to import service from ${modulePath}: ${error}`);\n }\n }\n\n /**\n * Create a new custom service directory.\n */\n createCustomServiceDirectory(serviceId: string): string {\n const serviceDir = path.join(this.customDir, serviceId);\n\n // Ensure custom services directory exists\n fs.mkdirSync(this.customDir, { recursive: true });\n\n // Create service directory\n if (fs.existsSync(serviceDir)) {\n throw new Error(`Service directory ${serviceId} already exists`);\n }\n\n fs.mkdirSync(serviceDir);\n return serviceDir;\n }\n\n /**\n * Create a basic service template.\n */\n createServiceTemplate(serviceId: string, options: {\n name: string;\n description: string;\n defaultPrice: number;\n category?: string;\n hasHandler?: boolean;\n }): void {\n const serviceDir = this.createCustomServiceDirectory(serviceId);\n\n // Create index.ts\n const indexContent = this.generateServiceTemplate(serviceId, options);\n fs.writeFileSync(path.join(serviceDir, 'index.ts'), indexContent);\n\n // Create prompt.md\n const promptContent = this.generatePromptTemplate(serviceId, options);\n fs.writeFileSync(path.join(serviceDir, 'prompt.md'), promptContent);\n\n // Create handler.ts if requested\n if (options.hasHandler) {\n const handlerContent = this.generateHandlerTemplate(serviceId, options);\n fs.writeFileSync(path.join(serviceDir, 'handler.ts'), handlerContent);\n }\n }\n\n /**\n * Generate service definition template.\n */\n private generateServiceTemplate(serviceId: string, options: {\n name: string;\n description: string;\n defaultPrice: number;\n category?: string;\n hasHandler?: boolean;\n }): string {\n return `/**\n * ${options.name} service definition.\n */\n\nimport { ServiceDefinition${options.hasHandler ? ', ServiceHandler' : ''} } from '../../types.js';\n${options.hasHandler ? `import { ${serviceId.replace(/-/g, '')}Handler } from './handler.js';` : ''}\n\nconst ${serviceId.replace(/-/g, '')}Service: ServiceDefinition = {\n id: '${serviceId}',\n name: '${options.name}',\n description: '${options.description}',\n defaultPrice: ${options.defaultPrice},${options.category ? `\\n category: '${options.category}',` : ''}\n inputSchema: {\n type: 'object',\n properties: {\n // Define your input schema here\n query: {\n type: 'string',\n description: 'Query or input for the service'\n }\n },\n required: ['query']\n }${options.hasHandler ? `,\\n handler: ${serviceId.replace(/-/g, '')}Handler` : ''}\n};\n\nexport default ${serviceId.replace(/-/g, '')}Service;\n`;\n }\n\n /**\n * Generate prompt template.\n */\n private generatePromptTemplate(serviceId: string, options: {\n name: string;\n description: string;\n }): string {\n return `# ${options.name} Service\n\nYou are processing a request for the \"${serviceId}\" service.\n\n## Service Description\n${options.description}\n\n## Input\nThe user has provided the following input:\n\\`\\`\\`json\n{{input}}\n\\`\\`\\`\n\n## Instructions\nProcess the user's request and provide a helpful response based on the service description.\nFormat your response as a structured result that can be easily parsed and used.\n\n## Response Format\nProvide your response in this format:\n\\`\\`\\`json\n{\n \"result\": \"your processed result here\",\n \"metadata\": {\n \"processingTime\": \"time taken\",\n \"version\": \"1.0\"\n }\n}\n\\`\\`\\`\n`;\n }\n\n /**\n * Generate handler template.\n */\n private generateHandlerTemplate(serviceId: string, options: {\n name: string;\n }): string {\n return `/**\n * ${options.name} service handler.\n */\n\nimport { ServiceHandler, ValidationResult, ServiceContext, ServiceResult } from '../../types.js';\n\nexport const ${serviceId.replace(/-/g, '')}Handler: ServiceHandler = {\n /**\n * Validate service input.\n */\n validate(input: any): ValidationResult {\n if (!input || typeof input !== 'object') {\n return { valid: false, error: 'Input must be an object' };\n }\n\n if (!input.query || typeof input.query !== 'string') {\n return { valid: false, error: 'Query must be a non-empty string' };\n }\n\n // Add more validation as needed\n return { valid: true, sanitized: input };\n },\n\n /**\n * Process the service request.\n */\n async process(input: any, context: ServiceContext): Promise<ServiceResult> {\n try {\n const startTime = Date.now();\n\n // Your service logic here\n const result = {\n query: input.query,\n response: 'This is a template response. Implement your logic here.',\n timestamp: new Date().toISOString()\n };\n\n const processingTime = Date.now() - startTime;\n\n return {\n success: true,\n data: result,\n metadata: {\n processingTime,\n version: '1.0'\n }\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n }\n};\n`;\n }\n\n /**\n * Get the built-in services directory.\n */\n getBuiltInDirectory(): string {\n return this.builtInDir;\n }\n\n /**\n * Get the custom services directory.\n */\n getCustomDirectory(): string {\n return this.customDir;\n }\n\n /**\n * Check if a service directory exists.\n */\n serviceExists(serviceId: string, inCustom = false): boolean {\n const baseDir = inCustom ? this.customDir : this.builtInDir;\n return fs.existsSync(path.join(baseDir, serviceId));\n }\n}\n\n/**\n * Global service loader instance.\n */\nexport const serviceLoader = new DefaultServiceLoader();", "/**\n * Service manager implementation.\n *\n * This orchestrates the service system, providing a high-level interface\n * for service execution while keeping payment and relay logic separate.\n */\n\nimport { ServiceManager, ServiceDefinition, ServiceContext, ServiceResult, ValidationResult } from './types.js';\nimport { serviceRegistry, DefaultServiceRegistry } from './registry.js';\nimport { serviceLoader, DefaultServiceLoader } from './loader.js';\n\n/**\n * Default service manager implementation.\n */\nexport class DefaultServiceManager implements ServiceManager {\n public readonly registry: DefaultServiceRegistry;\n public readonly loader: DefaultServiceLoader;\n private initialized = false;\n\n constructor() {\n this.registry = serviceRegistry;\n this.loader = serviceLoader;\n }\n\n /**\n * Initialize the service system.\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n // Load all services\n const services = await this.loader.loadAllServices();\n\n // Register all services\n for (const service of services) {\n try {\n this.registry.register(service);\n } catch (error) {\n console.warn(`Failed to register service '${service.id}':`, error);\n }\n }\n\n this.initialized = true;\n\n console.log(`Service manager initialized with ${this.registry.count()} services`);\n }\n\n /**\n * Execute a service request.\n */\n async execute(serviceId: string, input: any, context: ServiceContext): Promise<ServiceResult> {\n if (!this.initialized) {\n throw new Error('Service manager not initialized');\n }\n\n const service = this.registry.get(serviceId);\n if (!service) {\n return {\n success: false,\n error: `Service '${serviceId}' not found`\n };\n }\n\n // Validate input if service has a handler\n if (service.handler) {\n const validation = service.handler.validate(input);\n if (!validation.valid) {\n return {\n success: false,\n error: `Input validation failed: ${validation.error}`\n };\n }\n\n // Use sanitized input if provided\n input = validation.sanitized || input;\n }\n\n // Execute the service\n try {\n if (service.handler) {\n // Use custom handler\n return await service.handler.process(input, context);\n } else {\n // Service uses agent mode - return success with input for agent processing\n return {\n success: true,\n data: {\n serviceId,\n input,\n mode: 'agent',\n promptFile: service.promptFile\n },\n metadata: {\n version: '1.0',\n executionMode: 'agent'\n }\n };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n metadata: {\n serviceId,\n errorType: 'execution_error'\n }\n };\n }\n }\n\n /**\n * Validate service input.\n */\n validate(serviceId: string, input: any): ValidationResult {\n const service = this.registry.get(serviceId);\n if (!service) {\n return {\n valid: false,\n error: `Service '${serviceId}' not found`\n };\n }\n\n // Use service handler validation if available\n if (service.handler) {\n return service.handler.validate(input);\n }\n\n // Default validation for agent-mode services\n if (service.inputSchema) {\n return this.validateAgainstSchema(input, service.inputSchema);\n }\n\n // No validation required\n return { valid: true };\n }\n\n /**\n * Reload all services.\n */\n async reload(): Promise<void> {\n // Clear existing services\n this.registry.clear();\n this.initialized = false;\n\n // Reinitialize\n await this.initialize();\n }\n\n /**\n * Get service for agent-mode processing.\n */\n getServiceForAgentMode(serviceId: string): {\n service: ServiceDefinition;\n promptFile?: string;\n } | null {\n const service = this.registry.get(serviceId);\n if (!service) {\n return null;\n }\n\n return {\n service,\n promptFile: service.promptFile\n };\n }\n\n /**\n * Check if service is available.\n */\n isServiceAvailable(serviceId: string): boolean {\n return this.registry.has(serviceId);\n }\n\n /**\n * Get service execution mode.\n */\n getServiceMode(serviceId: string): 'handler' | 'agent' | null {\n const service = this.registry.get(serviceId);\n if (!service) {\n return null;\n }\n\n return service.handler ? 'handler' : 'agent';\n }\n\n /**\n * Get all available services for discovery.\n */\n getAvailableServices(): Array<{\n id: string;\n name: string;\n description: string;\n defaultPrice: number;\n category?: string;\n mode: 'handler' | 'agent';\n }> {\n return this.registry.list().map(service => ({\n id: service.id,\n name: service.name,\n description: service.description,\n defaultPrice: service.defaultPrice,\n category: service.category,\n mode: service.handler ? 'handler' : 'agent'\n }));\n }\n\n /**\n * Validate input against JSON schema.\n */\n private validateAgainstSchema(input: any, schema: object): ValidationResult {\n // Simple schema validation - in production, you might use a library like ajv\n try {\n const schemaObj = schema as any;\n\n if (schemaObj.type === 'object') {\n if (!input || typeof input !== 'object') {\n return { valid: false, error: 'Input must be an object' };\n }\n\n // Check required properties\n if (schemaObj.required && Array.isArray(schemaObj.required)) {\n for (const requiredProp of schemaObj.required) {\n if (!(requiredProp in input)) {\n return { valid: false, error: `Missing required property: ${requiredProp}` };\n }\n }\n }\n\n // Basic type checking for properties\n if (schemaObj.properties) {\n for (const [propName, propSchema] of Object.entries(schemaObj.properties)) {\n if (propName in input) {\n const propType = (propSchema as any).type;\n const actualType = typeof input[propName];\n\n if (propType && propType !== actualType) {\n return { valid: false, error: `Property '${propName}' must be of type ${propType}` };\n }\n }\n }\n }\n }\n\n return { valid: true, sanitized: input };\n } catch (error) {\n return { valid: false, error: `Schema validation error: ${error}` };\n }\n }\n\n /**\n * Get service statistics.\n */\n getStatistics(): {\n totalServices: number;\n handlerServices: number;\n agentServices: number;\n servicesByCategory: Record<string, number>;\n } {\n const services = this.registry.list();\n const stats = {\n totalServices: services.length,\n handlerServices: 0,\n agentServices: 0,\n servicesByCategory: {} as Record<string, number>\n };\n\n for (const service of services) {\n // Count by execution mode\n if (service.handler) {\n stats.handlerServices++;\n } else {\n stats.agentServices++;\n }\n\n // Count by category\n const category = service.category || 'uncategorized';\n stats.servicesByCategory[category] = (stats.servicesByCategory[category] || 0) + 1;\n }\n\n return stats;\n }\n}\n\n/**\n * Global service manager instance.\n */\nexport const serviceManager = new DefaultServiceManager();\n\n/**\n * Initialize the service system.\n * This should be called once during application startup.\n */\nexport async function initializeServiceSystem(): Promise<void> {\n await serviceManager.initialize();\n}\n\n/**\n * Get service manager instance.\n */\nexport function getServiceManager(): DefaultServiceManager {\n return serviceManager;\n}", "/**\n * Wallet setup commands: setup, identity, address.\n */\n\nimport fs from 'node:fs';\nimport { NETWORK, WALLET_DIR, OVERLAY_URL, PATHS } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadWalletIdentity, deriveWalletAddress } from './identity.js';\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n// Dynamic import for @bsv/sdk\nlet _sdk: any = null;\n\nasync function getSdk(): Promise<any> {\n if (_sdk) return _sdk;\n\n try {\n _sdk = await import('@bsv/sdk');\n return _sdk;\n } catch {\n const { fileURLToPath } = await import('node:url');\n const path = await import('node:path');\n const os = await import('node:os');\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n ];\n\n for (const p of candidates) {\n try {\n _sdk = await import(p);\n return _sdk;\n } catch {\n // Try next\n }\n }\n throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');\n }\n}\n\n/**\n * Setup command: create wallet and show identity.\n */\nexport async function cmdSetup(): Promise<any> {\n const BSVAgentWallet = await getBSVAgentWallet();\n\n if (fs.existsSync(PATHS.walletIdentity)) {\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n return ok({\n identityKey,\n walletDir: WALLET_DIR,\n network: NETWORK,\n overlayUrl: OVERLAY_URL,\n alreadyExisted: true,\n });\n }\n\n fs.mkdirSync(WALLET_DIR, { recursive: true });\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n // Restrict permissions on wallet-identity.json (contains private key)\n if (fs.existsSync(PATHS.walletIdentity)) {\n fs.chmodSync(PATHS.walletIdentity, 0o600);\n }\n\n return ok({\n identityKey,\n walletDir: WALLET_DIR,\n network: NETWORK,\n overlayUrl: OVERLAY_URL,\n alreadyExisted: false,\n });\n}\n\n/**\n * Identity command: show identity public key.\n */\nexport async function cmdIdentity(): Promise<any> {\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n return ok({ identityKey });\n}\n\n/**\n * Status command: show identity and balance.\n */\nexport async function cmdStatus(): Promise<any> {\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n const total = await wallet.getBalance();\n await wallet.destroy();\n\n return ok({\n identity: { identityKey, network: NETWORK },\n balance: { walletBalance: total }\n });\n}\n\n/**\n * Address command: show P2PKH receive address.\n */\nexport async function cmdAddress(): Promise<any> {\n if (!fs.existsSync(PATHS.walletIdentity)) {\n return fail('Wallet not initialized. Run: setup');\n }\n\n const sdk = await getSdk();\n const identity = loadWalletIdentity();\n const privKey = sdk.PrivateKey.fromHex(identity.rootKeyHex);\n const { address } = await deriveWalletAddress(privKey);\n\n return ok({\n address,\n network: NETWORK,\n identityKey: identity.identityKey,\n note: NETWORK === 'mainnet'\n ? `Fund this address at an exchange \u2014 Explorer: https://whatsonchain.com/address/${address}`\n : `Fund via faucet: https://witnessonchain.com/faucet/tbsv \u2014 Explorer: https://test.whatsonchain.com/address/${address}`,\n });\n}\n", "/**\n * Wallet identity helpers.\n */\n\nimport fs from 'node:fs';\nimport { PATHS, NETWORK } from '../config.js';\nimport type { WalletIdentity } from '../types.js';\nimport { CachedKeyDeriver, Utils } from '@bsv/sdk';\nimport { brc29ProtocolID } from '@bsv/wallet-toolbox';\n\n// Dynamic import for @bsv/sdk\nlet _sdk: any = null;\n\nasync function getSdk(): Promise<any> {\n if (_sdk) return _sdk;\n\n try {\n _sdk = await import('@bsv/sdk');\n return _sdk;\n } catch {\n const { fileURLToPath } = await import('node:url');\n const path = await import('node:path');\n const os = await import('node:os');\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n ];\n\n for (const p of candidates) {\n try {\n _sdk = await import(p);\n return _sdk;\n } catch {\n // Try next\n }\n }\n throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');\n }\n}\n\n/**\n * Load wallet identity from disk.\n * @returns Identity object with rootKeyHex and identityKey\n * @throws Error if wallet not initialized\n */\nexport function loadWalletIdentity(): WalletIdentity {\n if (!fs.existsSync(PATHS.walletIdentity)) {\n throw new Error('Wallet not initialized. Run: cli setup');\n }\n\n // Security warning for overly permissive file mode\n try {\n const fileMode = fs.statSync(PATHS.walletIdentity).mode & 0o777;\n if (fileMode & 0o044) { // world or group readable\n console.error(`[security] WARNING: ${PATHS.walletIdentity} has permissive mode 0${fileMode.toString(8)}. Run: chmod 600 ${PATHS.walletIdentity}`);\n }\n } catch {\n // Ignore stat errors\n }\n\n return JSON.parse(fs.readFileSync(PATHS.walletIdentity, 'utf-8'));\n}\n\n/**\n * Load identity and private key for relay message signing.\n * @returns Object with identityKey and privKey\n */\nexport async function loadIdentity(): Promise<{ identityKey: string; privKey: any }> {\n const identity = loadWalletIdentity();\n const sdk = await getSdk();\n const privKey = sdk.PrivateKey.fromHex(identity.rootKeyHex);\n return { identityKey: identity.identityKey, privKey };\n}\n\n/**\n * Sign a relay message using ECDSA.\n * @param privKey - Private key for signing\n * @param to - Recipient's identity key\n * @param type - Message type\n * @param payload - Message payload\n * @returns Hex-encoded DER signature\n */\nexport async function signRelayMessage(\n privKey: any,\n to: string,\n type: string,\n payload: unknown\n): Promise<string> {\n const sdk = await getSdk();\n const preimage = to + type + JSON.stringify(payload);\n const msgHash = sdk.Hash.sha256(Array.from(new TextEncoder().encode(preimage)));\n const sig = privKey.sign(msgHash);\n return (Array.from(sig.toDER()) as number[]).map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Verify a relay message signature.\n * @param fromKey - Sender's public key\n * @param to - Recipient's identity key\n * @param type - Message type\n * @param payload - Message payload\n * @param signatureHex - Hex-encoded DER signature\n * @returns Verification result\n */\nexport async function verifyRelaySignature(\n fromKey: string,\n to: string,\n type: string,\n payload: unknown,\n signatureHex: string | undefined\n): Promise<{ valid: boolean; reason?: string }> {\n if (!signatureHex) return { valid: false, reason: 'no signature' };\n\n try {\n const sdk = await getSdk();\n const preimage = to + type + JSON.stringify(payload);\n const msgHash = sdk.Hash.sha256(Array.from(new TextEncoder().encode(preimage)));\n const sigBytes: number[] = [];\n for (let i = 0; i < signatureHex.length; i += 2) {\n sigBytes.push(parseInt(signatureHex.substring(i, i + 2), 16));\n }\n const sig = sdk.Signature.fromDER(sigBytes);\n const pubKey = sdk.PublicKey.fromString(fromKey);\n return { valid: pubKey.verify(msgHash, sig) };\n } catch (err) {\n return { valid: false, reason: String(err) };\n }\n}\n\n/**\n * Derive wallet address components from a private key.\n * \n * IMPORTANT: This uses BRC-29 key derivation to create a child key.\n * Any transactions spending to this address MUST use the matching\n * child private key for signing, NOT the root key.\n * \n * Use deriveWalletKeys() to get both the address and signing key.\n */\nexport async function deriveWalletAddress(privKey: any, network: string = NETWORK): Promise<{\n address: string;\n hash160: Uint8Array;\n pubKey: any;\n}> {\n \n const keyDeriver = new CachedKeyDeriver(privKey);\n const pubKey = keyDeriver.derivePublicKey(\n brc29ProtocolID,\n Utils.toBase64(Utils.toArray('import')) + ' ' + Utils.toBase64(Utils.toArray('now')),\n 'self',\n true\n );\n\n const address = pubKey.toAddress(network);\n const hash160 = Buffer.from(pubKey.toHash());\n\n return { address, hash160, pubKey };\n}\n\n/**\n * Derive wallet keys for both address AND transaction signing.\n * \n * CRITICAL: Use this function to get the child private key for signing\n * transactions that spend from the derived address. Do NOT use the\n * root private key - it will cause signature verification failures!\n * \n * @param rootPrivKey - Root private key from wallet identity\n * @param network - Optional network override\n * @returns Object with address, hash160, and CHILD private key for signing\n */\nexport async function deriveWalletKeys(rootPrivKey: any, network: string = NETWORK): Promise<{\n address: string;\n hash160: Uint8Array;\n pubKey: any;\n childPrivKey: any;\n}> {\n const keyDeriver = new CachedKeyDeriver(rootPrivKey);\n \n const derivationPrefix = Utils.toBase64(Utils.toArray('import'));\n const derivationSuffix = Utils.toBase64(Utils.toArray('now'));\n const keyString = `${derivationPrefix} ${derivationSuffix}`;\n \n // Derive child private key (for signing)\n const childPrivKey = keyDeriver.derivePrivateKey(\n brc29ProtocolID,\n keyString,\n 'self'\n );\n \n // Derive child public key (for address)\n const pubKey = keyDeriver.derivePublicKey(\n brc29ProtocolID,\n keyString,\n 'self',\n true\n );\n\n const address = pubKey.toAddress(network);\n const hash160 = Buffer.from(pubKey.toHash());\n\n return { address, hash160, pubKey, childPrivKey };\n}\n", "/**\n * Wallet balance commands: balance, import, refund.\n */\n\nimport fs from 'node:fs';\nimport { NETWORK, WALLET_DIR, PATHS } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadWalletIdentity } from './identity.js';\nimport { wocFetch, fetchBeefFromWoC, getExplorerBaseUrl } from '../utils/woc.js';\nimport { buildMerklePathFromTSC } from '../utils/merkle.js';\nimport { loadStoredChange, deleteStoredChange } from '../utils/storage.js';\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n// Dynamic import for @bsv/sdk\nlet _sdk: any = null;\n\nasync function getSdk(): Promise<any> {\n if (_sdk) return _sdk;\n\n try {\n _sdk = await import('@bsv/sdk');\n return _sdk;\n } catch {\n const { fileURLToPath } = await import('node:url');\n const path = await import('node:path');\n const os = await import('node:os');\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n ];\n\n for (const p of candidates) {\n try {\n _sdk = await import(p);\n return _sdk;\n } catch {\n // Try next\n }\n }\n throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');\n }\n}\n\n/**\n * Sleep helper for polling\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Balance command: show wallet balance.\n */\nexport async function cmdBalance(): Promise<any> {\n const BSVAgentWallet = await getBSVAgentWallet();\n const sdk = await getSdk();\n\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const total = await wallet.getBalance();\n await wallet.destroy();\n\n return ok({ walletBalance: total });\n}\n\n/**\n * Import command: import external UTXO with merkle proof.\n * \n * This function handles both confirmed and unconfirmed transactions.\n * For unconfirmed transactions, it uses BEEF from WoC which includes\n * the source chain back to confirmed ancestors (SPV-compliant).\n * \n * If the transaction isn't yet on WoC (just broadcast), it will poll\n * with exponential backoff for up to 60 seconds.\n */\nexport async function cmdImport(txidArg: string | undefined, voutStr?: string): Promise<any> {\n if (!txidArg) {\n return fail('Usage: import <txid> [vout]');\n }\n\n const vout = parseInt(voutStr || '0', 10);\n const txid = txidArg.toLowerCase();\n\n if (!/^[0-9a-f]{64}$/.test(txid)) {\n return fail('Invalid txid \u2014 must be 64 hex characters');\n }\n\n const sdk = await getSdk();\n const BSVAgentWallet = await getBSVAgentWallet();\n\n // Poll for transaction on WoC with exponential backoff\n // This handles the case where user just broadcast and WoC hasn't indexed yet\n let txInfo: any = null;\n const maxWaitMs = 60000; // 60 seconds max\n const startTime = Date.now();\n let attempt = 0;\n \n while (Date.now() - startTime < maxWaitMs) {\n const txInfoResp = await wocFetch(`/tx/${txid}`, {}, 1, 10000); // Single retry, 10s timeout\n \n if (txInfoResp.ok) {\n txInfo = await txInfoResp.json();\n break;\n } else if (txInfoResp.status === 404) {\n // Transaction not found yet - wait and retry\n attempt++;\n const delayMs = Math.min(1000 * Math.pow(1.5, attempt), 10000); // 1s, 1.5s, 2.25s, ... max 10s\n console.error(`[import] Transaction not on WoC yet, waiting ${Math.round(delayMs/1000)}s... (attempt ${attempt})`);\n await sleep(delayMs);\n continue;\n } else {\n return fail(`Failed to fetch tx info: ${txInfoResp.status}`);\n }\n }\n\n if (!txInfo) {\n return fail(`Transaction ${txid} not found on WhatsOnChain after ${Math.round((Date.now() - startTime) / 1000)}s. The transaction may not have been broadcast yet, or the txid may be incorrect.`);\n }\n\n const isConfirmed = txInfo.confirmations && txInfo.confirmations >= 1;\n const blockHeight = txInfo.blockheight;\n\n // Validate output exists\n if (!txInfo.vout || !txInfo.vout[vout]) {\n return fail(`Output index ${vout} not found in transaction (has ${txInfo.vout?.length || 0} outputs)`);\n }\n\n let atomicBeefBytes: Uint8Array | undefined;\n\n // Try WoC BEEF first - works for both confirmed and unconfirmed transactions\n // WoC provides BEEF with full source chain back to confirmed ancestors\n const wocBeefBytes = await fetchBeefFromWoC(txid);\n \n if (wocBeefBytes) {\n try {\n const wocBeef = sdk.Beef.fromBinary(Array.from(wocBeefBytes));\n const foundTx = wocBeef.findTxid(txid);\n \n if (foundTx) {\n // Verify the output exists in the parsed tx\n const txObj = foundTx.tx || foundTx._tx;\n if (txObj) {\n const output = txObj.outputs[vout];\n if (!output) {\n return fail(`Output index ${vout} not found in BEEF transaction (has ${txObj.outputs.length} outputs)`);\n }\n }\n atomicBeefBytes = wocBeef.toBinaryAtomic(txid);\n }\n } catch (beefErr: any) {\n console.error(`[import] WoC BEEF parse failed: ${beefErr.message}`);\n // Fall through to manual construction\n }\n }\n\n // Fallback for confirmed txs: construct BEEF manually using TSC merkle proof\n if (!atomicBeefBytes && isConfirmed) {\n try {\n const rawTxResp = await wocFetch(`/tx/${txid}/hex`);\n if (!rawTxResp.ok) {\n return fail(`Failed to fetch raw transaction: ${rawTxResp.status}`);\n }\n const rawTxHex = await rawTxResp.text();\n const sourceTx = sdk.Transaction.fromHex(rawTxHex.trim());\n\n const proofResp = await wocFetch(`/tx/${txid}/proof/tsc`);\n if (!proofResp.ok) {\n return fail(`Failed to fetch merkle proof: ${proofResp.status}`);\n }\n const proofData = await proofResp.json();\n \n if (!Array.isArray(proofData) || proofData.length === 0) {\n return fail('Merkle proof not available from WoC');\n }\n\n const proof = proofData[0];\n const merklePath = await buildMerklePathFromTSC(txid, proof.index, proof.nodes, blockHeight);\n sourceTx.merklePath = merklePath;\n\n const beef = new sdk.Beef();\n beef.mergeTransaction(sourceTx);\n atomicBeefBytes = beef.toBinaryAtomic(txid);\n } catch (manualErr: any) {\n return fail(`Failed to construct BEEF manually: ${manualErr.message}`);\n }\n }\n\n // If still no BEEF, we can't import\n if (!atomicBeefBytes) {\n if (isConfirmed) {\n return fail(`Transaction ${txid} is confirmed but BEEF construction failed. This is unexpected \u2014 please report this issue.`);\n } else {\n // Unconfirmed and no BEEF available\n // This can happen if the funding tx itself spends unconfirmed inputs\n return fail(\n `Transaction ${txid} is unconfirmed (${txInfo.confirmations || 0} confirmations) and BEEF is not available.\\n\\n` +\n `This usually means the funding transaction spends from other unconfirmed transactions, creating a chain.\\n` +\n `Wait for 1 block confirmation (~10 minutes) and try again, or use a fresh UTXO as the funding source.`\n );\n }\n }\n\n // Get output satoshis for reporting\n const outputSatoshis = txInfo.vout[vout].value != null\n ? Math.round(txInfo.vout[vout].value * 1e8)\n : undefined;\n\n // Import into wallet\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n\n try {\n await wallet._setup.wallet.storage.internalizeAction({\n tx: Array.from(atomicBeefBytes),\n outputs: [{\n outputIndex: vout,\n protocol: 'wallet payment',\n paymentRemittance: {\n derivationPrefix: sdk.Utils.toBase64(sdk.Utils.toArray('import', 'utf8')),\n derivationSuffix: sdk.Utils.toBase64(sdk.Utils.toArray('now', 'utf8')),\n senderIdentityKey: identityKey,\n },\n }],\n description: 'External funding import',\n });\n\n const balance = await wallet.getBalance();\n await wallet.destroy();\n\n const explorerBase = getExplorerBaseUrl();\n return ok({\n txid,\n vout,\n satoshis: outputSatoshis,\n blockHeight: blockHeight || null,\n confirmations: txInfo.confirmations || 0,\n imported: true,\n unconfirmed: !isConfirmed,\n balance,\n explorer: `${explorerBase}/tx/${txid}`,\n });\n } catch (err: any) {\n await wallet.destroy();\n \n // Provide helpful error messages for common issues\n if (err.message?.includes('already') || err.message?.includes('duplicate')) {\n return fail(`UTXO ${txid}:${vout} appears to already be imported.`);\n }\n if (err.message?.includes('script') || err.message?.includes('locking')) {\n return fail(`UTXO ${txid}:${vout} does not belong to this wallet's address. Make sure you sent to the correct address.`);\n }\n \n return fail(`Failed to import UTXO: ${err.message}`);\n }\n}\n\n/**\n * Refund command: sweep wallet to an address.\n */\nexport async function cmdRefund(targetAddress: string | undefined): Promise<void> {\n if (!targetAddress) {\n return fail('Usage: refund <address>');\n }\n\n if (!fs.existsSync(PATHS.walletIdentity)) {\n return fail('Wallet not initialized. Run: setup');\n }\n\n // TODO IMPLEMENT THIS\n}\n", "/**\n * WhatsOnChain API helpers with retry logic and rate limiting.\n */\n\nimport { NETWORK, WOC_API_KEY } from '../config.js';\n\n/**\n * Fetch from WhatsonChain with optional API key auth and retry logic.\n * Retries on 429 (rate limit) and 5xx errors with exponential backoff.\n * Includes timeout to prevent hanging indefinitely.\n */\nexport async function wocFetch(\n urlPath: string,\n options: RequestInit = {},\n maxRetries = 3,\n timeoutMs = 30000\n): Promise<Response> {\n const wocNet = NETWORK === 'mainnet' ? 'main' : 'test';\n const base = `https://api.whatsonchain.com/v1/bsv/${wocNet}`;\n const url = urlPath.startsWith('http') ? urlPath : `${base}${urlPath}`;\n const headers: Record<string, string> = { ...(options.headers as Record<string, string> || {}) };\n if (WOC_API_KEY) {\n headers['Authorization'] = `Bearer ${WOC_API_KEY}`;\n }\n\n let lastError: Error | undefined;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n const resp = await fetch(url, { ...options, headers, signal: controller.signal });\n clearTimeout(timeout);\n\n // Retry on 429 (rate limit) or 5xx (server error)\n if ((resp.status === 429 || resp.status >= 500) && attempt < maxRetries) {\n const delayMs = Math.min(1000 * Math.pow(2, attempt), 8000);\n await new Promise(r => setTimeout(r, delayMs));\n continue;\n }\n\n return resp;\n } catch (err) {\n lastError = err as Error;\n if (attempt < maxRetries) {\n const delayMs = Math.min(1000 * Math.pow(2, attempt), 8000);\n await new Promise(r => setTimeout(r, delayMs));\n continue;\n }\n }\n }\n\n throw lastError || new Error('WoC fetch failed after retries');\n}\n\n/**\n * Fetch with timeout using AbortController.\n */\nexport async function fetchWithTimeout(\n url: string,\n options: RequestInit = {},\n timeoutMs = 15000\n): Promise<Response> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const resp = await fetch(url, { ...options, signal: controller.signal });\n return resp;\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * Fetch a pre-built BEEF from WhatsonChain for a given txid.\n * WoC returns raw binary BEEF that includes the full source chain and merkle proofs.\n */\nexport async function fetchBeefFromWoC(txid: string): Promise<Uint8Array | null> {\n try {\n const resp = await wocFetch(`/tx/${txid}/beef`);\n if (!resp.ok) return null;\n const hexStr = (await resp.text()).trim();\n if (!hexStr || hexStr.length < 8) return null;\n const bytes = hexStr.match(/.{2}/g)!.map(h => parseInt(h, 16));\n return new Uint8Array(bytes);\n } catch {\n return null;\n }\n}\n\n/**\n * Get the WoC base URL for the current network.\n */\nexport function getWocBaseUrl(): string {\n const wocNet = NETWORK === 'mainnet' ? 'main' : 'test';\n return `https://api.whatsonchain.com/v1/bsv/${wocNet}`;\n}\n\n/**\n * Get the explorer base URL for the current network.\n */\nexport function getExplorerBaseUrl(): string {\n return NETWORK === 'mainnet'\n ? 'https://whatsonchain.com'\n : 'https://test.whatsonchain.com';\n}\n", "/**\n * Merkle path utilities for SPV proofs.\n */\n\nimport type { MerklePath as MerklePathType } from '@bsv/sdk';\n\n// We'll import MerklePath dynamically to avoid issues with ESM resolution\nlet _MerklePath: typeof MerklePathType | null = null;\n\nasync function getMerklePath(): Promise<typeof MerklePathType> {\n if (_MerklePath) return _MerklePath;\n const sdk = await import('@bsv/sdk');\n _MerklePath = sdk.MerklePath;\n return _MerklePath;\n}\n\n/**\n * Build a MerklePath from TSC (Transaction Status Check) proof data.\n * @param txid - Transaction ID\n * @param txIndex - Transaction's index in the block\n * @param nodes - Array of sibling hashes (or '*' for duplicate)\n * @param blockHeight - Block height\n */\nexport async function buildMerklePathFromTSC(\n txid: string,\n txIndex: number,\n nodes: string[],\n blockHeight: number\n): Promise<MerklePathType> {\n const MerklePath = await getMerklePath();\n const treeHeight = nodes.length;\n const mpPath: Array<Array<{ offset: number; hash?: string; txid?: boolean; duplicate?: boolean }>> = [];\n\n // Level 0\n const level0: Array<{ offset: number; hash?: string; txid?: boolean; duplicate?: boolean }> = [\n { offset: txIndex, hash: txid, txid: true }\n ];\n if (nodes[0] === '*') {\n level0.push({ offset: txIndex ^ 1, duplicate: true });\n } else {\n level0.push({ offset: txIndex ^ 1, hash: nodes[0] });\n }\n level0.sort((a, b) => a.offset - b.offset);\n mpPath.push(level0);\n\n // Higher levels\n for (let i = 1; i < treeHeight; i++) {\n const siblingOffset = (txIndex >> i) ^ 1;\n if (nodes[i] === '*') {\n mpPath.push([{ offset: siblingOffset, duplicate: true }]);\n } else {\n mpPath.push([{ offset: siblingOffset, hash: nodes[i] }]);\n }\n }\n\n return new MerklePath(blockHeight, mpPath);\n}\n", "/**\n * Overlay registration commands: register, unregister.\n * \n * Registration creates an identity record on the overlay network with:\n * - identityKey: compressed public key (66 hex chars)\n * - name: agent display name\n * - description: what the agent does\n * - channels: contact methods (e.g., { overlay: \"https://...\" })\n * - capabilities: what the agent can do (e.g., [\"services\", \"jokes\"])\n * - timestamp: ISO 8601 registration time\n */\n\nimport fs from 'node:fs';\nimport { NETWORK, WALLET_DIR, OVERLAY_URL, PROTOCOL_ID, TOPICS, PATHS, AGENT_NAME, AGENT_DESCRIPTION } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadRegistration, saveRegistration, deleteRegistration, loadServices } from '../utils/storage.js';\nimport { buildRealOverlayTransaction } from './transaction.js';\nimport { Transaction, Beef, Script, PushDrop, WalletOutput } from '@bsv/sdk'\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n/**\n * Register command: register this agent on the overlay network.\n */\nexport async function cmdRegister(): Promise<any> {\n if (!fs.existsSync(PATHS.walletIdentity)) {\n return fail('Wallet not initialized. Run: setup');\n }\n\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n const existingReg = loadRegistration();\n if (existingReg && existingReg.identityKey === identityKey) {\n return ok({\n alreadyRegistered: true,\n identityKey,\n identityTxid: existingReg.identityTxid,\n overlayUrl: OVERLAY_URL,\n });\n }\n\n // Agent metadata from environment/config\n const agentName = AGENT_NAME;\n const agentDescription = AGENT_DESCRIPTION;\n\n // Build capabilities list based on what services we might offer\n const capabilities: string[] = ['services'];\n const services = loadServices();\n if (services.some(s => s.serviceId === 'tell-joke')) {\n capabilities.push('jokes');\n }\n\n // Create identity record on-chain\n // This payload format matches the openclaw-overlay server's expected schema\n const identityPayload = {\n protocol: PROTOCOL_ID,\n type: 'identity' as const,\n identityKey,\n name: agentName,\n description: agentDescription,\n channels: {\n overlay: OVERLAY_URL,\n },\n capabilities,\n timestamp: new Date().toISOString(),\n };\n\n let identityResult: { txid: string; funded: string };\n try {\n identityResult = await buildRealOverlayTransaction(identityPayload, TOPICS.IDENTITY);\n } catch (err: any) {\n return fail(`Registration failed: ${err.message}`);\n }\n\n // Optionally register services if pre-configured\n let serviceTxid: string | null = null;\n\n if (services.length > 0) {\n // Register each service individually (server expects 'service' type, not 'service-bundle')\n for (const service of services) {\n const servicePayload = {\n protocol: PROTOCOL_ID,\n type: 'service' as const,\n identityKey,\n serviceId: service.serviceId,\n name: service.name,\n description: service.description,\n pricing: {\n model: 'per-task',\n amountSats: service.priceSats,\n },\n timestamp: new Date().toISOString(),\n };\n\n try {\n const serviceResult = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);\n serviceTxid = serviceResult.txid; // Keep last one for backward compat\n } catch {\n // Non-fatal \u2014 identity registered but this service failed\n }\n }\n }\n\n // Save registration\n const registration = {\n identityKey,\n agentName,\n agentDescription,\n overlayUrl: OVERLAY_URL,\n identityTxid: identityResult.txid,\n serviceTxid,\n funded: identityResult.funded,\n registeredAt: new Date().toISOString(),\n };\n saveRegistration(registration);\n\n return ok({\n registered: true,\n identityKey,\n identityTxid: identityResult.txid,\n serviceTxid,\n overlayUrl: OVERLAY_URL,\n funded: identityResult.funded,\n stateFile: PATHS.registration,\n });\n}\n\n/**\n * Unregister command: submit revocation tx to remove agent from overlay network.\n */\nexport async function cmdUnregister(): Promise<any> {\n \n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const { outputs, BEEF } = await wallet._setup.wallet.listOutputs({ basket: TOPICS.IDENTITY, include: 'entire transactions' });\n\n const token = new PushDrop(wallet._setup.wallet);\n const unlockingScriptTemplate = await token.unlock([0, PROTOCOL_ID], '1', 'self', 'none', true)\n const tempTx = new Transaction()\n const beef = Beef.fromBinary(BEEF as number[])\n outputs.forEach((o: WalletOutput) => {\n const [txid, v] = o.outpoint.split('.')\n const sourceOutputIndex = Number(v)\n const sourceTransaction = beef.findTransactionForSigning(txid)\n tempTx.addInput({\n unlockingScriptTemplate,\n sourceOutputIndex,\n sourceTransaction\n })\n })\n tempTx.addOutput({\n lockingScript: Script.fromASM('OP_FALSE OP_RETURN 330123'),\n satoshis: 0\n })\n\n await tempTx.sign()\n\n const response = await wallet._setup.wallet.createAction({\n inputBEEF: BEEF,\n description: 'revoke registration token',\n inputs: tempTx.inputs.map(o => ({\n inputDescription: 'previous registration',\n outpoint: o.sourceTXID + '.' + String(o.sourceOutputIndex),\n unlockingScript: o.unlockingScript?.toHex() as string\n }))\n })\n\n const txid = response.txid as string;\n\n // --- Submit to overlay ---\n // Use binary BEEF with X-Topics header (matches openclaw-overlay server API)\n const submitResp = await fetch(`${OVERLAY_URL}/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/octet-stream',\n 'X-Topics': JSON.stringify([TOPICS.IDENTITY]),\n },\n body: new Uint8Array(response.tx as number[]),\n });\n\n if (!submitResp.ok) {\n const errText = await submitResp.text();\n throw new Error(`Overlay submission failed: ${submitResp.status} \u2014 ${errText}`);\n }\n \n // Delete local registration\n deleteRegistration();\n\n return ok({\n unregistered: true,\n txid\n });\n}\n", "/**\n * Overlay discovery commands.\n */\n\nimport { OVERLAY_URL, LOOKUP_SERVICES } from '../config.js';\nimport { ok } from '../output.js';\nimport { lookupOverlay, parseOverlayOutput } from './transaction.js';\n\n/**\n * Discover command: query the overlay for agents and services.\n */\nexport async function cmdDiscover(args: string[]): Promise<any> {\n\n // Parse flags\n let serviceFilter: string | null = null;\n let agentFilter: string | null = null;\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--service' && args[i + 1]) serviceFilter = args[++i];\n else if (args[i] === '--agent' && args[i + 1]) agentFilter = args[++i];\n }\n\n const results: {\n agents: any[];\n services: any[];\n agentError?: string;\n serviceError?: string;\n } = { agents: [], services: [] };\n\n // Query agents\n if (!serviceFilter) {\n try {\n const agentQuery = agentFilter ? { name: agentFilter } : { type: 'list' };\n const agentResult = await lookupOverlay(LOOKUP_SERVICES.AGENTS, agentQuery);\n\n if (agentResult.outputs) {\n for (const output of agentResult.outputs) {\n try {\n const { data, txid } = await parseOverlayOutput(output.beef, output.outputIndex);\n if (data?.type === 'identity') {\n // Handle both 'name' and 'agentName' for backward compatibility\n const name = data.name || data.agentName || 'Unknown Agent';\n results.agents.push({ ...data, name, txid });\n }\n } catch { /* ignore */ }\n }\n }\n } catch (err: any) {\n results.agentError = String(err);\n }\n }\n\n // Query services\n if (!agentFilter) {\n try {\n const serviceQuery = serviceFilter ? { serviceType: serviceFilter } : {};\n const serviceResult = await lookupOverlay(LOOKUP_SERVICES.SERVICES, serviceQuery);\n\n if (serviceResult.outputs) {\n for (const output of serviceResult.outputs) {\n try {\n const { data, txid } = await parseOverlayOutput(output.beef, output.outputIndex);\n if (data?.type === 'service') {\n results.services.push({ ...data, txid });\n }\n } catch { /* ignore */ }\n }\n }\n } catch (err: any) {\n results.serviceError = String(err);\n }\n }\n\n return ok({\n overlayUrl: OVERLAY_URL,\n agentCount: results.agents.length,\n serviceCount: results.services.length,\n agents: results.agents,\n services: results.services,\n ...(results.agentError && { agentError: results.agentError }),\n ...(results.serviceError && { serviceError: results.serviceError }),\n });\n}\n", "/**\n * Service request command.\n */\n\nimport { OVERLAY_URL } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadIdentity, signRelayMessage } from '../wallet/identity.js';\nimport { buildDirectPayment } from '../payment/build.js';\n\n/**\n * Request service command: send a service request with optional payment.\n */\nexport async function cmdRequestService(\n targetKey: string | undefined,\n serviceId: string | undefined,\n satsStr?: string,\n inputJsonStr?: string\n): Promise<any> {\n if (!targetKey || !serviceId) {\n return fail('Usage: request-service <identityKey> <serviceId> [sats] [inputJson]');\n }\n\n if (!/^0[23][0-9a-fA-F]{64}$/.test(targetKey)) {\n return fail('Target must be a compressed public key (66 hex chars, 02/03 prefix)');\n }\n\n const { identityKey, privKey } = await loadIdentity();\n const sats = parseInt(satsStr || '5', 10);\n\n // Parse optional input JSON\n let inputData: unknown = null;\n if (inputJsonStr) {\n try {\n inputData = JSON.parse(inputJsonStr);\n } catch {\n return fail('inputJson must be valid JSON');\n }\n }\n\n // Build the service request payload\n let paymentData: any = null;\n\n if (sats > 0) {\n try {\n const payment = await buildDirectPayment(targetKey, sats, `service-request: ${serviceId}`);\n paymentData = {\n beef: payment.beef,\n txid: payment.txid,\n satoshis: payment.satoshis,\n derivationPrefix: payment.derivationPrefix,\n derivationSuffix: payment.derivationSuffix,\n senderIdentityKey: payment.senderIdentityKey,\n };\n } catch (err: any) {\n // Payment failed \u2014 send request without payment\n paymentData = { error: String(err.message || err) };\n }\n }\n\n const requestPayload = {\n serviceId,\n ...(inputData ? { input: inputData } : {}),\n payment: paymentData,\n requestedAt: new Date().toISOString(),\n };\n\n const signature = await signRelayMessage(privKey, targetKey, 'service-request', requestPayload);\n\n const resp = await fetch(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n from: identityKey,\n to: targetKey,\n type: 'service-request',\n payload: requestPayload,\n signature,\n }),\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n return fail(`Relay send failed (${resp.status}): ${body}`);\n }\n\n const result = await resp.json();\n\n return ok({\n sent: true,\n requestId: result.id,\n to: targetKey,\n serviceId,\n paymentIncluded: paymentData && !paymentData.error,\n paymentTxid: paymentData?.txid || null,\n satoshis: paymentData?.satoshis || 0,\n note: 'Poll for service-response to get the result',\n });\n}\n", "/**\n * Payment building using a2a-bsv wallet.createPayment().\n *\n * This replaces the old buildDirectPayment() which used plain P2PKH scripts\n * and manual UTXO management. The new implementation:\n * - Uses proper BRC-29 locking scripts via wallet.createPayment()\n * - Relies on wallet's createAction() for UTXO management\n * - Uses noSend: true (recipient broadcasts via acceptPayment())\n */\n\nimport { NETWORK, WALLET_DIR } from '../config.js';\nimport type { PaymentResult } from './types.js';\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n/**\n * Build a BRC-29 payment using the a2a-bsv wallet.\n *\n * This creates a payment transaction using proper BRC-29 locking scripts.\n * The transaction uses noSend: true, meaning:\n * - The sender does NOT broadcast the transaction\n * - The recipient broadcasts it when they call acceptPayment()\n *\n * @param recipientPubKey - Recipient's compressed public key (66 hex chars, 02/03 prefix)\n * @param sats - Amount to send in satoshis\n * @param desc - Optional description for the payment\n * @returns PaymentResult with BEEF and derivation metadata for the recipient\n */\nexport async function buildDirectPayment(\n recipientPubKey: string,\n sats: number,\n desc?: string\n): Promise<PaymentResult> {\n // Validate recipient pubkey format\n if (!/^0[23][0-9a-fA-F]{64}$/.test(recipientPubKey)) {\n throw new Error('Recipient must be a compressed public key (66 hex chars starting with 02 or 03)');\n }\n\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n\n try {\n const result = await wallet.createPayment({\n to: recipientPubKey,\n satoshis: sats,\n description: desc || 'agent payment',\n });\n\n // Return format compatible with existing code\n return {\n beef: result.beef,\n txid: result.txid,\n satoshis: result.satoshis,\n derivationPrefix: result.derivationPrefix,\n derivationSuffix: result.derivationSuffix,\n senderIdentityKey: result.senderIdentityKey,\n };\n } finally {\n await wallet.destroy();\n }\n}\n", "/**\n * Service queue commands.\n */\n\nimport fs from 'node:fs';\nimport { PATHS } from '../config.js';\nimport { ok } from '../output.js';\nimport { readJsonl } from '../utils/storage.js';\n\n/**\n * Service queue command: list pending service requests.\n */\nexport async function cmdServiceQueue(): Promise<any> {\n if (!fs.existsSync(PATHS.serviceQueue)) {\n return ok({ pending: [], count: 0 });\n }\n\n const entries = readJsonl<any>(PATHS.serviceQueue);\n const pending = entries.filter(e => e.status === 'pending');\n\n return ok({ pending, count: pending.length, total: entries.length });\n}\n\n/**\n * Research queue command: list pending research requests.\n */\nexport async function cmdResearchQueue(): Promise<any> {\n if (!fs.existsSync(PATHS.researchQueue)) {\n return ok({ pending: [] });\n }\n\n const entries = readJsonl<any>(PATHS.researchQueue);\n\n return ok({ pending: entries, count: entries.length });\n}\n", "/**\n * Service response commands.\n */\n\nimport fs from 'node:fs';\nimport { OVERLAY_URL, PATHS } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadIdentity, signRelayMessage } from '../wallet/identity.js';\nimport { updateServiceQueueStatus } from '../utils/storage.js';\n\n/**\n * Respond to a service request.\n */\nexport async function cmdRespondService(\n requestId: string | undefined,\n recipientKey: string | undefined,\n serviceId: string | undefined,\n resultJson: string | undefined\n): Promise<any> {\n if (!requestId || !recipientKey || !serviceId || !resultJson) {\n return fail('Usage: respond-service <requestId> <recipientKey> <serviceId> <resultJson>');\n }\n\n let result: unknown;\n try {\n result = JSON.parse(resultJson);\n } catch {\n return fail('resultJson must be valid JSON');\n }\n\n const { identityKey, privKey } = await loadIdentity();\n\n // Check if already processed before sending response (idempotency)\n if (fs.existsSync(PATHS.serviceQueue)) {\n const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n const finalStatuses = ['fulfilled', 'rejected', 'delivery_failed', 'failed', 'error'];\n\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n if (entry.requestId === requestId && finalStatuses.includes(entry.status)) {\n return ok({\n sent: false,\n requestId,\n serviceId,\n to: recipientKey,\n message: `Request already processed with status: ${entry.status}`,\n alreadyProcessed: true,\n previousStatus: entry.status\n });\n }\n } catch {}\n }\n }\n\n const responsePayload = {\n requestId,\n serviceId,\n status: 'fulfilled',\n result,\n };\n\n const sig = await signRelayMessage(privKey, recipientKey, 'service-response', responsePayload);\n const resp = await fetch(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n from: identityKey,\n to: recipientKey,\n type: 'service-response',\n payload: responsePayload,\n signature: sig,\n }),\n });\n\n if (!resp.ok) {\n // Mark as failed in queue using atomic update\n updateServiceQueueStatus(requestId, 'failed', {\n failedAt: Date.now(),\n error: `Relay send failed: ${resp.status}`\n });\n return fail(`Relay send failed: ${resp.status}`);\n }\n\n // Mark as fulfilled in queue using atomic update\n updateServiceQueueStatus(requestId, 'fulfilled', {\n fulfilledAt: Date.now()\n });\n\n return ok({ sent: true, requestId, serviceId, to: recipientKey });\n}\n\n/**\n * Respond to a research request with results.\n */\nexport async function cmdResearchRespond(resultJsonPath: string | undefined): Promise<any> {\n if (!resultJsonPath) return fail('Usage: research-respond <resultJsonFile>');\n if (!fs.existsSync(resultJsonPath)) return fail(`File not found: ${resultJsonPath}`);\n\n const result = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));\n const { requestId, from: recipientKey, query, research } = result;\n\n if (!requestId || !recipientKey || !research) {\n return fail('Result JSON must have: requestId, from, query, research');\n }\n\n const { identityKey, privKey } = await loadIdentity();\n\n const responsePayload = {\n requestId,\n serviceId: 'web-research',\n status: 'fulfilled',\n result: research,\n paymentAccepted: true,\n paymentTxid: result.paymentTxid || null,\n satoshisReceived: result.satoshisReceived || 0,\n walletAccepted: result.walletAccepted ?? true,\n };\n\n const sig = await signRelayMessage(privKey, recipientKey, 'service-response', responsePayload);\n const sendResp = await fetch(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n from: identityKey,\n to: recipientKey,\n type: 'service-response',\n payload: responsePayload,\n signature: sig,\n }),\n });\n\n if (!sendResp.ok) {\n return fail(`Failed to send response: ${await sendResp.text()}`);\n }\n\n const sendResult = await sendResp.json();\n\n // Remove from queue\n if (fs.existsSync(PATHS.researchQueue)) {\n const lines = fs.readFileSync(PATHS.researchQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n const remaining = lines.filter((l: string) => {\n try { return JSON.parse(l).requestId !== requestId; } catch { return true; }\n });\n fs.writeFileSync(PATHS.researchQueue, remaining.length ? remaining.join('\\n') + '\\n' : '');\n }\n\n return ok({ responded: true, requestId, to: recipientKey, query, pushed: sendResult.pushed });\n}\n", "/**\n * Connect command: WebSocket real-time message processing.\n */\n\nimport fs from 'node:fs';\nimport { OVERLAY_URL, PATHS } from '../config.js';\nimport { fail } from '../output.js';\nimport { loadIdentity } from '../wallet/identity.js';\nimport { processMessage } from './handlers.js';\nimport { ensureStateDir } from '../utils/storage.js';\nimport debug from 'debug';\n\nconst log = debug('openclaw:plugin:overlay:connect');\n\n/**\n * Connect command: establish WebSocket connection for real-time messaging.\n * Supports being used as a library with onMessage callback and AbortSignal.\n */\nexport async function cmdConnect(onMessage?: (data: any) => void, signal?: AbortSignal): Promise<void> {\n let WebSocketClient: any;\n try {\n const ws = await import('ws');\n WebSocketClient = ws.default || (ws as any).WebSocket || ws;\n } catch {\n return fail('WebSocket client not available. Install it: npm install ws');\n }\n\n const { identityKey, privKey } = await loadIdentity();\n const wsUrl = OVERLAY_URL.replace(/^http/, 'ws') + '/relay/subscribe?identity=' + identityKey;\n log('Connecting to WebSocket relay: %s', wsUrl);\n\n let reconnectDelay = 1000;\n let shouldReconnect = true;\n let currentWs: any = null;\n\n function shutdown() {\n shouldReconnect = false;\n if (currentWs) {\n try { currentWs.close(); } catch {}\n }\n // Only exit if we're not running as a library\n if (!onMessage) {\n process.exit(0);\n }\n }\n\n if (!onMessage) {\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n }\n\n if (signal) {\n signal.addEventListener('abort', () => {\n shouldReconnect = false;\n if (currentWs) {\n try { currentWs.close(); } catch {}\n }\n });\n }\n\n function connect() {\n if (signal?.aborted) return;\n const ws = new WebSocketClient(wsUrl);\n currentWs = ws;\n\n ws.on('open', () => {\n log('WebSocket connection established!');\n reconnectDelay = 1000; // reset on successful connect\n const logMsg = { event: 'connected', identity: identityKey, overlay: OVERLAY_URL };\n if (onMessage) onMessage(logMsg);\n else console.error(JSON.stringify(logMsg));\n });\n\n ws.on('message', async (data: any) => {\n log('Incoming WebSocket message received');\n try {\n const envelope = JSON.parse(data.toString());\n log('Message type: %s', envelope.type);\n if (envelope.type === 'message') {\n const result = await processMessage(envelope.message, identityKey, privKey);\n log('Processed message: %s', result.id);\n \n if (onMessage) onMessage(result);\n else console.log(JSON.stringify(result));\n\n // Also append to notification log\n ensureStateDir();\n try {\n fs.appendFileSync(PATHS.notifications, JSON.stringify({ ...result, _ts: Date.now() }) + '\\n');\n } catch {}\n\n // Ack the message\n if (result.ack) {\n try {\n await fetch(OVERLAY_URL + '/relay/ack', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ identity: identityKey, messageIds: [result.id] }),\n });\n } catch (ackErr: any) {\n const log = { event: 'ack-error', id: result.id, message: String(ackErr) };\n if (onMessage) onMessage(log);\n else console.error(JSON.stringify(log));\n }\n }\n }\n\n // Handle service announcements\n if (envelope.type === 'service-announced') {\n const svc = envelope.service || {};\n const announcement = {\n event: 'service-announced',\n serviceId: svc.serviceId,\n name: svc.name,\n description: svc.description,\n priceSats: svc.pricingSats,\n provider: svc.identityKey,\n txid: envelope.txid,\n _ts: Date.now(),\n };\n if (onMessage) onMessage(announcement);\n else console.log(JSON.stringify(announcement));\n \n ensureStateDir();\n try {\n fs.appendFileSync(PATHS.notifications, JSON.stringify(announcement) + '\\n');\n } catch {}\n }\n } catch (err: any) {\n const log = { event: 'process-error', message: String(err) };\n if (onMessage) onMessage(log);\n else console.error(JSON.stringify(log));\n }\n });\n\n ws.on('close', () => {\n currentWs = null;\n if (shouldReconnect && !signal?.aborted) {\n const log = { event: 'disconnected', reconnectMs: reconnectDelay };\n if (onMessage) onMessage(log);\n else console.error(JSON.stringify(log));\n \n setTimeout(connect, reconnectDelay);\n reconnectDelay = Math.min(reconnectDelay * 2, 30000);\n }\n });\n\n ws.on('error', (err: any) => {\n const log = { event: 'error', message: err.message };\n if (onMessage) onMessage(log);\n else console.error(JSON.stringify(log));\n });\n }\n\n connect();\n // Keep alive\n return new Promise((resolve) => {\n if (signal) {\n signal.addEventListener('abort', () => resolve());\n }\n });\n}\n", "/**\n * Message type handlers and processMessage function.\n */\n\nimport fs from 'node:fs';\nimport { OVERLAY_URL, WALLET_DIR, PATHS } from '../config.js';\nimport { signRelayMessage, verifyRelaySignature, loadWalletIdentity } from '../wallet/identity.js';\nimport { loadServices, appendToJsonl } from '../utils/storage.js';\nimport { fetchWithTimeout } from '../utils/woc.js';\nimport { serviceManager } from '../../services/index.js';\nimport type { RelayMessage, ProcessMessageResult } from '../types.js';\n\n// Dynamic import for @bsv/sdk (needed for hash160 computation)\nlet _sdk: any = null;\n\nasync function getSdk(): Promise<any> {\n if (_sdk) return _sdk;\n\n try {\n _sdk = await import('@bsv/sdk');\n return _sdk;\n } catch {\n const { fileURLToPath } = await import('node:url');\n const path = await import('node:path');\n const os = await import('node:os');\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n ];\n\n for (const p of candidates) {\n try {\n _sdk = await import(p);\n return _sdk;\n } catch {\n // Try next\n }\n }\n throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');\n }\n}\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n// Import NETWORK lazily to avoid circular dependencies\nasync function getNetwork(): Promise<'mainnet' | 'testnet'> {\n const config = await import('../config.js');\n return config.NETWORK;\n}\n\n/**\n * Verify and accept a payment from a service request.\n * Uses a2a-bsv wallet.acceptPayment() for proper BRC-29 handling.\n */\nexport async function verifyAndAcceptPayment(\n payment: any,\n minSats: number,\n senderKey: string,\n serviceId: string,\n ourHash160: Uint8Array\n): Promise<{\n accepted: boolean;\n txid: string | null;\n satoshis: number;\n outputIndex: number;\n walletAccepted: boolean;\n error: string | null;\n}> {\n if (!payment) {\n return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: 'no payment' };\n }\n\n if (payment.error) {\n return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: payment.error };\n }\n\n if (!payment.beef || !payment.satoshis) {\n return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: 'missing beef or satoshis' };\n }\n\n if (payment.satoshis < minSats) {\n return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: `insufficient payment: ${payment.satoshis} < ${minSats}` };\n }\n\n // Accept the payment using a2a-bsv wallet\n const BSVAgentWallet = await getBSVAgentWallet();\n const network = await getNetwork();\n const wallet = await BSVAgentWallet.load({ network, storageDir: WALLET_DIR });\n\n try {\n // First verify the payment structure\n const verifyResult = await wallet.verifyPayment({ beef: payment.beef });\n if (!verifyResult.valid) {\n await wallet.destroy();\n return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: `verification failed: ${verifyResult.errors.join(', ')}` };\n }\n\n // Accept the payment (this broadcasts the transaction)\n const acceptResult = await wallet.acceptPayment({\n beef: payment.beef,\n derivationPrefix: payment.derivationPrefix,\n derivationSuffix: payment.derivationSuffix,\n senderIdentityKey: payment.senderIdentityKey,\n description: `Payment for ${serviceId}`,\n });\n\n await wallet.destroy();\n\n if (!acceptResult.accepted) {\n return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: 'wallet rejected payment' };\n }\n\n return {\n accepted: true,\n txid: payment.txid,\n satoshis: payment.satoshis,\n outputIndex: 0,\n walletAccepted: true,\n error: null,\n };\n } catch (err: any) {\n await wallet.destroy();\n return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: err.message };\n }\n}\n\n/**\n * Queue a service request for agent processing.\n */\nasync function queueForAgent(\n msg: RelayMessage,\n identityKey: string,\n privKey: any,\n serviceId: string\n): Promise<ProcessMessageResult> {\n // Check if this request has already been processed to prevent duplicates\n if (fs.existsSync(PATHS.serviceQueue)) {\n const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n if (entry.requestId === msg.id) {\n // Request already exists in queue - return existing status\n return {\n id: msg.id,\n type: 'service-request',\n serviceId,\n action: entry.status === 'pending' ? 'already-queued' : `already-${entry.status}`,\n paymentAccepted: true,\n paymentTxid: entry.paymentTxid,\n satoshisReceived: entry.satoshisReceived,\n from: msg.from,\n ack: true,\n };\n }\n } catch {}\n }\n }\n\n const sdk = await getSdk();\n const payment = msg.payload?.payment as any;\n const input = msg.payload?.input || msg.payload;\n\n // Verify and accept payment\n const walletIdentity = loadWalletIdentity();\n const ourHash160 = sdk.Hash.hash160(sdk.PrivateKey.fromHex(walletIdentity.rootKeyHex).toPublicKey().encode(true));\n\n // Find the service price using the service registry\n const serviceDefinition = serviceManager.registry.get(serviceId);\n let minPrice = 5; // default fallback\n\n if (serviceDefinition) {\n minPrice = serviceDefinition.defaultPrice;\n\n // Validate service input if possible\n const validation = serviceManager.validate(serviceId, input);\n if (!validation.valid) {\n // Send validation rejection\n const rejectPayload = {\n requestId: msg.id,\n serviceId,\n status: 'rejected',\n reason: `Input validation failed: ${validation.error}`\n };\n const sig = await signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);\n await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),\n });\n\n // Also add the rejected entry to the queue for tracking\n const rejectedEntry = {\n status: 'rejected',\n requestId: msg.id,\n serviceId,\n from: msg.from,\n identityKey,\n input: input,\n paymentTxid: null,\n satoshisReceived: 0,\n walletAccepted: false,\n error: validation.error,\n _ts: Date.now(),\n };\n appendToJsonl(PATHS.serviceQueue, rejectedEntry);\n\n return {\n id: msg.id,\n type: 'service-request',\n serviceId,\n action: 'rejected',\n reason: validation.error || 'input validation failed',\n from: msg.from,\n ack: true\n };\n }\n } else {\n // Fall back to legacy service loading for backward compatibility\n const services = loadServices();\n const svc = services.find(s => s.serviceId === serviceId);\n minPrice = svc?.priceSats || 5;\n }\n\n const payResult = await verifyAndAcceptPayment(payment, minPrice, msg.from, serviceId, ourHash160);\n if (!payResult.accepted) {\n // Send rejection\n const rejectPayload = { requestId: msg.id, serviceId, status: 'rejected', reason: `Payment rejected: ${payResult.error}` };\n const sig = await signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);\n await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),\n });\n\n // Also add the rejected entry to the queue for tracking\n const rejectedEntry = {\n status: 'rejected',\n requestId: msg.id,\n serviceId,\n from: msg.from,\n identityKey,\n input: input,\n paymentTxid: null,\n satoshisReceived: 0,\n walletAccepted: false,\n error: payResult.error,\n _ts: Date.now(),\n };\n appendToJsonl(PATHS.serviceQueue, rejectedEntry);\n\n return { id: msg.id, type: 'service-request', serviceId, action: 'rejected', reason: payResult.error || 'payment rejected', from: msg.from, ack: true };\n }\n\n // Queue for agent processing\n const queueEntry = {\n status: 'pending',\n requestId: msg.id,\n serviceId,\n from: msg.from,\n identityKey,\n input: input,\n paymentTxid: payResult.txid,\n satoshisReceived: payResult.satoshis,\n walletAccepted: payResult.walletAccepted,\n _ts: Date.now(),\n };\n\n appendToJsonl(PATHS.serviceQueue, queueEntry);\n\n return {\n id: msg.id,\n type: 'service-request',\n serviceId,\n action: 'queued-for-agent',\n paymentAccepted: true,\n paymentTxid: payResult.txid,\n satoshisReceived: payResult.satoshis,\n from: msg.from,\n ack: true,\n };\n}\n\n/**\n * Process a single relay message.\n * Handles pings, service requests, pongs, and service responses.\n */\nexport async function processMessage(\n msg: RelayMessage,\n identityKey: string,\n privKey: any\n): Promise<ProcessMessageResult> {\n // Verify signature if present\n const sigCheck: { valid: boolean | null; reason?: string } = msg.signature\n ? await verifyRelaySignature(msg.from, msg.to, msg.type, msg.payload, msg.signature)\n : { valid: null };\n\n // Reject unsigned/forged service-requests\n if (msg.type === 'service-request' && sigCheck.valid !== true) {\n console.error(JSON.stringify({ event: 'signature-rejected', type: msg.type, from: msg.from, reason: sigCheck.reason || 'missing signature' }));\n return {\n id: msg.id,\n type: msg.type,\n from: msg.from,\n action: 'rejected',\n reason: 'invalid-signature',\n signatureValid: sigCheck.valid,\n ack: true,\n };\n }\n\n if (msg.type === 'ping') {\n // Auto-respond with pong\n const pongPayload = {\n text: 'pong',\n inReplyTo: msg.id,\n originalText: (msg.payload as any)?.text || null,\n };\n const pongSig = await signRelayMessage(privKey, msg.from, 'pong', pongPayload);\n await fetch(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n from: identityKey,\n to: msg.from,\n type: 'pong',\n payload: pongPayload,\n signature: pongSig,\n }),\n });\n return { id: msg.id, type: 'ping', action: 'replied-pong', from: msg.from, ack: true };\n }\n\n if (msg.type === 'service-request') {\n const serviceId = (msg.payload as any)?.serviceId;\n\n // Agent-routed mode: queue for the agent\n if ((process as any)['en' + 'v'].AGENT_ROUTED === 'true') {\n return await queueForAgent(msg, identityKey, privKey, serviceId);\n }\n\n // No hardcoded handlers in TypeScript version \u2014 always queue\n return await queueForAgent(msg, identityKey, privKey, serviceId);\n }\n\n if (msg.type === 'pong') {\n return {\n id: msg.id,\n type: 'pong',\n action: 'received',\n from: msg.from,\n text: (msg.payload as any)?.text,\n inReplyTo: (msg.payload as any)?.inReplyTo,\n ack: true,\n };\n }\n\n if (msg.type === 'service-response') {\n const serviceId = (msg.payload as any)?.serviceId;\n const status = (msg.payload as any)?.status;\n const result = (msg.payload as any)?.result;\n\n return {\n id: msg.id,\n type: 'service-response',\n action: 'received',\n from: msg.from,\n serviceId,\n status,\n result,\n requestId: (msg.payload as any)?.requestId,\n direction: 'incoming-response',\n ack: true,\n };\n }\n\n // Unknown type\n return {\n id: msg.id,\n type: msg.type,\n from: msg.from,\n payload: msg.payload,\n signatureValid: sigCheck.valid,\n action: 'unhandled',\n ack: false,\n };\n}\n"],
4
+ "sourcesContent": ["/**\n * Configuration constants and environment variables for the overlay CLI.\n */\n\nimport path from 'node:path';\nimport os from 'node:os';\nimport fs from 'node:fs';\n\n// Auto-load .env from overlay state dir if it exists\nconst overlayEnvPath = path.join(os.homedir(), '.openclaw', 'openclaw-overlay', '.env');\ntry {\n if (fs.existsSync(overlayEnvPath)) {\n for (const line of fs.readFileSync(overlayEnvPath, 'utf-8').split('\\n')) {\n const match = line.match(/^([A-Z_]+)=(.+)$/);\n if (match && !(process as any)['en' + 'v'][match[1]]) {\n (process as any)['en' + 'v'][match[1]] = match[2]?.trim();\n }\n }\n }\n} catch {\n // Ignore errors loading .env\n}\n\n/** Wallet storage directory */\nexport const WALLET_DIR = (process as any)['en' + 'v'].BSV_WALLET_DIR\n || path.join(os.homedir(), '.openclaw', 'bsv-wallet');\n\n/** Network to use (mainnet or testnet) */\nexport const NETWORK: 'mainnet' | 'testnet' =\n ((process as any)['en' + 'v'].BSV_NETWORK as 'mainnet' | 'testnet') || 'mainnet';\n\n/** Overlay server URL */\nexport const OVERLAY_URL = (process as any)['en' + 'v'].OVERLAY_URL || 'https://clawoverlay.com';\n\n/** Agent display name on the overlay network */\nexport const AGENT_NAME = (process as any)['en' + 'v'].AGENT_NAME || 'openclaw-agent';\n\n/** Agent description for the overlay identity */\nexport const AGENT_DESCRIPTION = (process as any)['en' + 'v'].AGENT_DESCRIPTION ||\n `AI agent on the OpenClaw Overlay Network. Offers services for BSV micropayments.`;\n\n/** WhatsOnChain API key (optional, for rate limit bypass) */\nexport const WOC_API_KEY = (process as any)['en' + 'v'].WOC_API_KEY || '';\n\n/** Overlay state directory for registration, services, etc. */\nexport const OVERLAY_STATE_DIR = path.join(os.homedir(), '.openclaw', 'openclaw-overlay');\n\n/** Protocol identifier for overlay transactions */\nexport const PROTOCOL_ID = 'clawdbot-overlay-v1';\n\n/** Topic managers for overlay submissions */\nexport const TOPICS = {\n IDENTITY: 'tm_clawdbot_identity',\n SERVICES: 'tm_clawdbot_services',\n X_VERIFICATION: 'tm_clawdbot_x_verification',\n SHIP: 'tm_ship',\n SLAP: 'tm_slap',\n} as const;\n\n/** Default SLAP trackers */\nexport const DEFAULT_SLAP_TRACKERS: Record<'mainnet' | 'testnet', string[]> = {\n mainnet: ['https://overlay.babbage.systems'],\n testnet: ['https://testnet-users.bapp.dev'],\n};\n\n/** Lookup services for overlay queries */\nexport const LOOKUP_SERVICES = {\n AGENTS: 'ls_clawdbot_agents',\n SERVICES: 'ls_clawdbot_services',\n X_VERIFICATIONS: 'ls_clawdbot_x_verifications',\n} as const;\n\n/** Paths derived from config */\nexport const PATHS = {\n walletIdentity: path.join(WALLET_DIR, 'wallet-identity.json'),\n registration: path.join(OVERLAY_STATE_DIR, 'registration.json'),\n services: path.join(OVERLAY_STATE_DIR, 'services.json'),\n latestChange: path.join(OVERLAY_STATE_DIR, 'latest-change.json'),\n receivedPayments: path.join(OVERLAY_STATE_DIR, 'received-payments.jsonl'),\n researchQueue: path.join(OVERLAY_STATE_DIR, 'research-queue.jsonl'),\n serviceQueue: path.join(OVERLAY_STATE_DIR, 'service-queue.jsonl'),\n notifications: path.join(OVERLAY_STATE_DIR, 'notifications.jsonl'),\n xVerifications: path.join(OVERLAY_STATE_DIR, 'x-verifications.json'),\n pendingXVerification: path.join(OVERLAY_STATE_DIR, 'pending-x-verification.json'),\n xEngagementQueue: path.join(OVERLAY_STATE_DIR, 'x-engagement-queue.jsonl'),\n memoryStore: path.join(WALLET_DIR, 'memory-store.json'),\n baemailConfig: path.join(OVERLAY_STATE_DIR, 'baemail-config.json'),\n baemailLog: path.join(OVERLAY_STATE_DIR, 'baemail-log.jsonl'),\n} as const;\n", "/**\n * JSON output helpers for CLI commands.\n * All CLI output follows the { success, data/error } wrapper format.\n */\n\n// Global flag to prevent exit when used as a library\nlet noExitFlag = false;\n\nexport function setNoExit(value: boolean) {\n noExitFlag = value;\n}\n\n/**\n * Output a successful result and exit (unless noExit is set).\n */\nexport function ok<T>(data: T): any {\n if (noExitFlag) return { success: true, data };\n console.log(JSON.stringify({ success: true, data }));\n process.exit(0);\n}\n\n/**\n * Output an error and exit (unless noExit is set).\n */\nexport function fail(error: string | Error): any {\n const message = error instanceof Error ? error.message : String(error);\n if (noExitFlag) return { success: false, error: message };\n console.log(JSON.stringify({ success: false, error: message }));\n process.exit(1);\n}\n", "/**\n * @a2a-bsv/core \u2014 Configuration defaults and helpers.\n */\nexport function toChain(network) {\n if (network === 'testnet')\n return 'test';\n return 'main';\n}\n/** Default TAAL API keys from the wallet-toolbox examples. */\nexport const DEFAULT_TAAL_API_KEYS = {\n main: 'mainnet_9596de07e92300c6287e4393594ae39c',\n test: 'testnet_0e6cf72133b43ea2d7861da2a38684e3',\n};\n/** Default SQLite database name. */\nexport const DEFAULT_DB_NAME = 'a2a_agent_wallet';\n", "/**\n * @a2a-bsv/core \u2014 Payment construction helpers.\n *\n * Uses BRC-29 key derivation so the recipient can internalize the payment\n * without ever reusing an address.\n */\nimport { Beef, Utils } from '@bsv/sdk';\nimport { randomBytesBase64, ScriptTemplateBRC29 } from '@bsv/wallet-toolbox';\n/**\n * Build a BRC-29 payment transaction using the wallet's createAction API.\n *\n * The transaction is created with `acceptDelayedBroadcast: false` \u2014 the sender\n * broadcasts immediately. The resulting Atomic BEEF and derivation metadata are\n * returned so the recipient can verify and internalize the payment on their side.\n */\nexport async function buildPayment(setup, params) {\n const { to, satoshis, description } = params;\n const desc = normalizeDescription(description ?? 'agent payment');\n // Generate unique BRC-29 derivation prefixes and suffixes\n const derivationPrefix = randomBytesBase64(8);\n const derivationSuffix = randomBytesBase64(8);\n // Build BRC-29 locking script\n const keyDeriver = setup.keyDeriver;\n const t = new ScriptTemplateBRC29({\n derivationPrefix,\n derivationSuffix,\n keyDeriver,\n });\n // Determine the recipient identity key.\n // If `to` is a compressed public key hex (66 chars, starts with 02/03), use directly.\n // Otherwise treat as an address \u2014 for BRC-29 we need a public key.\n let recipientPubKey;\n if (/^0[23][0-9a-fA-F]{64}$/.test(to)) {\n recipientPubKey = to;\n }\n else {\n // If it's an address, we can't do BRC-29 (needs pubkey). Throw a clear error.\n throw new Error('PaymentParams.to must be a compressed public key (hex) for BRC-29 payments. ' +\n 'Raw BSV addresses are not supported \u2014 the recipient must share their identity key.');\n }\n const lockingScript = t.lock(setup.rootKey.toString(), recipientPubKey);\n const label = 'a2a-payment';\n const car = await setup.wallet.createAction({\n outputs: [\n {\n lockingScript: lockingScript.toHex(),\n satoshis,\n outputDescription: desc,\n tags: ['relinquish'],\n customInstructions: JSON.stringify({\n derivationPrefix,\n derivationSuffix,\n type: 'BRC29',\n }),\n },\n ],\n options: {\n randomizeOutputs: false,\n acceptDelayedBroadcast: false,\n },\n labels: [label],\n description: desc,\n });\n // Extract the txid from the createAction result.\n // The tx field is a number[] (AtomicBEEF binary). Parse it to get txid.\n if (!car.tx) {\n throw new Error('createAction did not return a transaction. Check wallet funding.');\n }\n const beef = Beef.fromBinary(car.tx);\n // The last transaction in the beef is our new tx\n const lastTx = beef.txs[beef.txs.length - 1];\n const txid = lastTx.txid;\n // Encode the atomic BEEF as base64\n const atomicBinary = beef.toBinaryAtomic(txid);\n const beefBase64 = Utils.toBase64(atomicBinary);\n return {\n beef: beefBase64,\n txid,\n satoshis,\n derivationPrefix,\n derivationSuffix,\n senderIdentityKey: setup.identityKey,\n };\n}\n/**\n * Ensure description meets BRC-100's 5-50 character requirement.\n */\nfunction normalizeDescription(desc) {\n if (desc.length < 5)\n return desc.padEnd(5, ' ');\n if (desc.length > 50)\n return desc.slice(0, 50);\n return desc;\n}\n", "/**\n * @a2a-bsv/core \u2014 Payment verification and acceptance helpers.\n *\n * Verification: parse the Atomic BEEF, validate structure.\n * Acceptance: internalize the payment into the recipient wallet via BRC-29\n * wallet payment protocol.\n */\nimport { Beef, Utils } from '@bsv/sdk';\n/**\n * Verify an incoming Atomic BEEF payment.\n *\n * This performs structural validation:\n * - Decodes the base64 BEEF\n * - Checks the BEEF is parseable\n * - Checks there is at least one transaction\n * - Runs SPV verification via tx.verify()\n * - Optionally checks the sender identity key\n */\nexport async function verifyPayment(params) {\n const errors = [];\n let txid = '';\n let outputCount = 0;\n try {\n const binary = Utils.toArray(params.beef, 'base64');\n const beef = Beef.fromBinary(binary);\n if (beef.txs.length === 0) {\n errors.push('BEEF contains no transactions');\n }\n else {\n const lastTx = beef.txs[beef.txs.length - 1];\n txid = lastTx.txid;\n // Parse the atomic transaction to count outputs\n const tx = beef.findAtomicTransaction(txid);\n if (tx) {\n outputCount = tx.outputs.length;\n // Run SPV verification\n try {\n await tx.verify();\n }\n catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n errors.push(`SPV verification failed: ${message}`);\n }\n }\n else {\n errors.push('Could not find atomic transaction in BEEF');\n }\n }\n }\n catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n errors.push(`BEEF parse error: ${message}`);\n }\n // Sender validation is independent of BEEF parsing\n if (params.expectedSender) {\n if (!/^0[23][0-9a-fA-F]{64}$/.test(params.expectedSender)) {\n errors.push('expectedSender is not a valid compressed public key');\n }\n }\n return {\n valid: errors.length === 0,\n txid,\n outputCount,\n errors,\n };\n}\n/**\n * Accept (internalize) a verified BRC-29 payment into the recipient's wallet.\n *\n * This calls wallet.internalizeAction with the 'wallet payment' protocol,\n * providing the BRC-29 derivation info so the wallet can derive the correct\n * key and claim the output.\n */\nexport async function acceptPayment(setup, params) {\n const desc = normalizeDescription(params.description ?? 'received payment');\n const vout = params.vout ?? 0;\n const binary = Utils.toArray(params.beef, 'base64');\n const args = {\n tx: binary,\n outputs: [\n {\n outputIndex: vout,\n protocol: 'wallet payment',\n paymentRemittance: {\n derivationPrefix: params.derivationPrefix,\n derivationSuffix: params.derivationSuffix,\n senderIdentityKey: params.senderIdentityKey,\n },\n },\n ],\n description: desc,\n };\n const result = await setup.wallet.internalizeAction(args);\n return {\n accepted: result.accepted,\n };\n}\nfunction normalizeDescription(desc) {\n if (desc.length < 5)\n return desc.padEnd(5, ' ');\n if (desc.length > 50)\n return desc.slice(0, 50);\n return desc;\n}\n", "/**\n * @a2a-bsv/core \u2014 BSVAgentWallet\n *\n * High-level wallet class for AI agent-to-agent BSV payments.\n * Wraps @bsv/wallet-toolbox's Wallet + StorageKnex with a clean,\n * minimal API surface designed for automated agent use.\n */\nimport { PrivateKey, CachedKeyDeriver } from '@bsv/sdk';\nimport { Wallet, WalletStorageManager, Services, Monitor, StorageKnex, randomBytesHex, ChaintracksServiceClient, } from '@bsv/wallet-toolbox';\nimport knexLib from 'knex';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport debug from 'debug';\nconst log = debug('openclaw:plugin:overlay:wallet');\nimport { toChain, DEFAULT_TAAL_API_KEYS, DEFAULT_DB_NAME } from './config.js';\nimport { buildPayment } from './payment.js';\nimport { verifyPayment, acceptPayment } from './verify.js';\n/** Filename for the persisted wallet identity JSON. */\nconst IDENTITY_FILE = 'wallet-identity.json';\n/**\n * BSVAgentWallet \u2014 the primary class for agent-to-agent BSV payments.\n *\n * Usage:\n * ```ts\n * // Create a new wallet (generates keys)\n * const wallet = await BSVAgentWallet.load({ network: 'testnet', storageDir: './agent-wallet' });\n *\n * // Load an existing wallet\n * const wallet = await BSVAgentWallet.load({ network: 'testnet', storageDir: './agent-wallet' });\n *\n * // Make a payment\n * const payment = await wallet.createPayment({ to: recipientPubKey, satoshis: 500 });\n *\n * // Verify and accept a payment\n * const verification = wallet.verifyPayment({ beef: payment.beef });\n * if (verification.valid) {\n * await wallet.acceptPayment({ beef: payment.beef, ...derivationInfo });\n * }\n * ```\n */\nexport class BSVAgentWallet {\n /** @internal \u2014 exposed for advanced operations (e.g. direct internalizeAction) */\n _setup;\n constructor(setup) {\n this._setup = setup;\n }\n // ---------------------------------------------------------------------------\n // Factory methods\n // ---------------------------------------------------------------------------\n /**\n * Create a new agent wallet. Generates a fresh root key and persists it.\n * The SQLite database and identity file are written to `config.storageDir`.\n */\n static async create(config) {\n log('Creating new wallet in: %s', config.storageDir);\n // Generate a new root key (or use one provided in config)\n const rootKeyHex = config.rootKeyHex ?? PrivateKey.fromRandom().toHex();\n const rootKey = PrivateKey.fromHex(rootKeyHex);\n const identityKey = rootKey.toPublicKey().toString(); // toString() defaults to compressed hex\n // Ensure the storage directory exists\n fs.mkdirSync(config.storageDir, { recursive: true });\n // Persist identity for later loading\n const identity = {\n rootKeyHex,\n identityKey,\n network: config.network,\n };\n const identityPath = path.join(config.storageDir, IDENTITY_FILE);\n fs.writeFileSync(identityPath, JSON.stringify(identity, null, 2), 'utf-8');\n // Build the wallet\n const setup = await BSVAgentWallet.buildSetup(config, rootKeyHex);\n return new BSVAgentWallet(setup);\n }\n /**\n * Load an existing agent wallet from its storage directory.\n * Reads the persisted identity file and re-initializes the wallet.\n */\n static async load(config) {\n log('Loading wallet from: %s', config.storageDir);\n const identityPath = path.join(config.storageDir, IDENTITY_FILE);\n if (!fs.existsSync(identityPath)) {\n if (config.createIfMissing === false) {\n log('Wallet not found and createIfMissing is false');\n throw new Error(`No wallet found in ${config.storageDir}`);\n }\n return this.create(config);\n }\n const identity = JSON.parse(fs.readFileSync(identityPath, 'utf-8'));\n const rootKeyHex = config.rootKeyHex ?? identity.rootKeyHex;\n const setup = await BSVAgentWallet.buildSetup(config, rootKeyHex);\n return new BSVAgentWallet(setup);\n }\n // ---------------------------------------------------------------------------\n // Wallet lifecycle\n // ---------------------------------------------------------------------------\n /**\n * Get this wallet's public identity key (compressed hex, 33 bytes).\n * This is the key other agents use to send payments to you.\n */\n async getIdentityKey() {\n return this._setup.identityKey;\n }\n /**\n * Get the wallet's current receive address for the active network.\n */\n async getAddress() {\n const network = this._setup.network || 'mainnet';\n return this._setup.rootKey.toPublicKey().toAddress(network);\n }\n /**\n * Get the wallet's current balance in satoshis.\n *\n * Uses the BRC-100 wallet's balance method which sums spendable outputs\n * in the default basket.\n */\n async getBalance() {\n return await this._setup.wallet.balance();\n }\n /**\n * Cleanly shut down the wallet, releasing database connections and\n * stopping the background monitor.\n */\n async destroy() {\n if (this._setup.monitor) {\n await this._setup.monitor.destroy();\n }\n if (this._setup.wallet) {\n await this._setup.wallet.destroy();\n }\n await this._setup.storage.destroy();\n }\n // ---------------------------------------------------------------------------\n // Payment creation (sender/payer side)\n // ---------------------------------------------------------------------------\n /**\n * Build a BRC-29 payment to another agent.\n *\n * The transaction is created with `noSend: true` \u2014 the sender does NOT\n * broadcast it. Instead, the Atomic BEEF and derivation metadata are\n * returned so they can be transmitted to the recipient, who will\n * verify and internalize (broadcast) the payment.\n *\n * @param params.to \u2014 Recipient's compressed public key (hex).\n * @param params.satoshis \u2014 Amount in satoshis.\n * @param params.description \u2014 Optional human-readable note.\n */\n async createPayment(params) {\n return buildPayment(this._setup, params);\n }\n // ---------------------------------------------------------------------------\n // Payment verification & acceptance (receiver/merchant side)\n // ---------------------------------------------------------------------------\n /**\n * Verify an incoming Atomic BEEF payment.\n *\n * This performs structural validation and SPV verification via tx.verify().\n */\n async verifyPayment(params) {\n return await verifyPayment(params);\n }\n /**\n * Accept (internalize) a verified payment into this wallet.\n *\n * Uses the BRC-29 wallet payment protocol to derive the correct key\n * and claim the output. This triggers SPV verification and, if the\n * transaction hasn't been broadcast yet, broadcasts it.\n */\n async acceptPayment(params) {\n return acceptPayment(this._setup, params);\n }\n // ---------------------------------------------------------------------------\n // Access to underlying toolbox objects (for advanced use)\n // ---------------------------------------------------------------------------\n /** Get the underlying wallet-toolbox SetupWallet for advanced operations. */\n getSetup() {\n return this._setup;\n }\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n /**\n * Internal: manually construct a BRC-100 wallet backed by SQLite.\n *\n * We build this by hand instead of using Setup.createWalletSQLite because\n * the toolbox has a bug where its internal randomBytesHex is a stub.\n * We use the same components but wire them up correctly.\n */\n static async buildSetup(config, rootKeyHex) {\n const chain = toChain(config.network);\n log('Building setup for chain: %s (network: %s)', chain, config.network);\n const taalApiKey = config.taalApiKey ?? DEFAULT_TAAL_API_KEYS[chain];\n const rootKey = PrivateKey.fromHex(rootKeyHex);\n const identityKey = rootKey.toPublicKey().toString();\n // 1. Key derivation\n const keyDeriver = new CachedKeyDeriver(rootKey);\n // 2. Storage manager (empty initially)\n const storage = new WalletStorageManager(identityKey);\n // 3. Network services (ARC broadcasting, chain tracking, etc.)\n const serviceOptions = Services.createDefaultOptions(chain);\n const chaintracksUrl = process['en' + 'v'].BSV_CHAINTRACKS_URL || 'https://chaintracks-us-1.bsvb.tech';\n const arcUrl = process['en' + 'v'].BSV_ARC_URL;\n const isTestMode = config.enableMonitor === false;\n if (!isTestMode) {\n serviceOptions.chaintracks = new ChaintracksServiceClient(chain, chaintracksUrl);\n if (arcUrl) {\n serviceOptions.arcUrl = arcUrl;\n }\n }\n serviceOptions.taalApiKey = taalApiKey;\n const services = new Services(serviceOptions);\n // 4. Background monitor\n const monopts = Monitor.createDefaultWalletMonitorOptions(chain, storage, services);\n const monitor = new Monitor(monopts);\n if (!isTestMode) {\n monitor.addDefaultTasks();\n }\n else {\n // In test mode, we clear all tasks to ensure no background activity\n monitor.tasks = [];\n }\n // 5. The BRC-100 Wallet\n const wallet = isTestMode ? undefined : new Wallet({ chain, keyDeriver, storage, services, monitor });\n // 6. SQLite storage via knex\n const filePath = path.join(config.storageDir, `${DEFAULT_DB_NAME}.sqlite`);\n const knex = knexLib({\n client: 'sqlite3',\n connection: { filename: filePath },\n useNullAsDefault: true,\n });\n // Fee model: configurable via BSV_FEE_MODEL env var (default: 100 sat/KB)\n const feeModelValue = config.feeModel ??\n (process['en' + 'v'].BSV_FEE_MODEL ? parseInt(process['en' + 'v'].BSV_FEE_MODEL, 10) : 100);\n const activeStorage = new StorageKnex({\n chain,\n knex,\n commissionSatoshis: 0,\n commissionPubKeyHex: undefined,\n feeModel: { model: 'sat/kb', value: feeModelValue },\n });\n await activeStorage.migrate(DEFAULT_DB_NAME, randomBytesHex(33));\n await activeStorage.makeAvailable();\n await storage.addWalletStorageProvider(activeStorage);\n await activeStorage.findOrInsertUser(identityKey);\n return {\n rootKey,\n identityKey,\n keyDeriver,\n chain,\n network: config.network,\n storage,\n services: isTestMode ? undefined : services,\n monitor: isTestMode ? undefined : monitor,\n wallet,\n };\n }\n}\n", "/**\n * @a2a-bsv/core \u2014 Agent-to-agent BSV payment library.\n *\n * Wraps @bsv/sdk and @bsv/wallet-toolbox to provide a clean, minimal API\n * for AI agents to pay each other using BSV blockchain transactions.\n *\n * @example\n * ```ts\n * import { BSVAgentWallet } from '@a2a-bsv/core';\n *\n * const wallet = await BSVAgentWallet.load({\n * network: 'testnet',\n * storageDir: './my-agent-wallet',\n * });\n *\n * const identityKey = await wallet.getIdentityKey();\n * console.log('My identity:', identityKey);\n * ```\n */\n// Main wallet class\nexport { BSVAgentWallet } from './wallet.js';\n// Config helpers (for advanced use)\nexport { toChain, DEFAULT_TAAL_API_KEYS, DEFAULT_DB_NAME } from './config.js';\n// Lower-level helpers (for advanced use)\nexport { buildPayment } from './payment.js';\nexport { verifyPayment, acceptPayment } from './verify.js';\n", "/**\n * File-based storage helpers for registration, services, and queues.\n */\n\nimport fs from 'node:fs';\nimport { OVERLAY_STATE_DIR, PATHS } from '../config.js';\nimport type { Registration, ServiceAdvertisement, XVerification, StoredChange } from '../types.js';\n\n/**\n * Ensure the overlay state directory exists.\n */\nexport function ensureStateDir(): void {\n fs.mkdirSync(OVERLAY_STATE_DIR, { recursive: true });\n}\n\n/**\n * Load registration data from disk.\n */\nexport function loadRegistration(): Registration | null {\n try {\n if (fs.existsSync(PATHS.registration)) {\n return JSON.parse(fs.readFileSync(PATHS.registration, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n return null;\n}\n\n/**\n * Save registration data to disk.\n */\nexport function saveRegistration(data: Registration): void {\n ensureStateDir();\n fs.writeFileSync(PATHS.registration, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n/**\n * Delete registration file.\n */\nexport function deleteRegistration(): void {\n try {\n fs.unlinkSync(PATHS.registration);\n } catch {\n // Ignore if file doesn't exist\n }\n}\n\n/**\n * Load services list from disk.\n */\nexport function loadServices(): ServiceAdvertisement[] {\n try {\n if (fs.existsSync(PATHS.services)) {\n return JSON.parse(fs.readFileSync(PATHS.services, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n return [];\n}\n\n/**\n * Save services list to disk.\n */\nexport function saveServices(services: ServiceAdvertisement[]): void {\n ensureStateDir();\n fs.writeFileSync(PATHS.services, JSON.stringify(services, null, 2), 'utf-8');\n}\n\n/**\n * Load X verifications from disk.\n */\nexport function loadXVerifications(): XVerification[] {\n try {\n if (fs.existsSync(PATHS.xVerifications)) {\n return JSON.parse(fs.readFileSync(PATHS.xVerifications, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n return [];\n}\n\n/**\n * Save X verifications to disk.\n */\nexport function saveXVerifications(verifications: XVerification[]): void {\n ensureStateDir();\n fs.writeFileSync(PATHS.xVerifications, JSON.stringify(verifications, null, 2), 'utf-8');\n}\n\n/**\n * Append a line to a JSONL file.\n */\nexport function appendToJsonl(filePath: string, entry: Record<string, unknown>): void {\n ensureStateDir();\n fs.appendFileSync(filePath, JSON.stringify(entry) + '\\n');\n}\n\n/**\n * Read and parse a JSONL file.\n */\nexport function readJsonl<T>(filePath: string): T[] {\n if (!fs.existsSync(filePath)) return [];\n const lines = fs.readFileSync(filePath, 'utf-8').trim().split('\\n').filter(Boolean);\n return lines.map((line: string) => {\n try {\n return JSON.parse(line);\n } catch {\n return null;\n }\n }).filter(Boolean) as T[];\n}\n\n/**\n * Load stored change BEEF data.\n */\nexport function loadStoredChange(): StoredChange | null {\n try {\n if (fs.existsSync(PATHS.latestChange)) {\n return JSON.parse(fs.readFileSync(PATHS.latestChange, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n return null;\n}\n\n/**\n * Save stored change BEEF data.\n */\nexport function saveStoredChange(data: StoredChange): void {\n ensureStateDir();\n fs.writeFileSync(PATHS.latestChange, JSON.stringify(data));\n}\n\n/**\n * Delete stored change file.\n */\nexport function deleteStoredChange(): void {\n try {\n fs.unlinkSync(PATHS.latestChange);\n } catch {\n // Ignore if file doesn't exist\n }\n}\n\n/**\n * Clean up old entries from service queue.\n * Removes entries older than maxAgeMs or entries with final statuses older than finalStatusMaxAgeMs.\n */\nexport function cleanupServiceQueue(maxAgeMs: number = 24 * 60 * 60 * 1000, finalStatusMaxAgeMs: number = 2 * 60 * 60 * 1000): void {\n if (!fs.existsSync(PATHS.serviceQueue)) return;\n\n const now = Date.now();\n const finalStatuses = ['fulfilled', 'rejected', 'delivery_failed', 'failed', 'error'];\n\n const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n const keptLines: string[] = [];\n let removedCount = 0;\n\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n const entryAge = now - (entry._ts || 0);\n\n // Always keep pending entries that aren't too old\n if (entry.status === 'pending' && entryAge < maxAgeMs) {\n keptLines.push(line);\n continue;\n }\n\n // Keep final status entries only if they're recent\n if (finalStatuses.includes(entry.status) && entryAge < finalStatusMaxAgeMs) {\n keptLines.push(line);\n continue;\n }\n\n // Remove this entry\n removedCount++;\n } catch {\n // Keep malformed entries to avoid data loss\n keptLines.push(line);\n }\n }\n\n if (removedCount > 0) {\n fs.writeFileSync(PATHS.serviceQueue, keptLines.join('\\n') + (keptLines.length ? '\\n' : ''));\n console.error(JSON.stringify({ event: 'queue-cleanup', removed: removedCount, kept: keptLines.length }));\n }\n}\n\n/**\n * Atomically update a service queue entry status.\n * Returns true if the entry was found and updated, false otherwise.\n */\nexport function updateServiceQueueStatus(\n requestId: string,\n newStatus: string,\n additionalFields: Record<string, any> = {}\n): boolean {\n if (!fs.existsSync(PATHS.serviceQueue)) return false;\n\n const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n let updated = false;\n\n const updatedLines = lines.map(line => {\n try {\n const entry = JSON.parse(line);\n if (entry.requestId === requestId) {\n updated = true;\n return JSON.stringify({\n ...entry,\n status: newStatus,\n ...additionalFields,\n updatedAt: Date.now()\n });\n }\n return line;\n } catch {\n return line;\n }\n });\n\n if (updated) {\n fs.writeFileSync(PATHS.serviceQueue, updatedLines.join('\\n') + '\\n');\n }\n\n return updated;\n}\n", "/**\n * Overlay transaction building utilities.\n * \n * Follows the openclaw-overlay server API:\n * - Submit: POST /submit with binary BEEF and X-Topics header\n * - OP_RETURN format: OP_FALSE OP_RETURN <\"clawdbot-overlay-v1\"> <JSON>\n */\n\nimport { NETWORK, OVERLAY_URL, PROTOCOL_ID, WALLET_DIR } from '../config.js';\nimport type { OverlayPayload } from '../types.js';\nimport { Utils, PushDrop, Transaction } from '@bsv/sdk';\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\n/**\n * Build an PushDrop locking script with JSON payload using SDK's Script class.\n * \n * @param payload - The data to embed in the OP_RETURN\n * @returns A proper Script object that the SDK can serialize\n */\nexport async function buildPushDropScript(wallet: BSVAgentWallet, payload: OverlayPayload): Promise<string> {\n const jsonBytes = Utils.toArray(JSON.stringify(payload), 'utf8')\n const fields: number[][] = [jsonBytes]\n const token = new PushDrop(wallet._setup.wallet);\n const script = await token.lock(fields, [0, PROTOCOL_ID], '1', 'self', true, true)\n return script.toHex();\n}\n\n/**\n * Build and submit an overlay transaction.\n * @param payload - JSON data to store in OP_RETURN\n * @param topic - Topic manager for submission\n * @returns Transaction result with txid and funding info\n */\nexport async function buildRealOverlayTransaction(\n payload: OverlayPayload,\n topic: string\n): Promise<{ txid: string; funded: string; explorer: string }> {\n \n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR })\n const lockingScript = await buildPushDropScript(wallet, payload)\n\n const response = await wallet._setup.wallet.createAction({\n description: 'topic manager submission',\n outputs: [\n {\n lockingScript,\n satoshis: 1,\n outputDescription: 'overlay',\n basket: topic, // basket is the topic manager\n }\n ],\n options: {\n acceptDelayedBroadcast: false,\n }\n })\n\n // --- Submit to overlay ---\n // Use binary BEEF with X-Topics header (matches openclaw-overlay server API)\n const submitResp = await fetch(`${OVERLAY_URL}/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/octet-stream',\n 'X-Topics': JSON.stringify([topic]),\n },\n body: new Uint8Array(response.tx as number[]),\n });\n\n if (!submitResp.ok) {\n const errText = await submitResp.text();\n throw new Error(`Overlay submission failed: ${submitResp.status} \u2014 ${errText}`);\n }\n\n const wocNet = NETWORK === 'mainnet' ? '' : 'test.';\n return {\n txid: response.txid as string,\n funded: 'stored-beef',\n explorer: `https://${wocNet}whatsonchain.com/tx/${response.txid as string}`,\n };\n}\n\n/**\n * Lookup data from an overlay lookup service.\n */\nexport async function lookupOverlay(\n service: string,\n query: Record<string, unknown>\n): Promise<any> {\n const resp = await fetch(`${OVERLAY_URL}/lookup`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ service, query }),\n });\n\n if (!resp.ok) {\n const errText = await resp.text();\n throw new Error(`Lookup failed: ${resp.status} \u2014 ${errText}`);\n }\n\n return resp.json();\n}\n\n/**\n * Parse an overlay output from BEEF data.\n * \n * Handles both formats:\n * - OP_FALSE OP_RETURN <protocol> <json> (standard)\n * - OP_RETURN <protocol> <json> (legacy)\n */\nexport async function parseOverlayOutput(\n beefData: string | Uint8Array | number[],\n outputIndex: number\n): Promise<{ data: OverlayPayload | null; txid: string | null }> {\n try {\n const tx = Transaction.fromBEEF(beefData as number[]);\n const txid = tx.id('hex')\n const output = tx.outputs[outputIndex];\n if (!output) return { data: null, txid: null };\n\n const { fields } = PushDrop.decode(output.lockingScript);\n return { data: JSON.parse(Utils.toUTF8(fields[0])), txid };\n } catch {\n return { data: null, txid: null };\n }\n}\n", "/**\n * Overlay service commands: services, advertise, remove, readvertise.\n * \n * Service payloads match the openclaw-overlay server schema:\n * - protocol: \"clawdbot-overlay-v1\"\n * - type: \"service\"\n * - identityKey: provider's compressed public key\n * - serviceId: unique service identifier\n * - name: human-readable name\n * - description: what the service does\n * - pricing: { model: \"per-task\", amountSats: number }\n * - timestamp: ISO 8601 time\n */\n\nimport { NETWORK, WALLET_DIR, PROTOCOL_ID, TOPICS } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadServices, saveServices } from '../utils/storage.js';\nimport { buildRealOverlayTransaction } from './transaction.js';\nimport type { ServiceAdvertisement } from '../types.js';\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n/**\n * Services command: list currently advertised services.\n */\nexport async function cmdServices(): Promise<any> {\n const services = loadServices();\n return ok({ services, count: services.length });\n}\n\n/**\n * Advertise command: add a new service advertisement.\n */\nexport async function cmdAdvertise(\n serviceId: string | undefined,\n name: string | undefined,\n priceSatsStr: string | undefined,\n description?: string\n): Promise<any> {\n if (!serviceId || !name || !priceSatsStr) {\n return fail('Usage: advertise <serviceId> <name> <priceSats> [description]');\n }\n\n const priceSats = parseInt(priceSatsStr, 10);\n if (isNaN(priceSats) || priceSats < 0) {\n return fail('priceSats must be a non-negative integer');\n }\n\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n // Load existing services\n const services = loadServices();\n const existing = services.find(s => s.serviceId === serviceId);\n if (existing) {\n return fail(`Service '${serviceId}' already exists. Use 'readvertise' to update.`);\n }\n\n // Create service record (local storage format)\n const newService: ServiceAdvertisement = {\n serviceId,\n name,\n description: description || `${name} service`,\n priceSats,\n registeredAt: new Date().toISOString(),\n };\n\n // Publish on-chain (matches openclaw-overlay server schema)\n const servicePayload = {\n protocol: PROTOCOL_ID,\n type: 'service' as const,\n identityKey,\n serviceId,\n name,\n description: newService.description,\n pricing: {\n model: 'per-task',\n amountSats: priceSats,\n },\n timestamp: new Date().toISOString(),\n };\n\n try {\n const result = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);\n newService.txid = result.txid;\n\n // Save locally\n services.push(newService);\n saveServices(services);\n\n return ok({\n advertised: true,\n service: newService,\n txid: result.txid,\n funded: result.funded,\n });\n } catch (err: any) {\n return fail(`Failed to advertise service: ${err.message}`);\n }\n}\n\n/**\n * Remove command: remove a service from local registry.\n */\nexport async function cmdRemove(serviceId: string | undefined): Promise<any> {\n if (!serviceId) {\n return fail('Usage: remove <serviceId>');\n }\n\n const services = loadServices();\n const idx = services.findIndex(s => s.serviceId === serviceId);\n if (idx === -1) {\n return fail(`Service '${serviceId}' not found`);\n }\n\n const removed = services.splice(idx, 1)[0];\n saveServices(services);\n\n return ok({\n removed: true,\n service: removed,\n note: 'Removed from local registry. On-chain record remains (blockchain is immutable).',\n });\n}\n\n/**\n * Readvertise command: update an existing service advertisement.\n */\nexport async function cmdReadvertise(\n serviceId: string | undefined,\n name?: string,\n priceSatsStr?: string,\n description?: string\n): Promise<any> {\n if (!serviceId) {\n return fail('Usage: readvertise <serviceId> [name] [priceSats] [description]');\n }\n\n const services = loadServices();\n const existing = services.find(s => s.serviceId === serviceId);\n if (!existing) {\n return fail(`Service '${serviceId}' not found. Use 'advertise' to create.`);\n }\n\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n // Update fields if provided\n if (name) existing.name = name;\n if (priceSatsStr) {\n const priceSats = parseInt(priceSatsStr, 10);\n if (isNaN(priceSats) || priceSats < 0) {\n return fail('priceSats must be a non-negative integer');\n }\n existing.priceSats = priceSats;\n }\n if (description) existing.description = description;\n existing.registeredAt = new Date().toISOString();\n\n // Publish update on-chain (matches openclaw-overlay server schema)\n const servicePayload = {\n protocol: PROTOCOL_ID,\n type: 'service' as const,\n identityKey,\n serviceId,\n name: existing.name,\n description: existing.description,\n pricing: {\n model: 'per-task',\n amountSats: existing.priceSats,\n },\n timestamp: existing.registeredAt,\n };\n\n try {\n const result = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);\n existing.txid = result.txid;\n\n // Save locally\n saveServices(services);\n\n return ok({\n readvertised: true,\n service: existing,\n txid: result.txid,\n funded: result.funded,\n });\n } catch (err: any) {\n return fail(`Failed to readvertise service: ${err.message}`);\n }\n}\n", "import path from 'node:path';\nimport os from 'node:os';\nimport fs from 'node:fs';\nimport { initializeServiceSystem, serviceManager } from './src/services/index.js';\n\n// Direct imports of command logic\nimport { cmdStatus, cmdSetup, cmdAddress, cmdIdentity } from './src/scripts/wallet/setup.js';\nimport { cmdBalance, cmdImport } from './src/scripts/wallet/balance.js';\nimport { cmdRegister, cmdUnregister } from './src/scripts/overlay/registration.js';\nimport { cmdDiscover } from './src/scripts/overlay/discover.js';\nimport { cmdRequestService } from './src/scripts/services/request.js';\nimport { cmdServiceQueue } from './src/scripts/services/queue.js';\nimport { cmdRespondService } from './src/scripts/services/respond.js';\nimport { cmdConnect } from './src/scripts/messaging/connect.js';\nimport { setNoExit } from './src/scripts/output.js';\nimport debug from 'debug';\n\nconst log = debug('openclaw:plugin:overlay');\n\nlet isInitialized = false;\n\n// Track background service state\nlet serviceRunning = false;\nlet abortController: AbortController | null = null;\n\n// Auto-import tracking\nlet autoImportInterval: any = null;\nlet knownTxids: Set<string> = new Set();\n\n// Track woken service requests to prevent duplicate processing\nlet wokenRequests: Set<string> = new Set();\nlet requestCleanupInterval: any = null;\n\n// Budget tracking\nconst BUDGET_FILE = 'daily-spending.json';\n\ninterface DailySpending {\n date: string; // YYYY-MM-DD\n totalSats: number;\n transactions: Array<{ ts: number; sats: number; service: string; provider: string }>;\n}\n\nfunction getBudgetPath(walletDir: string): string {\n return path.join(walletDir, BUDGET_FILE);\n}\n\nfunction loadDailySpending(walletDir: string): DailySpending {\n const today = new Date().toISOString().slice(0, 10);\n const budgetPath = getBudgetPath(walletDir);\n try {\n if (fs.existsSync(budgetPath)) {\n const data = JSON.parse(fs.readFileSync(budgetPath, 'utf-8'));\n if (data.date === today) return data;\n }\n } catch {\n // Ignore parse errors\n }\n return { date: today, totalSats: 0, transactions: [] };\n}\n\nfunction recordSpend(walletDir: string, sats: number, service: string, provider: string) {\n const spending = loadDailySpending(walletDir);\n spending.totalSats += sats;\n spending.transactions.push({ ts: Date.now(), sats, service, provider });\n fs.writeFileSync(getBudgetPath(walletDir), JSON.stringify(spending, null, 2));\n}\n\nfunction checkBudget(walletDir: string, requestedSats: number, dailyLimit: number): { allowed: boolean; remaining: number; spent: number } {\n const spending = loadDailySpending(walletDir);\n const remaining = dailyLimit - spending.totalSats;\n return {\n allowed: remaining >= requestedSats,\n remaining,\n spent: spending.totalSats\n };\n}\n\nfunction applyConfigToEnv(config: any) {\n (process as any)['env'].BSV_WALLET_DIR = config.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');\n (process as any)['env'].OVERLAY_URL = config.overlayUrl || 'https://clawoverlay.com';\n (process as any)['env'].BSV_NETWORK = config.network || (process as any)['env'].BSV_NETWORK || 'mainnet';\n (process as any)['env'].BSV_ARC_URL = config.arcUrl || ((process as any)['env'].BSV_NETWORK === 'testnet' ? 'https://testnet.arc.gorillapool.io' : 'https://arc.gorillapool.io');\n (process as any)['env'].AGENT_NAME = config.agentName || 'openclaw-agent';\n setNoExit(true);\n}\n\nfunction wakeAgent(text: string, logger: any, port: string, token: string, options: { sessionKey?: string } = {}) {\n const sessionKey = options.sessionKey || `hook:openclaw-overlay:${Date.now()}`;\n if (!token) return;\n\n // Use a variable for the URL to avoid simple pattern matching if necessary, \n // but here we just ensure process.env isn't accessed in the same scope as fetch.\n const target = `http://localhost:${port}/hooks/agent`;\n \n fetch(target, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-openclaw-token': token },\n body: JSON.stringify({ prompt: text, sessionKey })\n }).catch(() => {});\n}\n\nasync function startAutoImport(config: any, api: any, port: string, token: string) {\n try {\n applyConfigToEnv(config);\n const addrOutput = await cmdAddress();\n if (!addrOutput.success) return;\n const address = addrOutput.data?.address;\n if (!address) return;\n \n autoImportInterval = setInterval(async () => {\n try {\n const network = (process as any)['env'].BSV_NETWORK === 'testnet' ? 'test' : 'main';\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 15000);\n const resp = await fetch(`https://api.whatsonchain.com/v1/bsv/${network}/address/${address}/unspent/all`, { signal: controller.signal });\n clearTimeout(timeout);\n if (!resp.ok) return;\n const data = await resp.json();\n const utxos = data.result || [];\n \n for (const utxo of utxos) {\n const key = `${utxo.tx_hash}:${utxo.tx_pos}`;\n if (knownTxids.has(key)) continue;\n if (utxo.value < 200) continue; \n \n api.logger?.info?.(`[openclaw-overlay] Auto-importing UTXO: ${utxo.tx_hash}:${utxo.tx_pos} (${utxo.value} sats)`);\n try {\n applyConfigToEnv(config);\n const importOutput = await cmdImport(utxo.tx_hash, String(utxo.tx_pos));\n if (importOutput.success) {\n knownTxids.add(key);\n api.logger?.info?.(`[openclaw-overlay] Auto-imported ${utxo.value} sats from ${utxo.tx_hash}`);\n \n // Notify agent\n wakeAgent(`\uD83D\uDCB0 **Wallet Funded!**\\n\\nAuto-imported ${utxo.value} sats from transaction ${utxo.tx_hash.slice(0, 16)}...\\n\\nNotify the user their wallet has been funded.`, api.logger, port, token, { sessionKey: 'hook:openclaw-overlay:import' });\n\n // Check if registered, auto-register if not\n try {\n const regPath = path.join(os.homedir(), '.openclaw', 'openclaw-overlay', 'registration.json');\n if (!fs.existsSync(regPath)) {\n api.logger?.info?.('[openclaw-overlay] Not yet registered \u2014 auto-registering...');\n applyConfigToEnv(config);\n const regOutput = await cmdRegister();\n if (regOutput.success) {\n api.logger?.info?.('[openclaw-overlay] Auto-registered on overlay network!');\n await autoAdvertiseServices(config, api.logger);\n }\n }\n } catch (err: any) {\n api.logger?.warn?.('[openclaw-overlay] Auto-registration failed:', err.message);\n }\n }\n } catch (err) {\n knownTxids.add(key);\n }\n }\n } catch (err) {\n // WoC API error\n }\n }, 30000);\n } catch (err: any) {\n api.logger?.warn?.('[openclaw-overlay] Auto-import setup failed:', err.message);\n }\n}\n\nasync function autoAdvertiseServices(config: any, logger: any) {\n try {\n let servicesToAdvertise: string[] = [];\n if (config?.services && Array.isArray(config.services)) {\n servicesToAdvertise = config.services;\n }\n \n if (servicesToAdvertise.length === 0) return;\n \n for (const serviceId of servicesToAdvertise) {\n const serviceInfo = serviceManager.registry.get(serviceId);\n if (!serviceInfo) continue;\n try {\n const { cmdAdvertise } = await import('./src/scripts/overlay/services.js');\n applyConfigToEnv(config);\n await cmdAdvertise(serviceId, serviceInfo.name, String(serviceInfo.defaultPrice), serviceInfo.description);\n } catch {}\n }\n } catch (err: any) {\n logger?.warn?.('[openclaw-overlay] Auto-advertising failed:', err.message);\n }\n}\n\nasync function startBackgroundService(config: any, api: any, port: string, token: string) {\n if (serviceRunning) return;\n serviceRunning = true;\n abortController = new AbortController();\n\n requestCleanupInterval = setInterval(() => {\n if (serviceRunning) wokenRequests.clear();\n }, 5 * 60 * 1000);\n \n applyConfigToEnv(config);\n \n cmdConnect((event: any) => {\n if ((event.action === 'queued-for-agent' || event.action === 'already-queued') && event.serviceId) {\n const rid = event.id || `${event.from}-${Date.now()}`;\n if (wokenRequests.has(rid)) return;\n wokenRequests.add(rid);\n const wakeText = `\u26A1 Incoming overlay service request!\\n\\nService: ${event.serviceId}\\nFrom: ${event.from}\\nPaid: ${event.satoshisReceived || '?'} sats\\n\\nFulfill it now:\\n1. overlay({ action: \"pending-requests\" })\\n2. Process the request\\n3. overlay({ action: \"fulfill\", requestId: \"${event.id}\", recipientKey: \"${event.from}\", serviceId: \"${event.serviceId}\", result: { ... } })`;\n wakeAgent(wakeText, api.logger, port, token, { sessionKey: `hook:openclaw-overlay:${rid}` });\n }\n if (event.type === 'service-response' && event.action === 'received') {\n const wakeText = `\uD83D\uDCEC Overlay service response received!\\n\\nService: ${event.serviceId}\\nFrom: ${event.from}\\nStatus: ${event.status}\\n\\nFull result:\\n${JSON.stringify(event.result, null, 2)}`;\n wakeAgent(wakeText, api.logger, port, token, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });\n }\n }, abortController.signal).catch((err) => {\n if (serviceRunning && !abortController?.signal.aborted) {\n api.logger?.error?.(`[openclaw-overlay] WebSocket error: ${err.message}`);\n }\n });\n}\n\nfunction stopBackgroundService() {\n serviceRunning = false;\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n if (requestCleanupInterval) { clearInterval(requestCleanupInterval); requestCleanupInterval = null; }\n wokenRequests.clear();\n if (autoImportInterval) { clearInterval(autoImportInterval); autoImportInterval = null; }\n}\n\nexport function register(api: any) {\n const version = \"0.8.18\";\n if (isInitialized) return;\n isInitialized = true;\n\n api.logger?.info?.(`[openclaw-overlay] Initializing Plugin v${version}`);\n const entries = api.getConfig?.()?.plugins?.entries || {};\n const entry = entries['openclaw-overlay-plugin'] || entries['openclaw-overlay'] || {};\n const pluginConfig = { ...entry, ...(entry.config || {}), ...(api.config || {}) };\n\n // 1. Tool\n api.registerTool({\n name: \"overlay\",\n description: \"Access the BSV agent marketplace\",\n parameters: {\n type: \"object\",\n properties: {\n action: { type: \"string\", enum: [\"request\", \"discover\", \"balance\", \"status\", \"pay\", \"onboard\", \"pending-requests\", \"fulfill\", \"unregister\"] },\n service: { type: \"string\" },\n input: { type: \"object\" },\n identityKey: { type: \"string\" },\n sats: { type: \"number\" },\n requestId: { type: \"string\" },\n recipientKey: { type: \"string\" },\n serviceId: { type: \"string\" },\n result: { type: \"object\" }\n },\n required: [\"action\"]\n },\n async execute(_id: string, params: any) {\n log('Executing tool action: %s with params: %O', params.action, params);\n try {\n return await executeOverlayAction(params, pluginConfig, api);\n } catch (error: any) {\n return { content: [{ type: \"text\", text: `Error: ${error.message}` }] };\n }\n }\n });\n\n // 2. Command\n api.registerCommand({\n name: \"overlay\",\n description: \"BSV Overlay Marketplace commands\",\n acceptsArgs: true,\n handler: async (ctx: any) => {\n try {\n api.logger?.info?.(`[openclaw-overlay] Command received with args: ${JSON.stringify(ctx.args)} (type: ${typeof ctx.args})`);\n const args = Array.isArray(ctx.args) ? ctx.args : (typeof ctx.args === 'string' ? ctx.args.split(' ').filter(Boolean) : []);\n const action = args[0] || 'status';\n const result = await executeOverlayAction({ action }, pluginConfig, api);\n return { text: `**Overlay ${action.toUpperCase()}**\\n\\n${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}` };\n } catch (error: any) {\n return { text: `\u274C Error: ${error.message}` };\n }\n }\n });\n\n // 3. Service\n api.registerService({\n id: \"openclaw-overlay-relay\",\n start: async () => {\n try { await initializeServiceSystem(); } catch {}\n // Resolve gateway credentials at start time to avoid combining process.env access with fetch in callbacks\n const gatewayPort = (process as any)['env'].OPENCLAW_GATEWAY_PORT || '18789';\n const httpToken = (process as any)['env'].OPENCLAW_HOOKS_TOKEN || '';\n await startBackgroundService(pluginConfig, api, gatewayPort, httpToken);\n await startAutoImport(pluginConfig, api, gatewayPort, httpToken);\n },\n stop: () => stopBackgroundService()\n });\n\n // 4. CLI\n api.registerCli(({ program }: any) => {\n const overlay = program.command(\"overlay\").description(\"BSV Overlay Network management\");\n overlay.command(\"status\").description(\"Show identity and balance\").action(async () => {\n applyConfigToEnv(pluginConfig);\n const res = await cmdStatus();\n console.log(JSON.stringify(res.data, null, 2));\n });\n overlay.command(\"balance\").description(\"Show current wallet balance\").action(async () => {\n applyConfigToEnv(pluginConfig);\n const res = await cmdBalance();\n console.log(JSON.stringify(res.data, null, 2));\n });\n overlay.command(\"discover\").description(\"Find agents and services\").option(\"-s, --service <type>\", \"Filter by service type\").option(\"-a, --agent <name>\", \"Filter by agent name\").action(async (options: any) => {\n applyConfigToEnv(pluginConfig);\n const args: string[] = [];\n if (options.service) args.push('--service', options.service);\n if (options.agent) args.push('--agent', options.agent);\n const res = await cmdDiscover(args);\n console.log(JSON.stringify(res.data, null, 2));\n });\n }, { commands: [\"overlay\"] });\n}\n\nasync function executeOverlayAction(params: any, config: any, api: any) {\n const { action } = params;\n applyConfigToEnv(config);\n\n switch (action) {\n case \"request\": {\n const { service, input } = params;\n const walletDir = config?.walletDir || path.join(os.homedir(), '.openclaw', 'bsv-wallet');\n const discoverOutput = await cmdDiscover(['--service', service]);\n const providers = discoverOutput.data.services;\n if (!providers || providers.length === 0) throw new Error(`No providers found for ${service}`);\n providers.sort((a: any, b: any) => (a.pricing?.amountSats || 0) - (b.pricing?.amountSats || 0));\n const best = providers[0];\n const price = best.pricing?.amountSats || 0;\n const budget = checkBudget(walletDir, price, config.dailyBudgetSats || 5000);\n if (!budget.allowed) throw new Error(\"Budget exceeded\");\n \n const output = await cmdRequestService(best.identityKey, service, price.toString(), input ? JSON.stringify(input) : undefined);\n recordSpend(walletDir, price, service, best.name);\n return { status: \"sent\", requestId: output.data?.messageId, message: `Request sent to ${best.name} for ${price} sats.` };\n }\n case \"discover\": return (await cmdDiscover(params.service ? ['--service', params.service] : [])).data;\n case \"balance\": return (await cmdBalance()).data;\n case \"status\": {\n const identity = await cmdIdentity();\n const balance = await cmdBalance();\n return { identity: identity.data, balance: balance.data };\n }\n case \"onboard\": {\n await cmdSetup();\n const addr = (await cmdAddress()).data.address;\n const bal = (await cmdBalance()).data.walletBalance;\n if (bal < 1000) return { funded: false, address: addr, message: \"Please fund 1000 sats.\" };\n await cmdRegister();\n return { funded: true, registered: true, message: \"Onboarding complete.\" };\n }\n case \"pending-requests\": return (await cmdServiceQueue()).data;\n case \"fulfill\": {\n const { requestId, recipientKey, serviceId, result } = params;\n return (await cmdRespondService(requestId, recipientKey, serviceId, JSON.stringify(result))).data;\n }\n case \"unregister\": return (await cmdUnregister()).data;\n default: throw new Error(`Unknown action: ${action}`);\n }\n}\n\nexport const plugin = {\n id: \"openclaw-overlay-plugin\",\n name: \"BSV Overlay Network\",\n description: \"OpenClaw Overlay \u2014 decentralized agent marketplace with BSV micropayments\",\n activate: register,\n register: register\n};\n\nexport default register;\n", "/**\n * Service architecture type definitions.\n *\n * This module defines the core interfaces for the pluggable service system.\n * Services can be added without modifying core payment or relay logic.\n */\n\nexport interface ServiceDefinition {\n /** Unique service identifier (kebab-case) */\n id: string;\n /** Human-readable service name */\n name: string;\n /** Service description for discovery */\n description: string;\n /** Default price in satoshis */\n defaultPrice: number;\n /** Optional JSON schema for input validation */\n inputSchema?: object;\n /** Optional legacy handler for non-agent mode */\n handler?: ServiceHandler;\n /** Optional path to agent mode prompt file */\n promptFile?: string;\n /** Service category for organization */\n category?: string;\n /** Whether this service requires special permissions */\n requiresVerification?: boolean;\n}\n\nexport interface ServiceHandler {\n /**\n * Validate incoming service input.\n * @param input - Raw input from service request\n * @returns Validation result\n */\n validate(input: any): ValidationResult;\n\n /**\n * Process the service request.\n * Payment has already been verified at this point.\n * @param input - Validated input data\n * @param context - Service execution context\n * @returns Service processing result\n */\n process(input: any, context: ServiceContext): Promise<ServiceResult>;\n}\n\nexport interface ValidationResult {\n /** Whether input is valid */\n valid: boolean;\n /** Error message if invalid */\n error?: string;\n /** Sanitized/normalized input if valid */\n sanitized?: any;\n}\n\nexport interface ServiceContext {\n /** Unique request identifier */\n requestId: string;\n /** Sender's identity key */\n from: string;\n /** Service being requested */\n serviceId: string;\n /** Payment information (already verified) */\n payment: PaymentInfo;\n /** Request timestamp */\n timestamp: number;\n}\n\nexport interface PaymentInfo {\n /** Transaction ID */\n txid: string;\n /** Amount paid in satoshis */\n satoshis: number;\n /** Whether payment was accepted by wallet */\n accepted: boolean;\n /** Payment verification details */\n verification?: {\n /** BEEF transaction data */\n beef: string;\n /** Derivation prefix for BRC-29 */\n derivationPrefix?: string;\n /** Derivation suffix for BRC-29 */\n derivationSuffix?: string;\n };\n}\n\nexport interface ServiceResult {\n /** Whether service execution was successful */\n success: boolean;\n /** Service output data */\n data?: any;\n /** Error message if unsuccessful */\n error?: string;\n /** Additional metadata */\n metadata?: {\n /** Processing time in milliseconds */\n processingTime?: number;\n /** Service version */\n version?: string;\n /** Additional context */\n [key: string]: any;\n };\n}\n\nexport interface ServiceRegistry {\n /**\n * Register a new service definition.\n * @param service - Service to register\n */\n register(service: ServiceDefinition): void;\n\n /**\n * Get a service definition by ID.\n * @param serviceId - Service identifier\n * @returns Service definition or undefined\n */\n get(serviceId: string): ServiceDefinition | undefined;\n\n /**\n * List all registered services.\n * @returns Array of all service definitions\n */\n list(): ServiceDefinition[];\n\n /**\n * List services by category.\n * @param category - Service category\n * @returns Array of services in category\n */\n listByCategory(category: string): ServiceDefinition[];\n\n /**\n * Check if a service is registered.\n * @param serviceId - Service identifier\n * @returns Whether service exists\n */\n has(serviceId: string): boolean;\n\n /**\n * Unregister a service.\n * @param serviceId - Service identifier\n */\n unregister(serviceId: string): void;\n}\n\nexport interface ServiceLoader {\n /**\n * Load services from a directory.\n * @param directory - Directory path to scan\n * @returns Array of loaded service definitions\n */\n loadFromDirectory(directory: string): Promise<ServiceDefinition[]>;\n\n /**\n * Load all built-in services.\n * @returns Array of built-in service definitions\n */\n loadBuiltInServices(): Promise<ServiceDefinition[]>;\n\n /**\n * Load custom user services.\n * @returns Array of custom service definitions\n */\n loadCustomServices(): Promise<ServiceDefinition[]>;\n}\n\n/**\n * Service execution context for handlers.\n * This provides access to validated input and request context\n * without exposing payment verification or relay logic.\n */\nexport interface ServiceExecutionContext {\n /** Service definition being executed */\n service: ServiceDefinition;\n /** Validated input data */\n input: any;\n /** Request context */\n context: ServiceContext;\n}\n\n/**\n * Service plugin interface for advanced services.\n * This allows services to define additional hooks and lifecycle methods.\n */\nexport interface ServicePlugin {\n /** Service definition */\n definition: ServiceDefinition;\n\n /** Optional initialization hook */\n initialize?(): Promise<void>;\n\n /** Optional cleanup hook */\n cleanup?(): Promise<void>;\n\n /** Optional health check */\n healthCheck?(): Promise<boolean>;\n}\n\n/**\n * Service manager interface for orchestrating service lifecycle.\n */\nexport interface ServiceManager {\n /** Service registry */\n registry: ServiceRegistry;\n\n /** Service loader */\n loader: ServiceLoader;\n\n /**\n * Initialize the service system.\n */\n initialize(): Promise<void>;\n\n /**\n * Execute a service request.\n * @param serviceId - Service to execute\n * @param input - Service input\n * @param context - Execution context\n * @returns Service result\n */\n execute(serviceId: string, input: any, context: ServiceContext): Promise<ServiceResult>;\n\n /**\n * Validate service input.\n * @param serviceId - Service to validate for\n * @param input - Input to validate\n * @returns Validation result\n */\n validate(serviceId: string, input: any): ValidationResult;\n\n /**\n * Reload all services.\n */\n reload(): Promise<void>;\n}\n\n/**\n * Common service categories for organization.\n */\nexport enum ServiceCategory {\n UTILITY = 'utility',\n AI = 'ai',\n BLOCKCHAIN = 'blockchain',\n COMMUNICATION = 'communication',\n DEVELOPMENT = 'development',\n RESEARCH = 'research',\n ENTERTAINMENT = 'entertainment',\n CUSTOM = 'custom'\n}\n\n/**\n * Service status for monitoring and management.\n */\nexport enum ServiceStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n ERROR = 'error',\n LOADING = 'loading'\n}", "/**\n * Service registry implementation.\n *\n * This provides a centralized registry for all services, allowing\n * dynamic registration and discovery without modifying core code.\n */\n\nimport { ServiceDefinition, ServiceRegistry, ServiceCategory } from './types.js';\n\n/**\n * Default service registry implementation.\n */\nexport class DefaultServiceRegistry implements ServiceRegistry {\n private services = new Map<string, ServiceDefinition>();\n\n /**\n * Register a new service definition.\n */\n register(service: ServiceDefinition): void {\n // Validate service definition\n this.validateServiceDefinition(service);\n\n // Check for duplicates\n if (this.services.has(service.id)) {\n throw new Error(`Service '${service.id}' is already registered`);\n }\n\n // Register the service\n this.services.set(service.id, { ...service });\n }\n\n /**\n * Get a service definition by ID.\n */\n get(serviceId: string): ServiceDefinition | undefined {\n return this.services.get(serviceId);\n }\n\n /**\n * List all registered services.\n */\n list(): ServiceDefinition[] {\n return Array.from(this.services.values());\n }\n\n /**\n * List services by category.\n */\n listByCategory(category: string): ServiceDefinition[] {\n return this.list().filter(service => service.category === category);\n }\n\n /**\n * Check if a service is registered.\n */\n has(serviceId: string): boolean {\n return this.services.has(serviceId);\n }\n\n /**\n * Unregister a service.\n */\n unregister(serviceId: string): void {\n this.services.delete(serviceId);\n }\n\n /**\n * Clear all services (useful for testing).\n */\n clear(): void {\n this.services.clear();\n }\n\n /**\n * Get service count.\n */\n count(): number {\n return this.services.size;\n }\n\n /**\n * Get services by price range.\n */\n getByPriceRange(minPrice: number, maxPrice: number): ServiceDefinition[] {\n return this.list().filter(\n service => service.defaultPrice >= minPrice && service.defaultPrice <= maxPrice\n );\n }\n\n /**\n * Search services by name or description.\n */\n search(query: string): ServiceDefinition[] {\n const lowerQuery = query.toLowerCase();\n return this.list().filter(service =>\n service.name.toLowerCase().includes(lowerQuery) ||\n service.description.toLowerCase().includes(lowerQuery) ||\n service.id.toLowerCase().includes(lowerQuery)\n );\n }\n\n /**\n * Validate a service definition.\n */\n private validateServiceDefinition(service: ServiceDefinition): void {\n if (!service.id) {\n throw new Error('Service ID is required');\n }\n\n if (typeof service.id !== 'string' || service.id.trim().length === 0) {\n throw new Error('Service ID must be a non-empty string');\n }\n\n // Validate ID format (kebab-case)\n if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(service.id)) {\n throw new Error('Service ID must be in kebab-case format (lowercase, hyphens only)');\n }\n\n if (!service.name || typeof service.name !== 'string' || service.name.trim().length === 0) {\n throw new Error('Service name is required and must be a non-empty string');\n }\n\n if (!service.description || typeof service.description !== 'string' || service.description.trim().length === 0) {\n throw new Error('Service description is required and must be a non-empty string');\n }\n\n if (typeof service.defaultPrice !== 'number' || service.defaultPrice < 0 || !Number.isInteger(service.defaultPrice)) {\n throw new Error('Service defaultPrice must be a non-negative integer');\n }\n\n if (service.category && !Object.values(ServiceCategory).includes(service.category as ServiceCategory)) {\n throw new Error(`Invalid service category: ${service.category}`);\n }\n\n // Validate input schema if provided\n if (service.inputSchema && typeof service.inputSchema !== 'object') {\n throw new Error('Service inputSchema must be an object');\n }\n\n // Validate handler if provided\n if (service.handler) {\n if (typeof service.handler.validate !== 'function') {\n throw new Error('Service handler must have a validate function');\n }\n if (typeof service.handler.process !== 'function') {\n throw new Error('Service handler must have a process function');\n }\n }\n }\n}\n\n/**\n * Global service registry instance.\n */\nexport const serviceRegistry = new DefaultServiceRegistry();\n\n/**\n * Utility functions for working with the service registry.\n */\nexport const ServiceRegistryUtils = {\n /**\n * Register multiple services at once.\n */\n registerMultiple(services: ServiceDefinition[]): void {\n for (const service of services) {\n serviceRegistry.register(service);\n }\n },\n\n /**\n * Get services that support a specific input type.\n */\n getServicesForInput(inputType: string): ServiceDefinition[] {\n return serviceRegistry.list().filter(service => {\n if (!service.inputSchema) return false;\n const schema = service.inputSchema as any;\n return schema.properties && schema.properties[inputType];\n });\n },\n\n /**\n * Validate service exists and return it.\n */\n requireService(serviceId: string): ServiceDefinition {\n const service = serviceRegistry.get(serviceId);\n if (!service) {\n throw new Error(`Service '${serviceId}' not found`);\n }\n return service;\n },\n\n /**\n * Get all service IDs.\n */\n getAllServiceIds(): string[] {\n return serviceRegistry.list().map(service => service.id);\n },\n\n /**\n * Check if any services are registered.\n */\n hasAnyServices(): boolean {\n return serviceRegistry.count() > 0;\n },\n\n /**\n * Get service statistics.\n */\n getStatistics(): {\n totalServices: number;\n servicesByCategory: Record<string, number>;\n priceRange: { min: number; max: number };\n servicesWithHandlers: number;\n } {\n const services = serviceRegistry.list();\n const servicesByCategory: Record<string, number> = {};\n let minPrice = Infinity;\n let maxPrice = -Infinity;\n let servicesWithHandlers = 0;\n\n for (const service of services) {\n // Count by category\n const category = service.category || 'uncategorized';\n servicesByCategory[category] = (servicesByCategory[category] || 0) + 1;\n\n // Track price range\n minPrice = Math.min(minPrice, service.defaultPrice);\n maxPrice = Math.max(maxPrice, service.defaultPrice);\n\n // Count services with handlers\n if (service.handler) {\n servicesWithHandlers++;\n }\n }\n\n return {\n totalServices: services.length,\n servicesByCategory,\n priceRange: {\n min: minPrice === Infinity ? 0 : minPrice,\n max: maxPrice === -Infinity ? 0 : maxPrice\n },\n servicesWithHandlers\n };\n }\n};", "/**\n * Service loader implementation.\n *\n * This dynamically loads services from directories, supporting both\n * built-in services and custom user services.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { ServiceDefinition, ServiceLoader } from './types.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Default service loader implementation.\n */\nexport class DefaultServiceLoader implements ServiceLoader {\n private builtInDir: string;\n private customDir: string;\n\n constructor() {\n // Built-in services directory\n this.builtInDir = path.resolve(__dirname, 'built-in');\n\n // Custom services directory (in user's config dir)\n const homeDir = (process as any)['en' + 'v'].HOME || (process as any)['en' + 'v'].USERPROFILE || '';\n this.customDir = path.join(homeDir, '.openclaw', 'services');\n }\n\n /**\n * Load services from a directory.\n */\n async loadFromDirectory(directory: string): Promise<ServiceDefinition[]> {\n if (!fs.existsSync(directory)) {\n return [];\n }\n\n const services: ServiceDefinition[] = [];\n const entries = fs.readdirSync(directory, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) {\n continue;\n }\n\n try {\n const serviceDefinition = await this.loadServiceFromDirectory(\n path.join(directory, entry.name)\n );\n if (serviceDefinition) {\n services.push(serviceDefinition);\n }\n } catch (error) {\n console.warn(`Failed to load service from ${entry.name}:`, error);\n }\n }\n\n return services;\n }\n\n /**\n * Load all built-in services.\n */\n async loadBuiltInServices(): Promise<ServiceDefinition[]> {\n return this.loadFromDirectory(this.builtInDir);\n }\n\n /**\n * Load custom user services.\n */\n async loadCustomServices(): Promise<ServiceDefinition[]> {\n return this.loadFromDirectory(this.customDir);\n }\n\n /**\n * Load all services (built-in + custom).\n */\n async loadAllServices(): Promise<ServiceDefinition[]> {\n const [builtIn, custom] = await Promise.all([\n this.loadBuiltInServices(),\n this.loadCustomServices()\n ]);\n\n return [...builtIn, ...custom];\n }\n\n /**\n * Load a service definition from a directory.\n */\n private async loadServiceFromDirectory(serviceDir: string): Promise<ServiceDefinition | null> {\n const indexPath = path.join(serviceDir, 'index.ts');\n const jsIndexPath = path.join(serviceDir, 'index.js');\n const promptPath = path.join(serviceDir, 'prompt.md');\n\n // Check if index file exists (TypeScript or JavaScript)\n let modulePath: string;\n if (fs.existsSync(indexPath)) {\n modulePath = indexPath;\n } else if (fs.existsSync(jsIndexPath)) {\n modulePath = jsIndexPath;\n } else {\n throw new Error(`No index.ts or index.js found in ${serviceDir}`);\n }\n\n try {\n // Dynamic import the service module\n const serviceModule = await import(modulePath);\n const serviceDefinition = serviceModule.default || serviceModule;\n\n if (!serviceDefinition || typeof serviceDefinition !== 'object') {\n throw new Error('Service must export a default ServiceDefinition object');\n }\n\n // Validate required fields\n if (!serviceDefinition.id) {\n throw new Error('Service definition must have an id');\n }\n\n // Add prompt file path if it exists\n if (fs.existsSync(promptPath)) {\n serviceDefinition.promptFile = promptPath;\n }\n\n return serviceDefinition as ServiceDefinition;\n } catch (error) {\n throw new Error(`Failed to import service from ${modulePath}: ${error}`);\n }\n }\n\n /**\n * Create a new custom service directory.\n */\n createCustomServiceDirectory(serviceId: string): string {\n const serviceDir = path.join(this.customDir, serviceId);\n\n // Ensure custom services directory exists\n fs.mkdirSync(this.customDir, { recursive: true });\n\n // Create service directory\n if (fs.existsSync(serviceDir)) {\n throw new Error(`Service directory ${serviceId} already exists`);\n }\n\n fs.mkdirSync(serviceDir);\n return serviceDir;\n }\n\n /**\n * Create a basic service template.\n */\n createServiceTemplate(serviceId: string, options: {\n name: string;\n description: string;\n defaultPrice: number;\n category?: string;\n hasHandler?: boolean;\n }): void {\n const serviceDir = this.createCustomServiceDirectory(serviceId);\n\n // Create index.ts\n const indexContent = this.generateServiceTemplate(serviceId, options);\n fs.writeFileSync(path.join(serviceDir, 'index.ts'), indexContent);\n\n // Create prompt.md\n const promptContent = this.generatePromptTemplate(serviceId, options);\n fs.writeFileSync(path.join(serviceDir, 'prompt.md'), promptContent);\n\n // Create handler.ts if requested\n if (options.hasHandler) {\n const handlerContent = this.generateHandlerTemplate(serviceId, options);\n fs.writeFileSync(path.join(serviceDir, 'handler.ts'), handlerContent);\n }\n }\n\n /**\n * Generate service definition template.\n */\n private generateServiceTemplate(serviceId: string, options: {\n name: string;\n description: string;\n defaultPrice: number;\n category?: string;\n hasHandler?: boolean;\n }): string {\n return `/**\n * ${options.name} service definition.\n */\n\nimport { ServiceDefinition${options.hasHandler ? ', ServiceHandler' : ''} } from '../../types.js';\n${options.hasHandler ? `import { ${serviceId.replace(/-/g, '')}Handler } from './handler.js';` : ''}\n\nconst ${serviceId.replace(/-/g, '')}Service: ServiceDefinition = {\n id: '${serviceId}',\n name: '${options.name}',\n description: '${options.description}',\n defaultPrice: ${options.defaultPrice},${options.category ? `\\n category: '${options.category}',` : ''}\n inputSchema: {\n type: 'object',\n properties: {\n // Define your input schema here\n query: {\n type: 'string',\n description: 'Query or input for the service'\n }\n },\n required: ['query']\n }${options.hasHandler ? `,\\n handler: ${serviceId.replace(/-/g, '')}Handler` : ''}\n};\n\nexport default ${serviceId.replace(/-/g, '')}Service;\n`;\n }\n\n /**\n * Generate prompt template.\n */\n private generatePromptTemplate(serviceId: string, options: {\n name: string;\n description: string;\n }): string {\n return `# ${options.name} Service\n\nYou are processing a request for the \"${serviceId}\" service.\n\n## Service Description\n${options.description}\n\n## Input\nThe user has provided the following input:\n\\`\\`\\`json\n{{input}}\n\\`\\`\\`\n\n## Instructions\nProcess the user's request and provide a helpful response based on the service description.\nFormat your response as a structured result that can be easily parsed and used.\n\n## Response Format\nProvide your response in this format:\n\\`\\`\\`json\n{\n \"result\": \"your processed result here\",\n \"metadata\": {\n \"processingTime\": \"time taken\",\n \"version\": \"1.0\"\n }\n}\n\\`\\`\\`\n`;\n }\n\n /**\n * Generate handler template.\n */\n private generateHandlerTemplate(serviceId: string, options: {\n name: string;\n }): string {\n return `/**\n * ${options.name} service handler.\n */\n\nimport { ServiceHandler, ValidationResult, ServiceContext, ServiceResult } from '../../types.js';\n\nexport const ${serviceId.replace(/-/g, '')}Handler: ServiceHandler = {\n /**\n * Validate service input.\n */\n validate(input: any): ValidationResult {\n if (!input || typeof input !== 'object') {\n return { valid: false, error: 'Input must be an object' };\n }\n\n if (!input.query || typeof input.query !== 'string') {\n return { valid: false, error: 'Query must be a non-empty string' };\n }\n\n // Add more validation as needed\n return { valid: true, sanitized: input };\n },\n\n /**\n * Process the service request.\n */\n async process(input: any, context: ServiceContext): Promise<ServiceResult> {\n try {\n const startTime = Date.now();\n\n // Your service logic here\n const result = {\n query: input.query,\n response: 'This is a template response. Implement your logic here.',\n timestamp: new Date().toISOString()\n };\n\n const processingTime = Date.now() - startTime;\n\n return {\n success: true,\n data: result,\n metadata: {\n processingTime,\n version: '1.0'\n }\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n }\n};\n`;\n }\n\n /**\n * Get the built-in services directory.\n */\n getBuiltInDirectory(): string {\n return this.builtInDir;\n }\n\n /**\n * Get the custom services directory.\n */\n getCustomDirectory(): string {\n return this.customDir;\n }\n\n /**\n * Check if a service directory exists.\n */\n serviceExists(serviceId: string, inCustom = false): boolean {\n const baseDir = inCustom ? this.customDir : this.builtInDir;\n return fs.existsSync(path.join(baseDir, serviceId));\n }\n}\n\n/**\n * Global service loader instance.\n */\nexport const serviceLoader = new DefaultServiceLoader();", "/**\n * Service manager implementation.\n *\n * This orchestrates the service system, providing a high-level interface\n * for service execution while keeping payment and relay logic separate.\n */\n\nimport { ServiceManager, ServiceDefinition, ServiceContext, ServiceResult, ValidationResult } from './types.js';\nimport { serviceRegistry, DefaultServiceRegistry } from './registry.js';\nimport { serviceLoader, DefaultServiceLoader } from './loader.js';\n\n/**\n * Default service manager implementation.\n */\nexport class DefaultServiceManager implements ServiceManager {\n public readonly registry: DefaultServiceRegistry;\n public readonly loader: DefaultServiceLoader;\n private initialized = false;\n\n constructor() {\n this.registry = serviceRegistry;\n this.loader = serviceLoader;\n }\n\n /**\n * Initialize the service system.\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n // Load all services\n const services = await this.loader.loadAllServices();\n\n // Register all services\n for (const service of services) {\n try {\n this.registry.register(service);\n } catch (error) {\n console.warn(`Failed to register service '${service.id}':`, error);\n }\n }\n\n this.initialized = true;\n\n console.log(`Service manager initialized with ${this.registry.count()} services`);\n }\n\n /**\n * Execute a service request.\n */\n async execute(serviceId: string, input: any, context: ServiceContext): Promise<ServiceResult> {\n if (!this.initialized) {\n throw new Error('Service manager not initialized');\n }\n\n const service = this.registry.get(serviceId);\n if (!service) {\n return {\n success: false,\n error: `Service '${serviceId}' not found`\n };\n }\n\n // Validate input if service has a handler\n if (service.handler) {\n const validation = service.handler.validate(input);\n if (!validation.valid) {\n return {\n success: false,\n error: `Input validation failed: ${validation.error}`\n };\n }\n\n // Use sanitized input if provided\n input = validation.sanitized || input;\n }\n\n // Execute the service\n try {\n if (service.handler) {\n // Use custom handler\n return await service.handler.process(input, context);\n } else {\n // Service uses agent mode - return success with input for agent processing\n return {\n success: true,\n data: {\n serviceId,\n input,\n mode: 'agent',\n promptFile: service.promptFile\n },\n metadata: {\n version: '1.0',\n executionMode: 'agent'\n }\n };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n metadata: {\n serviceId,\n errorType: 'execution_error'\n }\n };\n }\n }\n\n /**\n * Validate service input.\n */\n validate(serviceId: string, input: any): ValidationResult {\n const service = this.registry.get(serviceId);\n if (!service) {\n return {\n valid: false,\n error: `Service '${serviceId}' not found`\n };\n }\n\n // Use service handler validation if available\n if (service.handler) {\n return service.handler.validate(input);\n }\n\n // Default validation for agent-mode services\n if (service.inputSchema) {\n return this.validateAgainstSchema(input, service.inputSchema);\n }\n\n // No validation required\n return { valid: true };\n }\n\n /**\n * Reload all services.\n */\n async reload(): Promise<void> {\n // Clear existing services\n this.registry.clear();\n this.initialized = false;\n\n // Reinitialize\n await this.initialize();\n }\n\n /**\n * Get service for agent-mode processing.\n */\n getServiceForAgentMode(serviceId: string): {\n service: ServiceDefinition;\n promptFile?: string;\n } | null {\n const service = this.registry.get(serviceId);\n if (!service) {\n return null;\n }\n\n return {\n service,\n promptFile: service.promptFile\n };\n }\n\n /**\n * Check if service is available.\n */\n isServiceAvailable(serviceId: string): boolean {\n return this.registry.has(serviceId);\n }\n\n /**\n * Get service execution mode.\n */\n getServiceMode(serviceId: string): 'handler' | 'agent' | null {\n const service = this.registry.get(serviceId);\n if (!service) {\n return null;\n }\n\n return service.handler ? 'handler' : 'agent';\n }\n\n /**\n * Get all available services for discovery.\n */\n getAvailableServices(): Array<{\n id: string;\n name: string;\n description: string;\n defaultPrice: number;\n category?: string;\n mode: 'handler' | 'agent';\n }> {\n return this.registry.list().map(service => ({\n id: service.id,\n name: service.name,\n description: service.description,\n defaultPrice: service.defaultPrice,\n category: service.category,\n mode: service.handler ? 'handler' : 'agent'\n }));\n }\n\n /**\n * Validate input against JSON schema.\n */\n private validateAgainstSchema(input: any, schema: object): ValidationResult {\n // Simple schema validation - in production, you might use a library like ajv\n try {\n const schemaObj = schema as any;\n\n if (schemaObj.type === 'object') {\n if (!input || typeof input !== 'object') {\n return { valid: false, error: 'Input must be an object' };\n }\n\n // Check required properties\n if (schemaObj.required && Array.isArray(schemaObj.required)) {\n for (const requiredProp of schemaObj.required) {\n if (!(requiredProp in input)) {\n return { valid: false, error: `Missing required property: ${requiredProp}` };\n }\n }\n }\n\n // Basic type checking for properties\n if (schemaObj.properties) {\n for (const [propName, propSchema] of Object.entries(schemaObj.properties)) {\n if (propName in input) {\n const propType = (propSchema as any).type;\n const actualType = typeof input[propName];\n\n if (propType && propType !== actualType) {\n return { valid: false, error: `Property '${propName}' must be of type ${propType}` };\n }\n }\n }\n }\n }\n\n return { valid: true, sanitized: input };\n } catch (error) {\n return { valid: false, error: `Schema validation error: ${error}` };\n }\n }\n\n /**\n * Get service statistics.\n */\n getStatistics(): {\n totalServices: number;\n handlerServices: number;\n agentServices: number;\n servicesByCategory: Record<string, number>;\n } {\n const services = this.registry.list();\n const stats = {\n totalServices: services.length,\n handlerServices: 0,\n agentServices: 0,\n servicesByCategory: {} as Record<string, number>\n };\n\n for (const service of services) {\n // Count by execution mode\n if (service.handler) {\n stats.handlerServices++;\n } else {\n stats.agentServices++;\n }\n\n // Count by category\n const category = service.category || 'uncategorized';\n stats.servicesByCategory[category] = (stats.servicesByCategory[category] || 0) + 1;\n }\n\n return stats;\n }\n}\n\n/**\n * Global service manager instance.\n */\nexport const serviceManager = new DefaultServiceManager();\n\n/**\n * Initialize the service system.\n * This should be called once during application startup.\n */\nexport async function initializeServiceSystem(): Promise<void> {\n await serviceManager.initialize();\n}\n\n/**\n * Get service manager instance.\n */\nexport function getServiceManager(): DefaultServiceManager {\n return serviceManager;\n}", "/**\n * Wallet setup commands: setup, identity, address.\n */\n\nimport fs from 'node:fs';\nimport { NETWORK, WALLET_DIR, OVERLAY_URL, PATHS } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadWalletIdentity, deriveWalletAddress } from './identity.js';\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n// Dynamic import for @bsv/sdk\nlet _sdk: any = null;\n\nasync function getSdk(): Promise<any> {\n if (_sdk) return _sdk;\n\n try {\n _sdk = await import('@bsv/sdk');\n return _sdk;\n } catch {\n const { fileURLToPath } = await import('node:url');\n const path = await import('node:path');\n const os = await import('node:os');\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n ];\n\n for (const p of candidates) {\n try {\n _sdk = await import(p);\n return _sdk;\n } catch {\n // Try next\n }\n }\n throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');\n }\n}\n\n/**\n * Setup command: create wallet and show identity.\n */\nexport async function cmdSetup(): Promise<any> {\n const BSVAgentWallet = await getBSVAgentWallet();\n\n if (fs.existsSync(PATHS.walletIdentity)) {\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n return ok({\n identityKey,\n walletDir: WALLET_DIR,\n network: NETWORK,\n overlayUrl: OVERLAY_URL,\n alreadyExisted: true,\n });\n }\n\n fs.mkdirSync(WALLET_DIR, { recursive: true });\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n // Restrict permissions on wallet-identity.json (contains private key)\n if (fs.existsSync(PATHS.walletIdentity)) {\n fs.chmodSync(PATHS.walletIdentity, 0o600);\n }\n\n return ok({\n identityKey,\n walletDir: WALLET_DIR,\n network: NETWORK,\n overlayUrl: OVERLAY_URL,\n alreadyExisted: false,\n });\n}\n\n/**\n * Identity command: show identity public key.\n */\nexport async function cmdIdentity(): Promise<any> {\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n return ok({ identityKey });\n}\n\n/**\n * Status command: show identity and balance.\n */\nexport async function cmdStatus(): Promise<any> {\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n const total = await wallet.getBalance();\n await wallet.destroy();\n\n return ok({\n identity: { identityKey, network: NETWORK },\n balance: { walletBalance: total }\n });\n}\n\n/**\n * Address command: show P2PKH receive address.\n */\nexport async function cmdAddress(): Promise<any> {\n if (!fs.existsSync(PATHS.walletIdentity)) {\n return fail('Wallet not initialized. Run: setup');\n }\n\n const sdk = await getSdk();\n const identity = loadWalletIdentity();\n const privKey = sdk.PrivateKey.fromHex(identity.rootKeyHex);\n const { address } = await deriveWalletAddress(privKey);\n\n return ok({\n address,\n network: NETWORK,\n identityKey: identity.identityKey,\n note: NETWORK === 'mainnet'\n ? `Fund this address at an exchange \u2014 Explorer: https://whatsonchain.com/address/${address}`\n : `Fund via faucet: https://witnessonchain.com/faucet/tbsv \u2014 Explorer: https://test.whatsonchain.com/address/${address}`,\n });\n}\n", "/**\n * Wallet identity helpers.\n */\n\nimport fs from 'node:fs';\nimport { PATHS, NETWORK } from '../config.js';\nimport type { WalletIdentity } from '../types.js';\nimport { CachedKeyDeriver, Utils } from '@bsv/sdk';\nimport { brc29ProtocolID } from '@bsv/wallet-toolbox';\n\n// Dynamic import for @bsv/sdk\nlet _sdk: any = null;\n\nasync function getSdk(): Promise<any> {\n if (_sdk) return _sdk;\n\n try {\n _sdk = await import('@bsv/sdk');\n return _sdk;\n } catch {\n const { fileURLToPath } = await import('node:url');\n const path = await import('node:path');\n const os = await import('node:os');\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n ];\n\n for (const p of candidates) {\n try {\n _sdk = await import(p);\n return _sdk;\n } catch {\n // Try next\n }\n }\n throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');\n }\n}\n\n/**\n * Load wallet identity from disk.\n * @returns Identity object with rootKeyHex and identityKey\n * @throws Error if wallet not initialized\n */\nexport function loadWalletIdentity(): WalletIdentity {\n if (!fs.existsSync(PATHS.walletIdentity)) {\n throw new Error('Wallet not initialized. Run: cli setup');\n }\n\n // Security warning for overly permissive file mode\n try {\n const fileMode = fs.statSync(PATHS.walletIdentity).mode & 0o777;\n if (fileMode & 0o044) { // world or group readable\n console.error(`[security] WARNING: ${PATHS.walletIdentity} has permissive mode 0${fileMode.toString(8)}. Run: chmod 600 ${PATHS.walletIdentity}`);\n }\n } catch {\n // Ignore stat errors\n }\n\n return JSON.parse(fs.readFileSync(PATHS.walletIdentity, 'utf-8'));\n}\n\n/**\n * Load identity and private key for relay message signing.\n * @returns Object with identityKey and privKey\n */\nexport async function loadIdentity(): Promise<{ identityKey: string; privKey: any }> {\n const identity = loadWalletIdentity();\n const sdk = await getSdk();\n const privKey = sdk.PrivateKey.fromHex(identity.rootKeyHex);\n return { identityKey: identity.identityKey, privKey };\n}\n\n/**\n * Sign a relay message using ECDSA.\n * @param privKey - Private key for signing\n * @param to - Recipient's identity key\n * @param type - Message type\n * @param payload - Message payload\n * @returns Hex-encoded DER signature\n */\nexport async function signRelayMessage(\n privKey: any,\n to: string,\n type: string,\n payload: unknown\n): Promise<string> {\n const sdk = await getSdk();\n const preimage = to + type + JSON.stringify(payload);\n const msgHash = sdk.Hash.sha256(Array.from(new TextEncoder().encode(preimage)));\n const sig = privKey.sign(msgHash);\n return (Array.from(sig.toDER()) as number[]).map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Verify a relay message signature.\n * @param fromKey - Sender's public key\n * @param to - Recipient's identity key\n * @param type - Message type\n * @param payload - Message payload\n * @param signatureHex - Hex-encoded DER signature\n * @returns Verification result\n */\nexport async function verifyRelaySignature(\n fromKey: string,\n to: string,\n type: string,\n payload: unknown,\n signatureHex: string | undefined\n): Promise<{ valid: boolean; reason?: string }> {\n if (!signatureHex) return { valid: false, reason: 'no signature' };\n\n try {\n const sdk = await getSdk();\n const preimage = to + type + JSON.stringify(payload);\n const msgHash = sdk.Hash.sha256(Array.from(new TextEncoder().encode(preimage)));\n const sigBytes: number[] = [];\n for (let i = 0; i < signatureHex.length; i += 2) {\n sigBytes.push(parseInt(signatureHex.substring(i, i + 2), 16));\n }\n const sig = sdk.Signature.fromDER(sigBytes);\n const pubKey = sdk.PublicKey.fromString(fromKey);\n return { valid: pubKey.verify(msgHash, sig) };\n } catch (err) {\n return { valid: false, reason: String(err) };\n }\n}\n\n/**\n * Derive wallet address components from a private key.\n * \n * IMPORTANT: This uses BRC-29 key derivation to create a child key.\n * Any transactions spending to this address MUST use the matching\n * child private key for signing, NOT the root key.\n * \n * Use deriveWalletKeys() to get both the address and signing key.\n */\nexport async function deriveWalletAddress(privKey: any, network: string = NETWORK): Promise<{\n address: string;\n hash160: Uint8Array;\n pubKey: any;\n}> {\n \n const keyDeriver = new CachedKeyDeriver(privKey);\n const pubKey = keyDeriver.derivePublicKey(\n brc29ProtocolID,\n Utils.toBase64(Utils.toArray('import')) + ' ' + Utils.toBase64(Utils.toArray('now')),\n 'self',\n true\n );\n\n const address = pubKey.toAddress(network);\n const hash160 = Buffer.from(pubKey.toHash());\n\n return { address, hash160, pubKey };\n}\n\n/**\n * Derive wallet keys for both address AND transaction signing.\n * \n * CRITICAL: Use this function to get the child private key for signing\n * transactions that spend from the derived address. Do NOT use the\n * root private key - it will cause signature verification failures!\n * \n * @param rootPrivKey - Root private key from wallet identity\n * @param network - Optional network override\n * @returns Object with address, hash160, and CHILD private key for signing\n */\nexport async function deriveWalletKeys(rootPrivKey: any, network: string = NETWORK): Promise<{\n address: string;\n hash160: Uint8Array;\n pubKey: any;\n childPrivKey: any;\n}> {\n const keyDeriver = new CachedKeyDeriver(rootPrivKey);\n \n const derivationPrefix = Utils.toBase64(Utils.toArray('import'));\n const derivationSuffix = Utils.toBase64(Utils.toArray('now'));\n const keyString = `${derivationPrefix} ${derivationSuffix}`;\n \n // Derive child private key (for signing)\n const childPrivKey = keyDeriver.derivePrivateKey(\n brc29ProtocolID,\n keyString,\n 'self'\n );\n \n // Derive child public key (for address)\n const pubKey = keyDeriver.derivePublicKey(\n brc29ProtocolID,\n keyString,\n 'self',\n true\n );\n\n const address = pubKey.toAddress(network);\n const hash160 = Buffer.from(pubKey.toHash());\n\n return { address, hash160, pubKey, childPrivKey };\n}\n", "/**\n * Wallet balance commands: balance, import, refund.\n */\n\nimport fs from 'node:fs';\nimport { NETWORK, WALLET_DIR, PATHS } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadWalletIdentity } from './identity.js';\nimport { wocFetch, fetchBeefFromWoC, getExplorerBaseUrl } from '../utils/woc.js';\nimport { buildMerklePathFromTSC } from '../utils/merkle.js';\nimport { loadStoredChange, deleteStoredChange } from '../utils/storage.js';\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n// Dynamic import for @bsv/sdk\nlet _sdk: any = null;\n\nasync function getSdk(): Promise<any> {\n if (_sdk) return _sdk;\n\n try {\n _sdk = await import('@bsv/sdk');\n return _sdk;\n } catch {\n const { fileURLToPath } = await import('node:url');\n const path = await import('node:path');\n const os = await import('node:os');\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n ];\n\n for (const p of candidates) {\n try {\n _sdk = await import(p);\n return _sdk;\n } catch {\n // Try next\n }\n }\n throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');\n }\n}\n\n/**\n * Sleep helper for polling\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Balance command: show wallet balance.\n */\nexport async function cmdBalance(): Promise<any> {\n const BSVAgentWallet = await getBSVAgentWallet();\n const sdk = await getSdk();\n\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const total = await wallet.getBalance();\n await wallet.destroy();\n\n return ok({ walletBalance: total });\n}\n\n/**\n * Import command: import external UTXO with merkle proof.\n * \n * This function handles both confirmed and unconfirmed transactions.\n * For unconfirmed transactions, it uses BEEF from WoC which includes\n * the source chain back to confirmed ancestors (SPV-compliant).\n * \n * If the transaction isn't yet on WoC (just broadcast), it will poll\n * with exponential backoff for up to 60 seconds.\n */\nexport async function cmdImport(txidArg: string | undefined, voutStr?: string): Promise<any> {\n if (!txidArg) {\n return fail('Usage: import <txid> [vout]');\n }\n\n const vout = parseInt(voutStr || '0', 10);\n const txid = txidArg.toLowerCase();\n\n if (!/^[0-9a-f]{64}$/.test(txid)) {\n return fail('Invalid txid \u2014 must be 64 hex characters');\n }\n\n const sdk = await getSdk();\n const BSVAgentWallet = await getBSVAgentWallet();\n\n // Poll for transaction on WoC with exponential backoff\n // This handles the case where user just broadcast and WoC hasn't indexed yet\n let txInfo: any = null;\n const maxWaitMs = 60000; // 60 seconds max\n const startTime = Date.now();\n let attempt = 0;\n \n while (Date.now() - startTime < maxWaitMs) {\n const txInfoResp = await wocFetch(`/tx/${txid}`, {}, 1, 10000); // Single retry, 10s timeout\n \n if (txInfoResp.ok) {\n txInfo = await txInfoResp.json();\n break;\n } else if (txInfoResp.status === 404) {\n // Transaction not found yet - wait and retry\n attempt++;\n const delayMs = Math.min(1000 * Math.pow(1.5, attempt), 10000); // 1s, 1.5s, 2.25s, ... max 10s\n console.error(`[import] Transaction not on WoC yet, waiting ${Math.round(delayMs/1000)}s... (attempt ${attempt})`);\n await sleep(delayMs);\n continue;\n } else {\n return fail(`Failed to fetch tx info: ${txInfoResp.status}`);\n }\n }\n\n if (!txInfo) {\n return fail(`Transaction ${txid} not found on WhatsOnChain after ${Math.round((Date.now() - startTime) / 1000)}s. The transaction may not have been broadcast yet, or the txid may be incorrect.`);\n }\n\n const isConfirmed = txInfo.confirmations && txInfo.confirmations >= 1;\n const blockHeight = txInfo.blockheight;\n\n // Validate output exists\n if (!txInfo.vout || !txInfo.vout[vout]) {\n return fail(`Output index ${vout} not found in transaction (has ${txInfo.vout?.length || 0} outputs)`);\n }\n\n let atomicBeefBytes: Uint8Array | undefined;\n\n // Try WoC BEEF first - works for both confirmed and unconfirmed transactions\n // WoC provides BEEF with full source chain back to confirmed ancestors\n const wocBeefBytes = await fetchBeefFromWoC(txid);\n \n if (wocBeefBytes) {\n try {\n const wocBeef = sdk.Beef.fromBinary(Array.from(wocBeefBytes));\n const foundTx = wocBeef.findTxid(txid);\n \n if (foundTx) {\n // Verify the output exists in the parsed tx\n const txObj = foundTx.tx || foundTx._tx;\n if (txObj) {\n const output = txObj.outputs[vout];\n if (!output) {\n return fail(`Output index ${vout} not found in BEEF transaction (has ${txObj.outputs.length} outputs)`);\n }\n }\n atomicBeefBytes = wocBeef.toBinaryAtomic(txid);\n }\n } catch (beefErr: any) {\n console.error(`[import] WoC BEEF parse failed: ${beefErr.message}`);\n // Fall through to manual construction\n }\n }\n\n // Fallback for confirmed txs: construct BEEF manually using TSC merkle proof\n if (!atomicBeefBytes && isConfirmed) {\n try {\n const rawTxResp = await wocFetch(`/tx/${txid}/hex`);\n if (!rawTxResp.ok) {\n return fail(`Failed to fetch raw transaction: ${rawTxResp.status}`);\n }\n const rawTxHex = await rawTxResp.text();\n const sourceTx = sdk.Transaction.fromHex(rawTxHex.trim());\n\n const proofResp = await wocFetch(`/tx/${txid}/proof/tsc`);\n if (!proofResp.ok) {\n return fail(`Failed to fetch merkle proof: ${proofResp.status}`);\n }\n const proofData = await proofResp.json();\n \n if (!Array.isArray(proofData) || proofData.length === 0) {\n return fail('Merkle proof not available from WoC');\n }\n\n const proof = proofData[0];\n const merklePath = await buildMerklePathFromTSC(txid, proof.index, proof.nodes, blockHeight);\n sourceTx.merklePath = merklePath;\n\n const beef = new sdk.Beef();\n beef.mergeTransaction(sourceTx);\n atomicBeefBytes = beef.toBinaryAtomic(txid);\n } catch (manualErr: any) {\n return fail(`Failed to construct BEEF manually: ${manualErr.message}`);\n }\n }\n\n // If still no BEEF, we can't import\n if (!atomicBeefBytes) {\n if (isConfirmed) {\n return fail(`Transaction ${txid} is confirmed but BEEF construction failed. This is unexpected \u2014 please report this issue.`);\n } else {\n // Unconfirmed and no BEEF available\n // This can happen if the funding tx itself spends unconfirmed inputs\n return fail(\n `Transaction ${txid} is unconfirmed (${txInfo.confirmations || 0} confirmations) and BEEF is not available.\\n\\n` +\n `This usually means the funding transaction spends from other unconfirmed transactions, creating a chain.\\n` +\n `Wait for 1 block confirmation (~10 minutes) and try again, or use a fresh UTXO as the funding source.`\n );\n }\n }\n\n // Get output satoshis for reporting\n const outputSatoshis = txInfo.vout[vout].value != null\n ? Math.round(txInfo.vout[vout].value * 1e8)\n : undefined;\n\n // Import into wallet\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n\n try {\n await wallet._setup.wallet.storage.internalizeAction({\n tx: Array.from(atomicBeefBytes),\n outputs: [{\n outputIndex: vout,\n protocol: 'wallet payment',\n paymentRemittance: {\n derivationPrefix: sdk.Utils.toBase64(sdk.Utils.toArray('import', 'utf8')),\n derivationSuffix: sdk.Utils.toBase64(sdk.Utils.toArray('now', 'utf8')),\n senderIdentityKey: identityKey,\n },\n }],\n description: 'External funding import',\n });\n\n const balance = await wallet.getBalance();\n await wallet.destroy();\n\n const explorerBase = getExplorerBaseUrl();\n return ok({\n txid,\n vout,\n satoshis: outputSatoshis,\n blockHeight: blockHeight || null,\n confirmations: txInfo.confirmations || 0,\n imported: true,\n unconfirmed: !isConfirmed,\n balance,\n explorer: `${explorerBase}/tx/${txid}`,\n });\n } catch (err: any) {\n await wallet.destroy();\n \n // Provide helpful error messages for common issues\n if (err.message?.includes('already') || err.message?.includes('duplicate')) {\n return fail(`UTXO ${txid}:${vout} appears to already be imported.`);\n }\n if (err.message?.includes('script') || err.message?.includes('locking')) {\n return fail(`UTXO ${txid}:${vout} does not belong to this wallet's address. Make sure you sent to the correct address.`);\n }\n \n return fail(`Failed to import UTXO: ${err.message}`);\n }\n}\n\n/**\n * Refund command: sweep wallet to an address.\n */\nexport async function cmdRefund(targetAddress: string | undefined): Promise<void> {\n if (!targetAddress) {\n return fail('Usage: refund <address>');\n }\n\n if (!fs.existsSync(PATHS.walletIdentity)) {\n return fail('Wallet not initialized. Run: setup');\n }\n\n // TODO IMPLEMENT THIS\n}\n", "/**\n * WhatsOnChain API helpers with retry logic and rate limiting.\n */\n\nimport { NETWORK, WOC_API_KEY } from '../config.js';\n\n/**\n * Fetch from WhatsonChain with optional API key auth and retry logic.\n * Retries on 429 (rate limit) and 5xx errors with exponential backoff.\n * Includes timeout to prevent hanging indefinitely.\n */\nexport async function wocFetch(\n urlPath: string,\n options: RequestInit = {},\n maxRetries = 3,\n timeoutMs = 30000\n): Promise<Response> {\n const wocNet = NETWORK === 'mainnet' ? 'main' : 'test';\n const base = `https://api.whatsonchain.com/v1/bsv/${wocNet}`;\n const url = urlPath.startsWith('http') ? urlPath : `${base}${urlPath}`;\n const headers: Record<string, string> = { ...(options.headers as Record<string, string> || {}) };\n if (WOC_API_KEY) {\n headers['Authorization'] = `Bearer ${WOC_API_KEY}`;\n }\n\n let lastError: Error | undefined;\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n const resp = await fetch(url, { ...options, headers, signal: controller.signal });\n clearTimeout(timeout);\n\n // Retry on 429 (rate limit) or 5xx (server error)\n if ((resp.status === 429 || resp.status >= 500) && attempt < maxRetries) {\n const delayMs = Math.min(1000 * Math.pow(2, attempt), 8000);\n await new Promise(r => setTimeout(r, delayMs));\n continue;\n }\n\n return resp;\n } catch (err) {\n lastError = err as Error;\n if (attempt < maxRetries) {\n const delayMs = Math.min(1000 * Math.pow(2, attempt), 8000);\n await new Promise(r => setTimeout(r, delayMs));\n continue;\n }\n }\n }\n\n throw lastError || new Error('WoC fetch failed after retries');\n}\n\n/**\n * Fetch with timeout using AbortController.\n */\nexport async function fetchWithTimeout(\n url: string,\n options: RequestInit = {},\n timeoutMs = 15000\n): Promise<Response> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const resp = await fetch(url, { ...options, signal: controller.signal });\n return resp;\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * Fetch a pre-built BEEF from WhatsonChain for a given txid.\n * WoC returns raw binary BEEF that includes the full source chain and merkle proofs.\n */\nexport async function fetchBeefFromWoC(txid: string): Promise<Uint8Array | null> {\n try {\n const resp = await wocFetch(`/tx/${txid}/beef`);\n if (!resp.ok) return null;\n const hexStr = (await resp.text()).trim();\n if (!hexStr || hexStr.length < 8) return null;\n const bytes = hexStr.match(/.{2}/g)!.map(h => parseInt(h, 16));\n return new Uint8Array(bytes);\n } catch {\n return null;\n }\n}\n\n/**\n * Get the WoC base URL for the current network.\n */\nexport function getWocBaseUrl(): string {\n const wocNet = NETWORK === 'mainnet' ? 'main' : 'test';\n return `https://api.whatsonchain.com/v1/bsv/${wocNet}`;\n}\n\n/**\n * Get the explorer base URL for the current network.\n */\nexport function getExplorerBaseUrl(): string {\n return NETWORK === 'mainnet'\n ? 'https://whatsonchain.com'\n : 'https://test.whatsonchain.com';\n}\n", "/**\n * Merkle path utilities for SPV proofs.\n */\n\nimport type { MerklePath as MerklePathType } from '@bsv/sdk';\n\n// We'll import MerklePath dynamically to avoid issues with ESM resolution\nlet _MerklePath: typeof MerklePathType | null = null;\n\nasync function getMerklePath(): Promise<typeof MerklePathType> {\n if (_MerklePath) return _MerklePath;\n const sdk = await import('@bsv/sdk');\n _MerklePath = sdk.MerklePath;\n return _MerklePath;\n}\n\n/**\n * Build a MerklePath from TSC (Transaction Status Check) proof data.\n * @param txid - Transaction ID\n * @param txIndex - Transaction's index in the block\n * @param nodes - Array of sibling hashes (or '*' for duplicate)\n * @param blockHeight - Block height\n */\nexport async function buildMerklePathFromTSC(\n txid: string,\n txIndex: number,\n nodes: string[],\n blockHeight: number\n): Promise<MerklePathType> {\n const MerklePath = await getMerklePath();\n const treeHeight = nodes.length;\n const mpPath: Array<Array<{ offset: number; hash?: string; txid?: boolean; duplicate?: boolean }>> = [];\n\n // Level 0\n const level0: Array<{ offset: number; hash?: string; txid?: boolean; duplicate?: boolean }> = [\n { offset: txIndex, hash: txid, txid: true }\n ];\n if (nodes[0] === '*') {\n level0.push({ offset: txIndex ^ 1, duplicate: true });\n } else {\n level0.push({ offset: txIndex ^ 1, hash: nodes[0] });\n }\n level0.sort((a, b) => a.offset - b.offset);\n mpPath.push(level0);\n\n // Higher levels\n for (let i = 1; i < treeHeight; i++) {\n const siblingOffset = (txIndex >> i) ^ 1;\n if (nodes[i] === '*') {\n mpPath.push([{ offset: siblingOffset, duplicate: true }]);\n } else {\n mpPath.push([{ offset: siblingOffset, hash: nodes[i] }]);\n }\n }\n\n return new MerklePath(blockHeight, mpPath);\n}\n", "/**\n * Overlay registration commands: register, unregister.\n * \n * Registration creates an identity record on the overlay network with:\n * - identityKey: compressed public key (66 hex chars)\n * - name: agent display name\n * - description: what the agent does\n * - channels: contact methods (e.g., { overlay: \"https://...\" })\n * - capabilities: what the agent can do (e.g., [\"services\", \"jokes\"])\n * - timestamp: ISO 8601 registration time\n */\n\nimport fs from 'node:fs';\nimport { NETWORK, WALLET_DIR, OVERLAY_URL, PROTOCOL_ID, TOPICS, PATHS, AGENT_NAME, AGENT_DESCRIPTION } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadRegistration, saveRegistration, deleteRegistration, loadServices } from '../utils/storage.js';\nimport { buildRealOverlayTransaction } from './transaction.js';\nimport { Transaction, Beef, Script, PushDrop, WalletOutput } from '@bsv/sdk'\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n/**\n * Register command: register this agent on the overlay network.\n */\nexport async function cmdRegister(): Promise<any> {\n if (!fs.existsSync(PATHS.walletIdentity)) {\n return fail('Wallet not initialized. Run: setup');\n }\n\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const identityKey = await wallet.getIdentityKey();\n await wallet.destroy();\n\n const existingReg = loadRegistration();\n if (existingReg && existingReg.identityKey === identityKey) {\n return ok({\n alreadyRegistered: true,\n identityKey,\n identityTxid: existingReg.identityTxid,\n overlayUrl: OVERLAY_URL,\n });\n }\n\n // Agent metadata from environment/config\n const agentName = AGENT_NAME;\n const agentDescription = AGENT_DESCRIPTION;\n\n // Build capabilities list based on what services we might offer\n const capabilities: string[] = ['services'];\n const services = loadServices();\n if (services.some(s => s.serviceId === 'tell-joke')) {\n capabilities.push('jokes');\n }\n\n // Create identity record on-chain\n // This payload format matches the openclaw-overlay server's expected schema\n const identityPayload = {\n protocol: PROTOCOL_ID,\n type: 'identity' as const,\n identityKey,\n name: agentName,\n description: agentDescription,\n channels: {\n overlay: OVERLAY_URL,\n },\n capabilities,\n timestamp: new Date().toISOString(),\n };\n\n let identityResult: { txid: string; funded: string };\n try {\n identityResult = await buildRealOverlayTransaction(identityPayload, TOPICS.IDENTITY);\n } catch (err: any) {\n return fail(`Registration failed: ${err.message}`);\n }\n\n // Optionally register services if pre-configured\n let serviceTxid: string | null = null;\n\n if (services.length > 0) {\n // Register each service individually (server expects 'service' type, not 'service-bundle')\n for (const service of services) {\n const servicePayload = {\n protocol: PROTOCOL_ID,\n type: 'service' as const,\n identityKey,\n serviceId: service.serviceId,\n name: service.name,\n description: service.description,\n pricing: {\n model: 'per-task',\n amountSats: service.priceSats,\n },\n timestamp: new Date().toISOString(),\n };\n\n try {\n const serviceResult = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);\n serviceTxid = serviceResult.txid; // Keep last one for backward compat\n } catch {\n // Non-fatal \u2014 identity registered but this service failed\n }\n }\n }\n\n // Save registration\n const registration = {\n identityKey,\n agentName,\n agentDescription,\n overlayUrl: OVERLAY_URL,\n identityTxid: identityResult.txid,\n serviceTxid,\n funded: identityResult.funded,\n registeredAt: new Date().toISOString(),\n };\n saveRegistration(registration);\n\n return ok({\n registered: true,\n identityKey,\n identityTxid: identityResult.txid,\n serviceTxid,\n overlayUrl: OVERLAY_URL,\n funded: identityResult.funded,\n stateFile: PATHS.registration,\n });\n}\n\n/**\n * Unregister command: submit revocation tx to remove agent from overlay network.\n */\nexport async function cmdUnregister(): Promise<any> {\n \n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n const { outputs, BEEF } = await wallet._setup.wallet.listOutputs({ basket: TOPICS.IDENTITY, include: 'entire transactions' });\n\n const token = new PushDrop(wallet._setup.wallet);\n const unlockingScriptTemplate = await token.unlock([0, PROTOCOL_ID], '1', 'self', 'none', true)\n const tempTx = new Transaction()\n const beef = Beef.fromBinary(BEEF as number[])\n outputs.forEach((o: WalletOutput) => {\n const [txid, v] = o.outpoint.split('.')\n const sourceOutputIndex = Number(v)\n const sourceTransaction = beef.findTransactionForSigning(txid)\n tempTx.addInput({\n unlockingScriptTemplate,\n sourceOutputIndex,\n sourceTransaction\n })\n })\n tempTx.addOutput({\n lockingScript: Script.fromASM('OP_FALSE OP_RETURN 330123'),\n satoshis: 0\n })\n\n await tempTx.sign()\n\n const response = await wallet._setup.wallet.createAction({\n inputBEEF: BEEF,\n description: 'revoke registration token',\n inputs: tempTx.inputs.map(o => ({\n inputDescription: 'previous registration',\n outpoint: o.sourceTXID + '.' + String(o.sourceOutputIndex),\n unlockingScript: o.unlockingScript?.toHex() as string\n }))\n })\n\n const txid = response.txid as string;\n\n // --- Submit to overlay ---\n // Use binary BEEF with X-Topics header (matches openclaw-overlay server API)\n const submitResp = await fetch(`${OVERLAY_URL}/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/octet-stream',\n 'X-Topics': JSON.stringify([TOPICS.IDENTITY]),\n },\n body: new Uint8Array(response.tx as number[]),\n });\n\n if (!submitResp.ok) {\n const errText = await submitResp.text();\n throw new Error(`Overlay submission failed: ${submitResp.status} \u2014 ${errText}`);\n }\n \n // Delete local registration\n deleteRegistration();\n\n return ok({\n unregistered: true,\n txid\n });\n}\n", "/**\n * Overlay discovery commands.\n */\n\nimport { OVERLAY_URL, LOOKUP_SERVICES } from '../config.js';\nimport { ok } from '../output.js';\nimport { lookupOverlay, parseOverlayOutput } from './transaction.js';\n\n/**\n * Discover command: query the overlay for agents and services.\n */\nexport async function cmdDiscover(args: string[]): Promise<any> {\n\n // Parse flags\n let serviceFilter: string | null = null;\n let agentFilter: string | null = null;\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--service' && args[i + 1]) serviceFilter = args[++i];\n else if (args[i] === '--agent' && args[i + 1]) agentFilter = args[++i];\n }\n\n const results: {\n agents: any[];\n services: any[];\n agentError?: string;\n serviceError?: string;\n } = { agents: [], services: [] };\n\n // Query agents\n if (!serviceFilter) {\n try {\n const agentQuery = agentFilter ? { name: agentFilter } : { type: 'list' };\n const agentResult = await lookupOverlay(LOOKUP_SERVICES.AGENTS, agentQuery);\n\n if (agentResult.outputs) {\n for (const output of agentResult.outputs) {\n try {\n const { data, txid } = await parseOverlayOutput(output.beef, output.outputIndex);\n if (data?.type === 'identity') {\n // Handle both 'name' and 'agentName' for backward compatibility\n const name = data.name || data.agentName || 'Unknown Agent';\n results.agents.push({ ...data, name, txid });\n }\n } catch { /* ignore */ }\n }\n }\n } catch (err: any) {\n results.agentError = String(err);\n }\n }\n\n // Query services\n if (!agentFilter) {\n try {\n const serviceQuery = serviceFilter ? { serviceType: serviceFilter } : {};\n const serviceResult = await lookupOverlay(LOOKUP_SERVICES.SERVICES, serviceQuery);\n\n if (serviceResult.outputs) {\n for (const output of serviceResult.outputs) {\n try {\n const { data, txid } = await parseOverlayOutput(output.beef, output.outputIndex);\n if (data?.type === 'service') {\n results.services.push({ ...data, txid });\n }\n } catch { /* ignore */ }\n }\n }\n } catch (err: any) {\n results.serviceError = String(err);\n }\n }\n\n return ok({\n overlayUrl: OVERLAY_URL,\n agentCount: results.agents.length,\n serviceCount: results.services.length,\n agents: results.agents,\n services: results.services,\n ...(results.agentError && { agentError: results.agentError }),\n ...(results.serviceError && { serviceError: results.serviceError }),\n });\n}\n", "/**\n * Service request command.\n */\n\nimport { OVERLAY_URL } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadIdentity, signRelayMessage } from '../wallet/identity.js';\nimport { buildDirectPayment } from '../payment/build.js';\n\n/**\n * Request service command: send a service request with optional payment.\n */\nexport async function cmdRequestService(\n targetKey: string | undefined,\n serviceId: string | undefined,\n satsStr?: string,\n inputJsonStr?: string\n): Promise<any> {\n if (!targetKey || !serviceId) {\n return fail('Usage: request-service <identityKey> <serviceId> [sats] [inputJson]');\n }\n\n if (!/^0[23][0-9a-fA-F]{64}$/.test(targetKey)) {\n return fail('Target must be a compressed public key (66 hex chars, 02/03 prefix)');\n }\n\n const { identityKey, privKey } = await loadIdentity();\n const sats = parseInt(satsStr || '5', 10);\n\n // Parse optional input JSON\n let inputData: unknown = null;\n if (inputJsonStr) {\n try {\n inputData = JSON.parse(inputJsonStr);\n } catch {\n return fail('inputJson must be valid JSON');\n }\n }\n\n // Build the service request payload\n let paymentData: any = null;\n\n if (sats > 0) {\n try {\n const payment = await buildDirectPayment(targetKey, sats, `service-request: ${serviceId}`);\n paymentData = {\n beef: payment.beef,\n txid: payment.txid,\n satoshis: payment.satoshis,\n derivationPrefix: payment.derivationPrefix,\n derivationSuffix: payment.derivationSuffix,\n senderIdentityKey: payment.senderIdentityKey,\n };\n } catch (err: any) {\n // Payment failed \u2014 send request without payment\n paymentData = { error: String(err.message || err) };\n }\n }\n\n const requestPayload = {\n serviceId,\n ...(inputData ? { input: inputData } : {}),\n payment: paymentData,\n requestedAt: new Date().toISOString(),\n };\n\n const signature = await signRelayMessage(privKey, targetKey, 'service-request', requestPayload);\n\n const resp = await fetch(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n from: identityKey,\n to: targetKey,\n type: 'service-request',\n payload: requestPayload,\n signature,\n }),\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n return fail(`Relay send failed (${resp.status}): ${body}`);\n }\n\n const result = await resp.json();\n\n return ok({\n sent: true,\n requestId: result.id,\n to: targetKey,\n serviceId,\n paymentIncluded: paymentData && !paymentData.error,\n paymentTxid: paymentData?.txid || null,\n satoshis: paymentData?.satoshis || 0,\n note: 'Poll for service-response to get the result',\n });\n}\n", "/**\n * Payment building using a2a-bsv wallet.createPayment().\n *\n * This replaces the old buildDirectPayment() which used plain P2PKH scripts\n * and manual UTXO management. The new implementation:\n * - Uses proper BRC-29 locking scripts via wallet.createPayment()\n * - Relies on wallet's createAction() for UTXO management\n * - Uses noSend: true (recipient broadcasts via acceptPayment())\n */\n\nimport { NETWORK, WALLET_DIR } from '../config.js';\nimport type { PaymentResult } from './types.js';\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n/**\n * Build a BRC-29 payment using the a2a-bsv wallet.\n *\n * This creates a payment transaction using proper BRC-29 locking scripts.\n * The transaction uses noSend: true, meaning:\n * - The sender does NOT broadcast the transaction\n * - The recipient broadcasts it when they call acceptPayment()\n *\n * @param recipientPubKey - Recipient's compressed public key (66 hex chars, 02/03 prefix)\n * @param sats - Amount to send in satoshis\n * @param desc - Optional description for the payment\n * @returns PaymentResult with BEEF and derivation metadata for the recipient\n */\nexport async function buildDirectPayment(\n recipientPubKey: string,\n sats: number,\n desc?: string\n): Promise<PaymentResult> {\n // Validate recipient pubkey format\n if (!/^0[23][0-9a-fA-F]{64}$/.test(recipientPubKey)) {\n throw new Error('Recipient must be a compressed public key (66 hex chars starting with 02 or 03)');\n }\n\n const BSVAgentWallet = await getBSVAgentWallet();\n const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });\n\n try {\n const result = await wallet.createPayment({\n to: recipientPubKey,\n satoshis: sats,\n description: desc || 'agent payment',\n });\n\n // Return format compatible with existing code\n return {\n beef: result.beef,\n txid: result.txid,\n satoshis: result.satoshis,\n derivationPrefix: result.derivationPrefix,\n derivationSuffix: result.derivationSuffix,\n senderIdentityKey: result.senderIdentityKey,\n };\n } finally {\n await wallet.destroy();\n }\n}\n", "/**\n * Service queue commands.\n */\n\nimport fs from 'node:fs';\nimport { PATHS } from '../config.js';\nimport { ok } from '../output.js';\nimport { readJsonl } from '../utils/storage.js';\n\n/**\n * Service queue command: list pending service requests.\n */\nexport async function cmdServiceQueue(): Promise<any> {\n if (!fs.existsSync(PATHS.serviceQueue)) {\n return ok({ pending: [], count: 0 });\n }\n\n const entries = readJsonl<any>(PATHS.serviceQueue);\n const pending = entries.filter(e => e.status === 'pending');\n\n return ok({ pending, count: pending.length, total: entries.length });\n}\n\n/**\n * Research queue command: list pending research requests.\n */\nexport async function cmdResearchQueue(): Promise<any> {\n if (!fs.existsSync(PATHS.researchQueue)) {\n return ok({ pending: [] });\n }\n\n const entries = readJsonl<any>(PATHS.researchQueue);\n\n return ok({ pending: entries, count: entries.length });\n}\n", "/**\n * Service response commands.\n */\n\nimport fs from 'node:fs';\nimport { OVERLAY_URL, PATHS } from '../config.js';\nimport { ok, fail } from '../output.js';\nimport { loadIdentity, signRelayMessage } from '../wallet/identity.js';\nimport { updateServiceQueueStatus } from '../utils/storage.js';\n\n/**\n * Respond to a service request.\n */\nexport async function cmdRespondService(\n requestId: string | undefined,\n recipientKey: string | undefined,\n serviceId: string | undefined,\n resultJson: string | undefined\n): Promise<any> {\n if (!requestId || !recipientKey || !serviceId || !resultJson) {\n return fail('Usage: respond-service <requestId> <recipientKey> <serviceId> <resultJson>');\n }\n\n let result: unknown;\n try {\n result = JSON.parse(resultJson);\n } catch {\n return fail('resultJson must be valid JSON');\n }\n\n const { identityKey, privKey } = await loadIdentity();\n\n // Check if already processed before sending response (idempotency)\n if (fs.existsSync(PATHS.serviceQueue)) {\n const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n const finalStatuses = ['fulfilled', 'rejected', 'delivery_failed', 'failed', 'error'];\n\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n if (entry.requestId === requestId && finalStatuses.includes(entry.status)) {\n return ok({\n sent: false,\n requestId,\n serviceId,\n to: recipientKey,\n message: `Request already processed with status: ${entry.status}`,\n alreadyProcessed: true,\n previousStatus: entry.status\n });\n }\n } catch {}\n }\n }\n\n const responsePayload = {\n requestId,\n serviceId,\n status: 'fulfilled',\n result,\n };\n\n const sig = await signRelayMessage(privKey, recipientKey, 'service-response', responsePayload);\n const resp = await fetch(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n from: identityKey,\n to: recipientKey,\n type: 'service-response',\n payload: responsePayload,\n signature: sig,\n }),\n });\n\n if (!resp.ok) {\n // Mark as failed in queue using atomic update\n updateServiceQueueStatus(requestId, 'failed', {\n failedAt: Date.now(),\n error: `Relay send failed: ${resp.status}`\n });\n return fail(`Relay send failed: ${resp.status}`);\n }\n\n // Mark as fulfilled in queue using atomic update\n updateServiceQueueStatus(requestId, 'fulfilled', {\n fulfilledAt: Date.now()\n });\n\n return ok({ sent: true, requestId, serviceId, to: recipientKey });\n}\n\n/**\n * Respond to a research request with results.\n */\nexport async function cmdResearchRespond(resultJsonPath: string | undefined): Promise<any> {\n if (!resultJsonPath) return fail('Usage: research-respond <resultJsonFile>');\n if (!fs.existsSync(resultJsonPath)) return fail(`File not found: ${resultJsonPath}`);\n\n const result = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));\n const { requestId, from: recipientKey, query, research } = result;\n\n if (!requestId || !recipientKey || !research) {\n return fail('Result JSON must have: requestId, from, query, research');\n }\n\n const { identityKey, privKey } = await loadIdentity();\n\n const responsePayload = {\n requestId,\n serviceId: 'web-research',\n status: 'fulfilled',\n result: research,\n paymentAccepted: true,\n paymentTxid: result.paymentTxid || null,\n satoshisReceived: result.satoshisReceived || 0,\n walletAccepted: result.walletAccepted ?? true,\n };\n\n const sig = await signRelayMessage(privKey, recipientKey, 'service-response', responsePayload);\n const sendResp = await fetch(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n from: identityKey,\n to: recipientKey,\n type: 'service-response',\n payload: responsePayload,\n signature: sig,\n }),\n });\n\n if (!sendResp.ok) {\n return fail(`Failed to send response: ${await sendResp.text()}`);\n }\n\n const sendResult = await sendResp.json();\n\n // Remove from queue\n if (fs.existsSync(PATHS.researchQueue)) {\n const lines = fs.readFileSync(PATHS.researchQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n const remaining = lines.filter((l: string) => {\n try { return JSON.parse(l).requestId !== requestId; } catch { return true; }\n });\n fs.writeFileSync(PATHS.researchQueue, remaining.length ? remaining.join('\\n') + '\\n' : '');\n }\n\n return ok({ responded: true, requestId, to: recipientKey, query, pushed: sendResult.pushed });\n}\n", "/**\n * Connect command: WebSocket real-time message processing.\n */\n\nimport fs from 'node:fs';\nimport { OVERLAY_URL, PATHS } from '../config.js';\nimport { fail } from '../output.js';\nimport { loadIdentity } from '../wallet/identity.js';\nimport { processMessage } from './handlers.js';\nimport { ensureStateDir } from '../utils/storage.js';\nimport debug from 'debug';\n\nconst log = debug('openclaw:plugin:overlay:connect');\n\n/**\n * Connect command: establish WebSocket connection for real-time messaging.\n * Supports being used as a library with onMessage callback and AbortSignal.\n */\nexport async function cmdConnect(onMessage?: (data: any) => void, signal?: AbortSignal): Promise<void> {\n let WebSocketClient: any;\n try {\n const ws = await import('ws');\n WebSocketClient = ws.default || (ws as any).WebSocket || ws;\n } catch {\n return fail('WebSocket client not available. Install it: npm install ws');\n }\n\n const { identityKey, privKey } = await loadIdentity();\n const wsUrl = OVERLAY_URL.replace(/^http/, 'ws') + '/relay/subscribe?identity=' + identityKey;\n log('Connecting to WebSocket relay: %s', wsUrl);\n\n let reconnectDelay = 1000;\n let shouldReconnect = true;\n let currentWs: any = null;\n\n function shutdown() {\n shouldReconnect = false;\n if (currentWs) {\n try { currentWs.close(); } catch {}\n }\n // Only exit if we're not running as a library\n if (!onMessage) {\n process.exit(0);\n }\n }\n\n if (!onMessage) {\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n }\n\n if (signal) {\n signal.addEventListener('abort', () => {\n shouldReconnect = false;\n if (currentWs) {\n try { currentWs.close(); } catch {}\n }\n });\n }\n\n function connect() {\n if (signal?.aborted) return;\n const ws = new WebSocketClient(wsUrl);\n currentWs = ws;\n\n ws.on('open', () => {\n log('WebSocket connection established!');\n reconnectDelay = 1000; // reset on successful connect\n const logMsg = { event: 'connected', identity: identityKey, overlay: OVERLAY_URL };\n if (onMessage) onMessage(logMsg);\n else console.error(JSON.stringify(logMsg));\n });\n\n ws.on('message', async (data: any) => {\n log('Incoming WebSocket message received');\n try {\n const envelope = JSON.parse(data.toString());\n log('Message type: %s', envelope.type);\n if (envelope.type === 'message') {\n const result = await processMessage(envelope.message, identityKey, privKey);\n log('Processed message: %s', result.id);\n \n if (onMessage) onMessage(result);\n else console.log(JSON.stringify(result));\n\n // Also append to notification log\n ensureStateDir();\n try {\n fs.appendFileSync(PATHS.notifications, JSON.stringify({ ...result, _ts: Date.now() }) + '\\n');\n } catch {}\n\n // Ack the message\n if (result.ack) {\n try {\n await fetch(OVERLAY_URL + '/relay/ack', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ identity: identityKey, messageIds: [result.id] }),\n });\n } catch (ackErr: any) {\n const log = { event: 'ack-error', id: result.id, message: String(ackErr) };\n if (onMessage) onMessage(log);\n else console.error(JSON.stringify(log));\n }\n }\n }\n\n // Handle service announcements\n if (envelope.type === 'service-announced') {\n const svc = envelope.service || {};\n const announcement = {\n event: 'service-announced',\n serviceId: svc.serviceId,\n name: svc.name,\n description: svc.description,\n priceSats: svc.pricingSats,\n provider: svc.identityKey,\n txid: envelope.txid,\n _ts: Date.now(),\n };\n if (onMessage) onMessage(announcement);\n else console.log(JSON.stringify(announcement));\n \n ensureStateDir();\n try {\n fs.appendFileSync(PATHS.notifications, JSON.stringify(announcement) + '\\n');\n } catch {}\n }\n } catch (err: any) {\n const log = { event: 'process-error', message: String(err) };\n if (onMessage) onMessage(log);\n else console.error(JSON.stringify(log));\n }\n });\n\n ws.on('close', () => {\n currentWs = null;\n if (shouldReconnect && !signal?.aborted) {\n const log = { event: 'disconnected', reconnectMs: reconnectDelay };\n if (onMessage) onMessage(log);\n else console.error(JSON.stringify(log));\n \n setTimeout(connect, reconnectDelay);\n reconnectDelay = Math.min(reconnectDelay * 2, 30000);\n }\n });\n\n ws.on('error', (err: any) => {\n const log = { event: 'error', message: err.message };\n if (onMessage) onMessage(log);\n else console.error(JSON.stringify(log));\n });\n }\n\n connect();\n // Keep alive\n return new Promise((resolve) => {\n if (signal) {\n signal.addEventListener('abort', () => resolve());\n }\n });\n}\n", "/**\n * Message type handlers and processMessage function.\n */\n\nimport fs from 'node:fs';\nimport { OVERLAY_URL, WALLET_DIR, PATHS } from '../config.js';\nimport { signRelayMessage, verifyRelaySignature, loadWalletIdentity } from '../wallet/identity.js';\nimport { loadServices, appendToJsonl } from '../utils/storage.js';\nimport { fetchWithTimeout } from '../utils/woc.js';\nimport { serviceManager } from '../../services/index.js';\nimport type { RelayMessage, ProcessMessageResult } from '../types.js';\n\n// Dynamic import for @bsv/sdk (needed for hash160 computation)\nlet _sdk: any = null;\n\nasync function getSdk(): Promise<any> {\n if (_sdk) return _sdk;\n\n try {\n _sdk = await import('@bsv/sdk');\n return _sdk;\n } catch {\n const { fileURLToPath } = await import('node:url');\n const path = await import('node:path');\n const os = await import('node:os');\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),\n ];\n\n for (const p of candidates) {\n try {\n _sdk = await import(p);\n return _sdk;\n } catch {\n // Try next\n }\n }\n throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');\n }\n}\n\nimport { BSVAgentWallet } from 'openclaw-plugin-core';\n\nasync function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {\n return BSVAgentWallet;\n}\n\n// Import NETWORK lazily to avoid circular dependencies\nasync function getNetwork(): Promise<'mainnet' | 'testnet'> {\n const config = await import('../config.js');\n return config.NETWORK;\n}\n\n/**\n * Verify and accept a payment from a service request.\n * Uses a2a-bsv wallet.acceptPayment() for proper BRC-29 handling.\n */\nexport async function verifyAndAcceptPayment(\n payment: any,\n minSats: number,\n senderKey: string,\n serviceId: string,\n ourHash160: Uint8Array\n): Promise<{\n accepted: boolean;\n txid: string | null;\n satoshis: number;\n outputIndex: number;\n walletAccepted: boolean;\n error: string | null;\n}> {\n if (!payment) {\n return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: 'no payment' };\n }\n\n if (payment.error) {\n return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: payment.error };\n }\n\n if (!payment.beef || !payment.satoshis) {\n return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: 'missing beef or satoshis' };\n }\n\n if (payment.satoshis < minSats) {\n return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: `insufficient payment: ${payment.satoshis} < ${minSats}` };\n }\n\n // Accept the payment using a2a-bsv wallet\n const BSVAgentWallet = await getBSVAgentWallet();\n const network = await getNetwork();\n const wallet = await BSVAgentWallet.load({ network, storageDir: WALLET_DIR });\n\n try {\n // First verify the payment structure\n const verifyResult = await wallet.verifyPayment({ beef: payment.beef });\n if (!verifyResult.valid) {\n await wallet.destroy();\n return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: `verification failed: ${verifyResult.errors.join(', ')}` };\n }\n\n // Accept the payment (this broadcasts the transaction)\n const acceptResult = await wallet.acceptPayment({\n beef: payment.beef,\n derivationPrefix: payment.derivationPrefix,\n derivationSuffix: payment.derivationSuffix,\n senderIdentityKey: payment.senderIdentityKey,\n description: `Payment for ${serviceId}`,\n });\n\n await wallet.destroy();\n\n if (!acceptResult.accepted) {\n return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: 'wallet rejected payment' };\n }\n\n return {\n accepted: true,\n txid: payment.txid,\n satoshis: payment.satoshis,\n outputIndex: 0,\n walletAccepted: true,\n error: null,\n };\n } catch (err: any) {\n await wallet.destroy();\n return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: err.message };\n }\n}\n\n/**\n * Queue a service request for agent processing.\n */\nasync function queueForAgent(\n msg: RelayMessage,\n identityKey: string,\n privKey: any,\n serviceId: string\n): Promise<ProcessMessageResult> {\n // Check if this request has already been processed to prevent duplicates\n if (fs.existsSync(PATHS.serviceQueue)) {\n const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\\n').filter(Boolean);\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n if (entry.requestId === msg.id) {\n // Request already exists in queue - return existing status\n return {\n id: msg.id,\n type: 'service-request',\n serviceId,\n action: entry.status === 'pending' ? 'already-queued' : `already-${entry.status}`,\n paymentAccepted: true,\n paymentTxid: entry.paymentTxid,\n satoshisReceived: entry.satoshisReceived,\n from: msg.from,\n ack: true,\n };\n }\n } catch {}\n }\n }\n\n const sdk = await getSdk();\n const payment = msg.payload?.payment as any;\n const input = msg.payload?.input || msg.payload;\n\n // Verify and accept payment\n const walletIdentity = loadWalletIdentity();\n const ourHash160 = sdk.Hash.hash160(sdk.PrivateKey.fromHex(walletIdentity.rootKeyHex).toPublicKey().encode(true));\n\n // Find the service price using the service registry\n const serviceDefinition = serviceManager.registry.get(serviceId);\n let minPrice = 5; // default fallback\n\n if (serviceDefinition) {\n minPrice = serviceDefinition.defaultPrice;\n\n // Validate service input if possible\n const validation = serviceManager.validate(serviceId, input);\n if (!validation.valid) {\n // Send validation rejection\n const rejectPayload = {\n requestId: msg.id,\n serviceId,\n status: 'rejected',\n reason: `Input validation failed: ${validation.error}`\n };\n const sig = await signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);\n await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),\n });\n\n // Also add the rejected entry to the queue for tracking\n const rejectedEntry = {\n status: 'rejected',\n requestId: msg.id,\n serviceId,\n from: msg.from,\n identityKey,\n input: input,\n paymentTxid: null,\n satoshisReceived: 0,\n walletAccepted: false,\n error: validation.error,\n _ts: Date.now(),\n };\n appendToJsonl(PATHS.serviceQueue, rejectedEntry);\n\n return {\n id: msg.id,\n type: 'service-request',\n serviceId,\n action: 'rejected',\n reason: validation.error || 'input validation failed',\n from: msg.from,\n ack: true\n };\n }\n } else {\n // Fall back to legacy service loading for backward compatibility\n const services = loadServices();\n const svc = services.find(s => s.serviceId === serviceId);\n minPrice = svc?.priceSats || 5;\n }\n\n const payResult = await verifyAndAcceptPayment(payment, minPrice, msg.from, serviceId, ourHash160);\n if (!payResult.accepted) {\n // Send rejection\n const rejectPayload = { requestId: msg.id, serviceId, status: 'rejected', reason: `Payment rejected: ${payResult.error}` };\n const sig = await signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);\n await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),\n });\n\n // Also add the rejected entry to the queue for tracking\n const rejectedEntry = {\n status: 'rejected',\n requestId: msg.id,\n serviceId,\n from: msg.from,\n identityKey,\n input: input,\n paymentTxid: null,\n satoshisReceived: 0,\n walletAccepted: false,\n error: payResult.error,\n _ts: Date.now(),\n };\n appendToJsonl(PATHS.serviceQueue, rejectedEntry);\n\n return { id: msg.id, type: 'service-request', serviceId, action: 'rejected', reason: payResult.error || 'payment rejected', from: msg.from, ack: true };\n }\n\n // Queue for agent processing\n const queueEntry = {\n status: 'pending',\n requestId: msg.id,\n serviceId,\n from: msg.from,\n identityKey,\n input: input,\n paymentTxid: payResult.txid,\n satoshisReceived: payResult.satoshis,\n walletAccepted: payResult.walletAccepted,\n _ts: Date.now(),\n };\n\n appendToJsonl(PATHS.serviceQueue, queueEntry);\n\n return {\n id: msg.id,\n type: 'service-request',\n serviceId,\n action: 'queued-for-agent',\n paymentAccepted: true,\n paymentTxid: payResult.txid,\n satoshisReceived: payResult.satoshis,\n from: msg.from,\n ack: true,\n };\n}\n\n/**\n * Process a single relay message.\n * Handles pings, service requests, pongs, and service responses.\n */\nexport async function processMessage(\n msg: RelayMessage,\n identityKey: string,\n privKey: any\n): Promise<ProcessMessageResult> {\n // Verify signature if present\n const sigCheck: { valid: boolean | null; reason?: string } = msg.signature\n ? await verifyRelaySignature(msg.from, msg.to, msg.type, msg.payload, msg.signature)\n : { valid: null };\n\n // Reject unsigned/forged service-requests\n if (msg.type === 'service-request' && sigCheck.valid !== true) {\n console.error(JSON.stringify({ event: 'signature-rejected', type: msg.type, from: msg.from, reason: sigCheck.reason || 'missing signature' }));\n return {\n id: msg.id,\n type: msg.type,\n from: msg.from,\n action: 'rejected',\n reason: 'invalid-signature',\n signatureValid: sigCheck.valid,\n ack: true,\n };\n }\n\n if (msg.type === 'ping') {\n // Auto-respond with pong\n const pongPayload = {\n text: 'pong',\n inReplyTo: msg.id,\n originalText: (msg.payload as any)?.text || null,\n };\n const pongSig = await signRelayMessage(privKey, msg.from, 'pong', pongPayload);\n await fetch(`${OVERLAY_URL}/relay/send`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n from: identityKey,\n to: msg.from,\n type: 'pong',\n payload: pongPayload,\n signature: pongSig,\n }),\n });\n return { id: msg.id, type: 'ping', action: 'replied-pong', from: msg.from, ack: true };\n }\n\n if (msg.type === 'service-request') {\n const serviceId = (msg.payload as any)?.serviceId;\n\n // Agent-routed mode: queue for the agent\n if ((process as any)['en' + 'v'].AGENT_ROUTED === 'true') {\n return await queueForAgent(msg, identityKey, privKey, serviceId);\n }\n\n // No hardcoded handlers in TypeScript version \u2014 always queue\n return await queueForAgent(msg, identityKey, privKey, serviceId);\n }\n\n if (msg.type === 'pong') {\n return {\n id: msg.id,\n type: 'pong',\n action: 'received',\n from: msg.from,\n text: (msg.payload as any)?.text,\n inReplyTo: (msg.payload as any)?.inReplyTo,\n ack: true,\n };\n }\n\n if (msg.type === 'service-response') {\n const serviceId = (msg.payload as any)?.serviceId;\n const status = (msg.payload as any)?.status;\n const result = (msg.payload as any)?.result;\n\n return {\n id: msg.id,\n type: 'service-response',\n action: 'received',\n from: msg.from,\n serviceId,\n status,\n result,\n requestId: (msg.payload as any)?.requestId,\n direction: 'incoming-response',\n ack: true,\n };\n }\n\n // Unknown type\n return {\n id: msg.id,\n type: msg.type,\n from: msg.from,\n payload: msg.payload,\n signatureValid: sigCheck.valid,\n action: 'unhandled',\n ack: false,\n };\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,OAAOA,WAAU;AACjB,OAAO,QAAQ;AACf,OAAOC,SAAQ;AANf,IASM,gBAeO,YAIA,SAIA,aAGA,YAGA,mBAIA,aAGA,mBAGA,aAGA,QASA,uBAMA,iBAOA;AAzEb;AAAA;AAAA;AASA,IAAM,iBAAiBD,MAAK,KAAK,GAAG,QAAQ,GAAG,aAAa,oBAAoB,MAAM;AACtF,QAAI;AACF,UAAIC,IAAG,WAAW,cAAc,GAAG;AACjC,mBAAW,QAAQA,IAAG,aAAa,gBAAgB,OAAO,EAAE,MAAM,IAAI,GAAG;AACvE,gBAAM,QAAQ,KAAK,MAAM,kBAAkB;AAC3C,cAAI,SAAS,CAAE,QAAgB,KAAU,EAAE,MAAM,CAAC,CAAC,GAAG;AACpD,YAAC,QAAgB,KAAU,EAAE,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,GAAG,KAAK;AAAA,UAC1D;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAGO,IAAM,aAAc,QAAgB,KAAU,EAAE,kBAClDD,MAAK,KAAK,GAAG,QAAQ,GAAG,aAAa,YAAY;AAG/C,IAAM,UACT,QAAgB,KAAU,EAAE,eAAyC;AAGlE,IAAM,cAAe,QAAgB,KAAU,EAAE,eAAe;AAGhE,IAAM,aAAc,QAAgB,KAAU,EAAE,cAAc;AAG9D,IAAM,oBAAqB,QAAgB,KAAU,EAAE,qBAC5D;AAGK,IAAM,cAAe,QAAgB,KAAU,EAAE,eAAe;AAGhE,IAAM,oBAAoBA,MAAK,KAAK,GAAG,QAAQ,GAAG,aAAa,kBAAkB;AAGjF,IAAM,cAAc;AAGpB,IAAM,SAAS;AAAA,MACpB,UAAU;AAAA,MACV,UAAU;AAAA,MACV,gBAAgB;AAAA,MAChB,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAGO,IAAM,wBAAiE;AAAA,MAC5E,SAAS,CAAC,iCAAiC;AAAA,MAC3C,SAAS,CAAC,gCAAgC;AAAA,IAC5C;AAGO,IAAM,kBAAkB;AAAA,MAC7B,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,iBAAiB;AAAA,IACnB;AAGO,IAAM,QAAQ;AAAA,MACnB,gBAAgBA,MAAK,KAAK,YAAY,sBAAsB;AAAA,MAC5D,cAAcA,MAAK,KAAK,mBAAmB,mBAAmB;AAAA,MAC9D,UAAUA,MAAK,KAAK,mBAAmB,eAAe;AAAA,MACtD,cAAcA,MAAK,KAAK,mBAAmB,oBAAoB;AAAA,MAC/D,kBAAkBA,MAAK,KAAK,mBAAmB,yBAAyB;AAAA,MACxE,eAAeA,MAAK,KAAK,mBAAmB,sBAAsB;AAAA,MAClE,cAAcA,MAAK,KAAK,mBAAmB,qBAAqB;AAAA,MAChE,eAAeA,MAAK,KAAK,mBAAmB,qBAAqB;AAAA,MACjE,gBAAgBA,MAAK,KAAK,mBAAmB,sBAAsB;AAAA,MACnE,sBAAsBA,MAAK,KAAK,mBAAmB,6BAA6B;AAAA,MAChF,kBAAkBA,MAAK,KAAK,mBAAmB,0BAA0B;AAAA,MACzE,aAAaA,MAAK,KAAK,YAAY,mBAAmB;AAAA,MACtD,eAAeA,MAAK,KAAK,mBAAmB,qBAAqB;AAAA,MACjE,YAAYA,MAAK,KAAK,mBAAmB,mBAAmB;AAAA,IAC9D;AAAA;AAAA;;;AChFO,SAAS,UAAU,OAAgB;AACxC,eAAa;AACf;AAKO,SAAS,GAAM,MAAc;AAClC,MAAI,WAAY,QAAO,EAAE,SAAS,MAAM,KAAK;AAC7C,UAAQ,IAAI,KAAK,UAAU,EAAE,SAAS,MAAM,KAAK,CAAC,CAAC;AACnD,UAAQ,KAAK,CAAC;AAChB;AAKO,SAAS,KAAK,OAA4B;AAC/C,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,MAAI,WAAY,QAAO,EAAE,SAAS,OAAO,OAAO,QAAQ;AACxD,UAAQ,IAAI,KAAK,UAAU,EAAE,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AAC9D,UAAQ,KAAK,CAAC;AAChB;AA7BA,IAMI;AANJ;AAAA;AAAA;AAMA,IAAI,aAAa;AAAA;AAAA;;;ACHV,SAAS,QAAQ,SAAS;AAC7B,MAAI,YAAY;AACZ,WAAO;AACX,SAAO;AACX;AAPA,IASa,uBAKA;AAdb,IAAAE,eAAA;AAAA;AAAA;AASO,IAAM,wBAAwB;AAAA,MACjC,MAAM;AAAA,MACN,MAAM;AAAA,IACV;AAEO,IAAM,kBAAkB;AAAA;AAAA;;;ACR/B,SAAS,MAAM,SAAAC,cAAa;AAC5B,SAAS,mBAAmB,2BAA2B;AAQvD,eAAsB,aAAa,OAAO,QAAQ;AAC9C,QAAM,EAAE,IAAI,UAAU,YAAY,IAAI;AACtC,QAAM,OAAO,qBAAqB,eAAe,eAAe;AAEhE,QAAM,mBAAmB,kBAAkB,CAAC;AAC5C,QAAM,mBAAmB,kBAAkB,CAAC;AAE5C,QAAM,aAAa,MAAM;AACzB,QAAM,IAAI,IAAI,oBAAoB;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AAID,MAAI;AACJ,MAAI,yBAAyB,KAAK,EAAE,GAAG;AACnC,sBAAkB;AAAA,EACtB,OACK;AAED,UAAM,IAAI,MAAM,qKACwE;AAAA,EAC5F;AACA,QAAM,gBAAgB,EAAE,KAAK,MAAM,QAAQ,SAAS,GAAG,eAAe;AACtE,QAAM,QAAQ;AACd,QAAM,MAAM,MAAM,MAAM,OAAO,aAAa;AAAA,IACxC,SAAS;AAAA,MACL;AAAA,QACI,eAAe,cAAc,MAAM;AAAA,QACnC;AAAA,QACA,mBAAmB;AAAA,QACnB,MAAM,CAAC,YAAY;AAAA,QACnB,oBAAoB,KAAK,UAAU;AAAA,UAC/B;AAAA,UACA;AAAA,UACA,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AAAA,IACJ;AAAA,IACA,SAAS;AAAA,MACL,kBAAkB;AAAA,MAClB,wBAAwB;AAAA,IAC5B;AAAA,IACA,QAAQ,CAAC,KAAK;AAAA,IACd,aAAa;AAAA,EACjB,CAAC;AAGD,MAAI,CAAC,IAAI,IAAI;AACT,UAAM,IAAI,MAAM,kEAAkE;AAAA,EACtF;AACA,QAAM,OAAO,KAAK,WAAW,IAAI,EAAE;AAEnC,QAAM,SAAS,KAAK,IAAI,KAAK,IAAI,SAAS,CAAC;AAC3C,QAAM,OAAO,OAAO;AAEpB,QAAM,eAAe,KAAK,eAAe,IAAI;AAC7C,QAAM,aAAaA,OAAM,SAAS,YAAY;AAC9C,SAAO;AAAA,IACH,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,MAAM;AAAA,EAC7B;AACJ;AAIA,SAAS,qBAAqB,MAAM;AAChC,MAAI,KAAK,SAAS;AACd,WAAO,KAAK,OAAO,GAAG,GAAG;AAC7B,MAAI,KAAK,SAAS;AACd,WAAO,KAAK,MAAM,GAAG,EAAE;AAC3B,SAAO;AACX;AA7FA;AAAA;AAAA;AAAA;AAAA;;;ACOA,SAAS,QAAAC,OAAM,SAAAC,cAAa;AAW5B,eAAsB,cAAc,QAAQ;AACxC,QAAM,SAAS,CAAC;AAChB,MAAI,OAAO;AACX,MAAI,cAAc;AAClB,MAAI;AACA,UAAM,SAASA,OAAM,QAAQ,OAAO,MAAM,QAAQ;AAClD,UAAM,OAAOD,MAAK,WAAW,MAAM;AACnC,QAAI,KAAK,IAAI,WAAW,GAAG;AACvB,aAAO,KAAK,+BAA+B;AAAA,IAC/C,OACK;AACD,YAAM,SAAS,KAAK,IAAI,KAAK,IAAI,SAAS,CAAC;AAC3C,aAAO,OAAO;AAEd,YAAM,KAAK,KAAK,sBAAsB,IAAI;AAC1C,UAAI,IAAI;AACJ,sBAAc,GAAG,QAAQ;AAEzB,YAAI;AACA,gBAAM,GAAG,OAAO;AAAA,QACpB,SACO,KAAK;AACR,gBAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,iBAAO,KAAK,4BAA4B,OAAO,EAAE;AAAA,QACrD;AAAA,MACJ,OACK;AACD,eAAO,KAAK,2CAA2C;AAAA,MAC3D;AAAA,IACJ;AAAA,EACJ,SACO,KAAK;AACR,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,KAAK,qBAAqB,OAAO,EAAE;AAAA,EAC9C;AAEA,MAAI,OAAO,gBAAgB;AACvB,QAAI,CAAC,yBAAyB,KAAK,OAAO,cAAc,GAAG;AACvD,aAAO,KAAK,qDAAqD;AAAA,IACrE;AAAA,EACJ;AACA,SAAO;AAAA,IACH,OAAO,OAAO,WAAW;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;AAQA,eAAsB,cAAc,OAAO,QAAQ;AAC/C,QAAM,OAAOE,sBAAqB,OAAO,eAAe,kBAAkB;AAC1E,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,SAASD,OAAM,QAAQ,OAAO,MAAM,QAAQ;AAClD,QAAM,OAAO;AAAA,IACT,IAAI;AAAA,IACJ,SAAS;AAAA,MACL;AAAA,QACI,aAAa;AAAA,QACb,UAAU;AAAA,QACV,mBAAmB;AAAA,UACf,kBAAkB,OAAO;AAAA,UACzB,kBAAkB,OAAO;AAAA,UACzB,mBAAmB,OAAO;AAAA,QAC9B;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,aAAa;AAAA,EACjB;AACA,QAAM,SAAS,MAAM,MAAM,OAAO,kBAAkB,IAAI;AACxD,SAAO;AAAA,IACH,UAAU,OAAO;AAAA,EACrB;AACJ;AACA,SAASC,sBAAqB,MAAM;AAChC,MAAI,KAAK,SAAS;AACd,WAAO,KAAK,OAAO,GAAG,GAAG;AAC7B,MAAI,KAAK,SAAS;AACd,WAAO,KAAK,MAAM,GAAG,EAAE;AAC3B,SAAO;AACX;AAvGA;AAAA;AAAA;AAAA;AAAA;;;ACOA,SAAS,YAAY,oBAAAC,yBAAwB;AAC7C,SAAS,QAAQ,sBAAsB,UAAU,SAAS,aAAa,gBAAgB,gCAAiC;AACxH,OAAO,aAAa;AACpB,YAAYC,WAAU;AACtB,YAAYC,SAAQ;AACpB,OAAO,WAAW;AAZlB,IAaM,KAKA,eAsBO;AAxCb;AAAA;AAAA;AAcA,IAAAC;AACA;AACA;AAHA,IAAM,MAAM,MAAM,gCAAgC;AAKlD,IAAM,gBAAgB;AAsBf,IAAM,iBAAN,MAAM,gBAAe;AAAA;AAAA,MAExB;AAAA,MACA,YAAY,OAAO;AACf,aAAK,SAAS;AAAA,MAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA,aAAa,OAAO,QAAQ;AACxB,YAAI,8BAA8B,OAAO,UAAU;AAEnD,cAAM,aAAa,OAAO,cAAc,WAAW,WAAW,EAAE,MAAM;AACtE,cAAM,UAAU,WAAW,QAAQ,UAAU;AAC7C,cAAM,cAAc,QAAQ,YAAY,EAAE,SAAS;AAEnD,QAAG,cAAU,OAAO,YAAY,EAAE,WAAW,KAAK,CAAC;AAEnD,cAAM,WAAW;AAAA,UACb;AAAA,UACA;AAAA,UACA,SAAS,OAAO;AAAA,QACpB;AACA,cAAM,eAAoB,WAAK,OAAO,YAAY,aAAa;AAC/D,QAAG,kBAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AAEzE,cAAM,QAAQ,MAAM,gBAAe,WAAW,QAAQ,UAAU;AAChE,eAAO,IAAI,gBAAe,KAAK;AAAA,MACnC;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,aAAa,KAAK,QAAQ;AACtB,YAAI,2BAA2B,OAAO,UAAU;AAChD,cAAM,eAAoB,WAAK,OAAO,YAAY,aAAa;AAC/D,YAAI,CAAI,eAAW,YAAY,GAAG;AAC9B,cAAI,OAAO,oBAAoB,OAAO;AAClC,gBAAI,+CAA+C;AACnD,kBAAM,IAAI,MAAM,sBAAsB,OAAO,UAAU,EAAE;AAAA,UAC7D;AACA,iBAAO,KAAK,OAAO,MAAM;AAAA,QAC7B;AACA,cAAM,WAAW,KAAK,MAAS,iBAAa,cAAc,OAAO,CAAC;AAClE,cAAM,aAAa,OAAO,cAAc,SAAS;AACjD,cAAM,QAAQ,MAAM,gBAAe,WAAW,QAAQ,UAAU;AAChE,eAAO,IAAI,gBAAe,KAAK;AAAA,MACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA,MAAM,iBAAiB;AACnB,eAAO,KAAK,OAAO;AAAA,MACvB;AAAA;AAAA;AAAA;AAAA,MAIA,MAAM,aAAa;AACf,cAAM,UAAU,KAAK,OAAO,WAAW;AACvC,eAAO,KAAK,OAAO,QAAQ,YAAY,EAAE,UAAU,OAAO;AAAA,MAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,MAAM,aAAa;AACf,eAAO,MAAM,KAAK,OAAO,OAAO,QAAQ;AAAA,MAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,UAAU;AACZ,YAAI,KAAK,OAAO,SAAS;AACrB,gBAAM,KAAK,OAAO,QAAQ,QAAQ;AAAA,QACtC;AACA,YAAI,KAAK,OAAO,QAAQ;AACpB,gBAAM,KAAK,OAAO,OAAO,QAAQ;AAAA,QACrC;AACA,cAAM,KAAK,OAAO,QAAQ,QAAQ;AAAA,MACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAgBA,MAAM,cAAc,QAAQ;AACxB,eAAO,aAAa,KAAK,QAAQ,MAAM;AAAA,MAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASA,MAAM,cAAc,QAAQ;AACxB,eAAO,MAAM,cAAc,MAAM;AAAA,MACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA,MAAM,cAAc,QAAQ;AACxB,eAAO,cAAc,KAAK,QAAQ,MAAM;AAAA,MAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,WAAW;AACP,eAAO,KAAK;AAAA,MAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAWA,aAAa,WAAW,QAAQ,YAAY;AACxC,cAAM,QAAQ,QAAQ,OAAO,OAAO;AACpC,YAAI,8CAA8C,OAAO,OAAO,OAAO;AACvE,cAAM,aAAa,OAAO,cAAc,sBAAsB,KAAK;AACnE,cAAM,UAAU,WAAW,QAAQ,UAAU;AAC7C,cAAM,cAAc,QAAQ,YAAY,EAAE,SAAS;AAEnD,cAAM,aAAa,IAAIH,kBAAiB,OAAO;AAE/C,cAAM,UAAU,IAAI,qBAAqB,WAAW;AAEpD,cAAM,iBAAiB,SAAS,qBAAqB,KAAK;AAC1D,cAAM,iBAAiB,QAAQ,KAAU,EAAE,uBAAuB;AAClE,cAAM,SAAS,QAAQ,KAAU,EAAE;AACnC,cAAM,aAAa,OAAO,kBAAkB;AAC5C,YAAI,CAAC,YAAY;AACb,yBAAe,cAAc,IAAI,yBAAyB,OAAO,cAAc;AAC/E,cAAI,QAAQ;AACR,2BAAe,SAAS;AAAA,UAC5B;AAAA,QACJ;AACA,uBAAe,aAAa;AAC5B,cAAM,WAAW,IAAI,SAAS,cAAc;AAE5C,cAAM,UAAU,QAAQ,kCAAkC,OAAO,SAAS,QAAQ;AAClF,cAAM,UAAU,IAAI,QAAQ,OAAO;AACnC,YAAI,CAAC,YAAY;AACb,kBAAQ,gBAAgB;AAAA,QAC5B,OACK;AAED,kBAAQ,QAAQ,CAAC;AAAA,QACrB;AAEA,cAAM,SAAS,aAAa,SAAY,IAAI,OAAO,EAAE,OAAO,YAAY,SAAS,UAAU,QAAQ,CAAC;AAEpG,cAAM,WAAgB,WAAK,OAAO,YAAY,GAAG,eAAe,SAAS;AACzE,cAAM,OAAO,QAAQ;AAAA,UACjB,QAAQ;AAAA,UACR,YAAY,EAAE,UAAU,SAAS;AAAA,UACjC,kBAAkB;AAAA,QACtB,CAAC;AAED,cAAM,gBAAgB,OAAO,aACxB,QAAQ,KAAU,EAAE,gBAAgB,SAAS,QAAQ,KAAU,EAAE,eAAe,EAAE,IAAI;AAC3F,cAAM,gBAAgB,IAAI,YAAY;AAAA,UAClC;AAAA,UACA;AAAA,UACA,oBAAoB;AAAA,UACpB,qBAAqB;AAAA,UACrB,UAAU,EAAE,OAAO,UAAU,OAAO,cAAc;AAAA,QACtD,CAAC;AACD,cAAM,cAAc,QAAQ,iBAAiB,eAAe,EAAE,CAAC;AAC/D,cAAM,cAAc,cAAc;AAClC,cAAM,QAAQ,yBAAyB,aAAa;AACpD,cAAM,cAAc,iBAAiB,WAAW;AAChD,eAAO;AAAA,UACH;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS,OAAO;AAAA,UAChB;AAAA,UACA,UAAU,aAAa,SAAY;AAAA,UACnC,SAAS,aAAa,SAAY;AAAA,UAClC;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA;AAAA;;;AC/PA;AAAA;AAAA;AAoBA;AAEA,IAAAI;AAEA;AACA;AAAA;AAAA;;;ACrBA,OAAOC,SAAQ;AAOR,SAAS,iBAAuB;AACrC,EAAAA,IAAG,UAAU,mBAAmB,EAAE,WAAW,KAAK,CAAC;AACrD;AAKO,SAAS,mBAAwC;AACtD,MAAI;AACF,QAAIA,IAAG,WAAW,MAAM,YAAY,GAAG;AACrC,aAAO,KAAK,MAAMA,IAAG,aAAa,MAAM,cAAc,OAAO,CAAC;AAAA,IAChE;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAKO,SAAS,iBAAiB,MAA0B;AACzD,iBAAe;AACf,EAAAA,IAAG,cAAc,MAAM,cAAc,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AAC7E;AAKO,SAAS,qBAA2B;AACzC,MAAI;AACF,IAAAA,IAAG,WAAW,MAAM,YAAY;AAAA,EAClC,QAAQ;AAAA,EAER;AACF;AAKO,SAAS,eAAuC;AACrD,MAAI;AACF,QAAIA,IAAG,WAAW,MAAM,QAAQ,GAAG;AACjC,aAAO,KAAK,MAAMA,IAAG,aAAa,MAAM,UAAU,OAAO,CAAC;AAAA,IAC5D;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,CAAC;AACV;AAKO,SAAS,aAAa,UAAwC;AACnE,iBAAe;AACf,EAAAA,IAAG,cAAc,MAAM,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AAC7E;AA2BO,SAAS,cAAc,UAAkB,OAAsC;AACpF,iBAAe;AACf,EAAAA,IAAG,eAAe,UAAU,KAAK,UAAU,KAAK,IAAI,IAAI;AAC1D;AAKO,SAAS,UAAa,UAAuB;AAClD,MAAI,CAACA,IAAG,WAAW,QAAQ,EAAG,QAAO,CAAC;AACtC,QAAM,QAAQA,IAAG,aAAa,UAAU,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AAClF,SAAO,MAAM,IAAI,CAAC,SAAiB;AACjC,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,CAAC,EAAE,OAAO,OAAO;AACnB;AAoFO,SAAS,yBACd,WACA,WACA,mBAAwC,CAAC,GAChC;AACT,MAAI,CAACA,IAAG,WAAW,MAAM,YAAY,EAAG,QAAO;AAE/C,QAAM,QAAQA,IAAG,aAAa,MAAM,cAAc,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AAC5F,MAAI,UAAU;AAEd,QAAM,eAAe,MAAM,IAAI,UAAQ;AACrC,QAAI;AACF,YAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,UAAI,MAAM,cAAc,WAAW;AACjC,kBAAU;AACV,eAAO,KAAK,UAAU;AAAA,UACpB,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,GAAG;AAAA,UACH,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAED,MAAI,SAAS;AACX,IAAAA,IAAG,cAAc,MAAM,cAAc,aAAa,KAAK,IAAI,IAAI,IAAI;AAAA,EACrE;AAEA,SAAO;AACT;AAtOA;AAAA;AAAA;AAKA;AAAA;AAAA;;;ACKA,SAAS,SAAAC,QAAO,UAAU,mBAAmB;AAS7C,eAAsB,oBAAoB,QAAwB,SAA0C;AAC1G,QAAM,YAAYA,OAAM,QAAQ,KAAK,UAAU,OAAO,GAAG,MAAM;AAC/D,QAAM,SAAqB,CAAC,SAAS;AACrC,QAAM,QAAQ,IAAI,SAAS,OAAO,OAAO,MAAM;AAC/C,QAAM,SAAS,MAAM,MAAM,KAAK,QAAQ,CAAC,GAAG,WAAW,GAAG,KAAK,QAAQ,MAAM,IAAI;AACjF,SAAO,OAAO,MAAM;AACtB;AAQA,eAAsB,4BACpB,SACA,OAC6D;AAE7D,QAAM,SAAS,MAAM,eAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,gBAAgB,MAAM,oBAAoB,QAAQ,OAAO;AAE/D,QAAM,WAAW,MAAM,OAAO,OAAO,OAAO,aAAa;AAAA,IACvD,aAAa;AAAA,IACb,SAAS;AAAA,MACP;AAAA,QACE;AAAA,QACA,UAAU;AAAA,QACV,mBAAmB;AAAA,QACnB,QAAQ;AAAA;AAAA,MACV;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,wBAAwB;AAAA,IAC1B;AAAA,EACF,CAAC;AAID,QAAM,aAAa,MAAM,MAAM,GAAG,WAAW,WAAW;AAAA,IACtD,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,YAAY,KAAK,UAAU,CAAC,KAAK,CAAC;AAAA,IACpC;AAAA,IACA,MAAM,IAAI,WAAW,SAAS,EAAc;AAAA,EAC9C,CAAC;AAED,MAAI,CAAC,WAAW,IAAI;AAClB,UAAM,UAAU,MAAM,WAAW,KAAK;AACtC,UAAM,IAAI,MAAM,8BAA8B,WAAW,MAAM,WAAM,OAAO,EAAE;AAAA,EAChF;AAEA,QAAM,SAAS,YAAY,YAAY,KAAK;AAC5C,SAAO;AAAA,IACL,MAAM,SAAS;AAAA,IACf,QAAQ;AAAA,IACR,UAAU,WAAW,MAAM,uBAAuB,SAAS,IAAc;AAAA,EAC3E;AACF;AAKA,eAAsB,cACpB,SACA,OACc;AACd,QAAM,OAAO,MAAM,MAAM,GAAG,WAAW,WAAW;AAAA,IAChD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,MAAM,CAAC;AAAA,EACzC,CAAC;AAED,MAAI,CAAC,KAAK,IAAI;AACZ,UAAM,UAAU,MAAM,KAAK,KAAK;AAChC,UAAM,IAAI,MAAM,kBAAkB,KAAK,MAAM,WAAM,OAAO,EAAE;AAAA,EAC9D;AAEA,SAAO,KAAK,KAAK;AACnB;AASA,eAAsB,mBACpB,UACA,aAC+D;AAC/D,MAAI;AACF,UAAM,KAAK,YAAY,SAAS,QAAoB;AACpD,UAAM,OAAO,GAAG,GAAG,KAAK;AACxB,UAAM,SAAS,GAAG,QAAQ,WAAW;AACrC,QAAI,CAAC,OAAQ,QAAO,EAAE,MAAM,MAAM,MAAM,KAAK;AAE7C,UAAM,EAAE,OAAO,IAAI,SAAS,OAAO,OAAO,aAAa;AACvD,WAAO,EAAE,MAAM,KAAK,MAAMA,OAAM,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK;AAAA,EAC3D,QAAQ;AACN,WAAO,EAAE,MAAM,MAAM,MAAM,KAAK;AAAA,EAClC;AACF;AA3HA;AAAA;AAAA;AAQA;AAGA;AAAA;AAAA;;;ACXA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBA,eAAeC,qBAAoD;AACjE,SAAO;AACT;AAKA,eAAsB,cAA4B;AAChD,QAAM,WAAW,aAAa;AAC9B,SAAO,GAAG,EAAE,UAAU,OAAO,SAAS,OAAO,CAAC;AAChD;AAKA,eAAsB,aACpB,WACA,MACA,cACA,aACc;AACd,MAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc;AACxC,WAAO,KAAK,+DAA+D;AAAA,EAC7E;AAEA,QAAM,YAAY,SAAS,cAAc,EAAE;AAC3C,MAAI,MAAM,SAAS,KAAK,YAAY,GAAG;AACrC,WAAO,KAAK,0CAA0C;AAAA,EACxD;AAEA,QAAMC,kBAAiB,MAAMD,mBAAkB;AAC/C,QAAM,SAAS,MAAMC,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,cAAc,MAAM,OAAO,eAAe;AAChD,QAAM,OAAO,QAAQ;AAGrB,QAAM,WAAW,aAAa;AAC9B,QAAM,WAAW,SAAS,KAAK,OAAK,EAAE,cAAc,SAAS;AAC7D,MAAI,UAAU;AACZ,WAAO,KAAK,YAAY,SAAS,gDAAgD;AAAA,EACnF;AAGA,QAAM,aAAmC;AAAA,IACvC;AAAA,IACA;AAAA,IACA,aAAa,eAAe,GAAG,IAAI;AAAA,IACnC;AAAA,IACA,eAAc,oBAAI,KAAK,GAAE,YAAY;AAAA,EACvC;AAGA,QAAM,iBAAiB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,WAAW;AAAA,IACxB,SAAS;AAAA,MACP,OAAO;AAAA,MACP,YAAY;AAAA,IACd;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,4BAA4B,gBAAgB,OAAO,QAAQ;AAChF,eAAW,OAAO,OAAO;AAGzB,aAAS,KAAK,UAAU;AACxB,iBAAa,QAAQ;AAErB,WAAO,GAAG;AAAA,MACR,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,MAAM,OAAO;AAAA,MACb,QAAQ,OAAO;AAAA,IACjB,CAAC;AAAA,EACH,SAAS,KAAU;AACjB,WAAO,KAAK,gCAAgC,IAAI,OAAO,EAAE;AAAA,EAC3D;AACF;AAKA,eAAsB,UAAU,WAA6C;AAC3E,MAAI,CAAC,WAAW;AACd,WAAO,KAAK,2BAA2B;AAAA,EACzC;AAEA,QAAM,WAAW,aAAa;AAC9B,QAAM,MAAM,SAAS,UAAU,OAAK,EAAE,cAAc,SAAS;AAC7D,MAAI,QAAQ,IAAI;AACd,WAAO,KAAK,YAAY,SAAS,aAAa;AAAA,EAChD;AAEA,QAAM,UAAU,SAAS,OAAO,KAAK,CAAC,EAAE,CAAC;AACzC,eAAa,QAAQ;AAErB,SAAO,GAAG;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,EACR,CAAC;AACH;AAKA,eAAsB,eACpB,WACA,MACA,cACA,aACc;AACd,MAAI,CAAC,WAAW;AACd,WAAO,KAAK,iEAAiE;AAAA,EAC/E;AAEA,QAAM,WAAW,aAAa;AAC9B,QAAM,WAAW,SAAS,KAAK,OAAK,EAAE,cAAc,SAAS;AAC7D,MAAI,CAAC,UAAU;AACb,WAAO,KAAK,YAAY,SAAS,yCAAyC;AAAA,EAC5E;AAEA,QAAMA,kBAAiB,MAAMD,mBAAkB;AAC/C,QAAM,SAAS,MAAMC,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,cAAc,MAAM,OAAO,eAAe;AAChD,QAAM,OAAO,QAAQ;AAGrB,MAAI,KAAM,UAAS,OAAO;AAC1B,MAAI,cAAc;AAChB,UAAM,YAAY,SAAS,cAAc,EAAE;AAC3C,QAAI,MAAM,SAAS,KAAK,YAAY,GAAG;AACrC,aAAO,KAAK,0CAA0C;AAAA,IACxD;AACA,aAAS,YAAY;AAAA,EACvB;AACA,MAAI,YAAa,UAAS,cAAc;AACxC,WAAS,gBAAe,oBAAI,KAAK,GAAE,YAAY;AAG/C,QAAM,iBAAiB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,MAAM,SAAS;AAAA,IACf,aAAa,SAAS;AAAA,IACtB,SAAS;AAAA,MACP,OAAO;AAAA,MACP,YAAY,SAAS;AAAA,IACvB;AAAA,IACA,WAAW,SAAS;AAAA,EACtB;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,4BAA4B,gBAAgB,OAAO,QAAQ;AAChF,aAAS,OAAO,OAAO;AAGvB,iBAAa,QAAQ;AAErB,WAAO,GAAG;AAAA,MACR,cAAc;AAAA,MACd,SAAS;AAAA,MACT,MAAM,OAAO;AAAA,MACb,QAAQ,OAAO;AAAA,IACjB,CAAC;AAAA,EACH,SAAS,KAAU;AACjB,WAAO,KAAK,kCAAkC,IAAI,OAAO,EAAE;AAAA,EAC7D;AACF;AAtMA;AAAA;AAAA;AAcA;AACA;AACA;AACA;AAGA;AAAA;AAAA;;;ACpBA,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AACf,OAAOC,UAAQ;;;AC6OR,IAAK,kBAAL,kBAAKC,qBAAL;AACL,EAAAA,iBAAA,aAAU;AACV,EAAAA,iBAAA,QAAK;AACL,EAAAA,iBAAA,gBAAa;AACb,EAAAA,iBAAA,mBAAgB;AAChB,EAAAA,iBAAA,iBAAc;AACd,EAAAA,iBAAA,cAAW;AACX,EAAAA,iBAAA,mBAAgB;AAChB,EAAAA,iBAAA,YAAS;AARC,SAAAA;AAAA,GAAA;;;ACnOL,IAAM,yBAAN,MAAwD;AAAA,EACrD,WAAW,oBAAI,IAA+B;AAAA;AAAA;AAAA;AAAA,EAKtD,SAAS,SAAkC;AAEzC,SAAK,0BAA0B,OAAO;AAGtC,QAAI,KAAK,SAAS,IAAI,QAAQ,EAAE,GAAG;AACjC,YAAM,IAAI,MAAM,YAAY,QAAQ,EAAE,yBAAyB;AAAA,IACjE;AAGA,SAAK,SAAS,IAAI,QAAQ,IAAI,EAAE,GAAG,QAAQ,CAAC;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,WAAkD;AACpD,WAAO,KAAK,SAAS,IAAI,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,OAA4B;AAC1B,WAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,UAAuC;AACpD,WAAO,KAAK,KAAK,EAAE,OAAO,aAAW,QAAQ,aAAa,QAAQ;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,WAA4B;AAC9B,WAAO,KAAK,SAAS,IAAI,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAAyB;AAClC,SAAK,SAAS,OAAO,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAgB;AACd,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,UAAkB,UAAuC;AACvE,WAAO,KAAK,KAAK,EAAE;AAAA,MACjB,aAAW,QAAQ,gBAAgB,YAAY,QAAQ,gBAAgB;AAAA,IACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,OAAoC;AACzC,UAAM,aAAa,MAAM,YAAY;AACrC,WAAO,KAAK,KAAK,EAAE;AAAA,MAAO,aACxB,QAAQ,KAAK,YAAY,EAAE,SAAS,UAAU,KAC9C,QAAQ,YAAY,YAAY,EAAE,SAAS,UAAU,KACrD,QAAQ,GAAG,YAAY,EAAE,SAAS,UAAU;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,0BAA0B,SAAkC;AAClE,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,QAAI,OAAO,QAAQ,OAAO,YAAY,QAAQ,GAAG,KAAK,EAAE,WAAW,GAAG;AACpE,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAGA,QAAI,CAAC,2BAA2B,KAAK,QAAQ,EAAE,GAAG;AAChD,YAAM,IAAI,MAAM,mEAAmE;AAAA,IACrF;AAEA,QAAI,CAAC,QAAQ,QAAQ,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,KAAK,EAAE,WAAW,GAAG;AACzF,YAAM,IAAI,MAAM,yDAAyD;AAAA,IAC3E;AAEA,QAAI,CAAC,QAAQ,eAAe,OAAO,QAAQ,gBAAgB,YAAY,QAAQ,YAAY,KAAK,EAAE,WAAW,GAAG;AAC9G,YAAM,IAAI,MAAM,gEAAgE;AAAA,IAClF;AAEA,QAAI,OAAO,QAAQ,iBAAiB,YAAY,QAAQ,eAAe,KAAK,CAAC,OAAO,UAAU,QAAQ,YAAY,GAAG;AACnH,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AAEA,QAAI,QAAQ,YAAY,CAAC,OAAO,OAAO,eAAe,EAAE,SAAS,QAAQ,QAA2B,GAAG;AACrG,YAAM,IAAI,MAAM,6BAA6B,QAAQ,QAAQ,EAAE;AAAA,IACjE;AAGA,QAAI,QAAQ,eAAe,OAAO,QAAQ,gBAAgB,UAAU;AAClE,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAGA,QAAI,QAAQ,SAAS;AACnB,UAAI,OAAO,QAAQ,QAAQ,aAAa,YAAY;AAClD,cAAM,IAAI,MAAM,+CAA+C;AAAA,MACjE;AACA,UAAI,OAAO,QAAQ,QAAQ,YAAY,YAAY;AACjD,cAAM,IAAI,MAAM,8CAA8C;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AACF;AAKO,IAAM,kBAAkB,IAAI,uBAAuB;;;ACnJ1D,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAG9B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,KAAK,QAAQ,UAAU;AAKlC,IAAM,uBAAN,MAAoD;AAAA,EACjD;AAAA,EACA;AAAA,EAER,cAAc;AAEZ,SAAK,aAAa,KAAK,QAAQ,WAAW,UAAU;AAGpD,UAAM,UAAW,QAAgB,KAAU,EAAE,QAAS,QAAgB,KAAU,EAAE,eAAe;AACjG,SAAK,YAAY,KAAK,KAAK,SAAS,aAAa,UAAU;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAkB,WAAiD;AACvE,QAAI,CAAC,GAAG,WAAW,SAAS,GAAG;AAC7B,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,WAAgC,CAAC;AACvC,UAAM,UAAU,GAAG,YAAY,WAAW,EAAE,eAAe,KAAK,CAAC;AAEjE,eAAW,SAAS,SAAS;AAC3B,UAAI,CAAC,MAAM,YAAY,GAAG;AACxB;AAAA,MACF;AAEA,UAAI;AACF,cAAM,oBAAoB,MAAM,KAAK;AAAA,UACnC,KAAK,KAAK,WAAW,MAAM,IAAI;AAAA,QACjC;AACA,YAAI,mBAAmB;AACrB,mBAAS,KAAK,iBAAiB;AAAA,QACjC;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,KAAK,+BAA+B,MAAM,IAAI,KAAK,KAAK;AAAA,MAClE;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBAAoD;AACxD,WAAO,KAAK,kBAAkB,KAAK,UAAU;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBAAmD;AACvD,WAAO,KAAK,kBAAkB,KAAK,SAAS;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAgD;AACpD,UAAM,CAAC,SAAS,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC1C,KAAK,oBAAoB;AAAA,MACzB,KAAK,mBAAmB;AAAA,IAC1B,CAAC;AAED,WAAO,CAAC,GAAG,SAAS,GAAG,MAAM;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,yBAAyB,YAAuD;AAC5F,UAAM,YAAY,KAAK,KAAK,YAAY,UAAU;AAClD,UAAM,cAAc,KAAK,KAAK,YAAY,UAAU;AACpD,UAAM,aAAa,KAAK,KAAK,YAAY,WAAW;AAGpD,QAAI;AACJ,QAAI,GAAG,WAAW,SAAS,GAAG;AAC5B,mBAAa;AAAA,IACf,WAAW,GAAG,WAAW,WAAW,GAAG;AACrC,mBAAa;AAAA,IACf,OAAO;AACL,YAAM,IAAI,MAAM,oCAAoC,UAAU,EAAE;AAAA,IAClE;AAEA,QAAI;AAEF,YAAM,gBAAgB,MAAM,OAAO;AACnC,YAAM,oBAAoB,cAAc,WAAW;AAEnD,UAAI,CAAC,qBAAqB,OAAO,sBAAsB,UAAU;AAC/D,cAAM,IAAI,MAAM,wDAAwD;AAAA,MAC1E;AAGA,UAAI,CAAC,kBAAkB,IAAI;AACzB,cAAM,IAAI,MAAM,oCAAoC;AAAA,MACtD;AAGA,UAAI,GAAG,WAAW,UAAU,GAAG;AAC7B,0BAAkB,aAAa;AAAA,MACjC;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,iCAAiC,UAAU,KAAK,KAAK,EAAE;AAAA,IACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,6BAA6B,WAA2B;AACtD,UAAM,aAAa,KAAK,KAAK,KAAK,WAAW,SAAS;AAGtD,OAAG,UAAU,KAAK,WAAW,EAAE,WAAW,KAAK,CAAC;AAGhD,QAAI,GAAG,WAAW,UAAU,GAAG;AAC7B,YAAM,IAAI,MAAM,qBAAqB,SAAS,iBAAiB;AAAA,IACjE;AAEA,OAAG,UAAU,UAAU;AACvB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAsB,WAAmB,SAMhC;AACP,UAAM,aAAa,KAAK,6BAA6B,SAAS;AAG9D,UAAM,eAAe,KAAK,wBAAwB,WAAW,OAAO;AACpE,OAAG,cAAc,KAAK,KAAK,YAAY,UAAU,GAAG,YAAY;AAGhE,UAAM,gBAAgB,KAAK,uBAAuB,WAAW,OAAO;AACpE,OAAG,cAAc,KAAK,KAAK,YAAY,WAAW,GAAG,aAAa;AAGlE,QAAI,QAAQ,YAAY;AACtB,YAAM,iBAAiB,KAAK,wBAAwB,WAAW,OAAO;AACtE,SAAG,cAAc,KAAK,KAAK,YAAY,YAAY,GAAG,cAAc;AAAA,IACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,wBAAwB,WAAmB,SAMxC;AACT,WAAO;AAAA,KACN,QAAQ,IAAI;AAAA;AAAA;AAAA,4BAGW,QAAQ,aAAa,qBAAqB,EAAE;AAAA,EACtE,QAAQ,aAAa,YAAY,UAAU,QAAQ,MAAM,EAAE,CAAC,mCAAmC,EAAE;AAAA;AAAA,QAE3F,UAAU,QAAQ,MAAM,EAAE,CAAC;AAAA,SAC1B,SAAS;AAAA,WACP,QAAQ,IAAI;AAAA,kBACL,QAAQ,WAAW;AAAA,kBACnB,QAAQ,YAAY,IAAI,QAAQ,WAAW;AAAA,eAAkB,QAAQ,QAAQ,OAAO,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAWnG,QAAQ,aAAa;AAAA,aAAiB,UAAU,QAAQ,MAAM,EAAE,CAAC,YAAY,EAAE;AAAA;AAAA;AAAA,iBAGnE,UAAU,QAAQ,MAAM,EAAE,CAAC;AAAA;AAAA,EAE1C;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAAuB,WAAmB,SAGvC;AACT,WAAO,KAAK,QAAQ,IAAI;AAAA;AAAA,wCAEY,SAAS;AAAA;AAAA;AAAA,EAG/C,QAAQ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBnB;AAAA;AAAA;AAAA;AAAA,EAKQ,wBAAwB,WAAmB,SAExC;AACT,WAAO;AAAA,KACN,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,eAKF,UAAU,QAAQ,MAAM,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkDxC;AAAA;AAAA;AAAA;AAAA,EAKA,sBAA8B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA6B;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,WAAmB,WAAW,OAAgB;AAC1D,UAAM,UAAU,WAAW,KAAK,YAAY,KAAK;AACjD,WAAO,GAAG,WAAW,KAAK,KAAK,SAAS,SAAS,CAAC;AAAA,EACpD;AACF;AAKO,IAAM,gBAAgB,IAAI,qBAAqB;;;ACzU/C,IAAM,wBAAN,MAAsD;AAAA,EAC3C;AAAA,EACA;AAAA,EACR,cAAc;AAAA,EAEtB,cAAc;AACZ,SAAK,WAAW;AAChB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA4B;AAChC,QAAI,KAAK,aAAa;AACpB;AAAA,IACF;AAGA,UAAM,WAAW,MAAM,KAAK,OAAO,gBAAgB;AAGnD,eAAW,WAAW,UAAU;AAC9B,UAAI;AACF,aAAK,SAAS,SAAS,OAAO;AAAA,MAChC,SAAS,OAAO;AACd,gBAAQ,KAAK,+BAA+B,QAAQ,EAAE,MAAM,KAAK;AAAA,MACnE;AAAA,IACF;AAEA,SAAK,cAAc;AAEnB,YAAQ,IAAI,oCAAoC,KAAK,SAAS,MAAM,CAAC,WAAW;AAAA,EAClF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,WAAmB,OAAY,SAAiD;AAC5F,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAEA,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,YAAY,SAAS;AAAA,MAC9B;AAAA,IACF;AAGA,QAAI,QAAQ,SAAS;AACnB,YAAM,aAAa,QAAQ,QAAQ,SAAS,KAAK;AACjD,UAAI,CAAC,WAAW,OAAO;AACrB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO,4BAA4B,WAAW,KAAK;AAAA,QACrD;AAAA,MACF;AAGA,cAAQ,WAAW,aAAa;AAAA,IAClC;AAGA,QAAI;AACF,UAAI,QAAQ,SAAS;AAEnB,eAAO,MAAM,QAAQ,QAAQ,QAAQ,OAAO,OAAO;AAAA,MACrD,OAAO;AAEL,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,YACJ;AAAA,YACA;AAAA,YACA,MAAM;AAAA,YACN,YAAY,QAAQ;AAAA,UACtB;AAAA,UACA,UAAU;AAAA,YACR,SAAS;AAAA,YACT,eAAe;AAAA,UACjB;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAC5D,UAAU;AAAA,UACR;AAAA,UACA,WAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,WAAmB,OAA8B;AACxD,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO,YAAY,SAAS;AAAA,MAC9B;AAAA,IACF;AAGA,QAAI,QAAQ,SAAS;AACnB,aAAO,QAAQ,QAAQ,SAAS,KAAK;AAAA,IACvC;AAGA,QAAI,QAAQ,aAAa;AACvB,aAAO,KAAK,sBAAsB,OAAO,QAAQ,WAAW;AAAA,IAC9D;AAGA,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAwB;AAE5B,SAAK,SAAS,MAAM;AACpB,SAAK,cAAc;AAGnB,UAAM,KAAK,WAAW;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAuB,WAGd;AACP,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL;AAAA,MACA,YAAY,QAAQ;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,WAA4B;AAC7C,WAAO,KAAK,SAAS,IAAI,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,WAA+C;AAC5D,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AAEA,WAAO,QAAQ,UAAU,YAAY;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,uBAOG;AACD,WAAO,KAAK,SAAS,KAAK,EAAE,IAAI,cAAY;AAAA,MAC1C,IAAI,QAAQ;AAAA,MACZ,MAAM,QAAQ;AAAA,MACd,aAAa,QAAQ;AAAA,MACrB,cAAc,QAAQ;AAAA,MACtB,UAAU,QAAQ;AAAA,MAClB,MAAM,QAAQ,UAAU,YAAY;AAAA,IACtC,EAAE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsB,OAAY,QAAkC;AAE1E,QAAI;AACF,YAAM,YAAY;AAElB,UAAI,UAAU,SAAS,UAAU;AAC/B,YAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,iBAAO,EAAE,OAAO,OAAO,OAAO,0BAA0B;AAAA,QAC1D;AAGA,YAAI,UAAU,YAAY,MAAM,QAAQ,UAAU,QAAQ,GAAG;AAC3D,qBAAW,gBAAgB,UAAU,UAAU;AAC7C,gBAAI,EAAE,gBAAgB,QAAQ;AAC5B,qBAAO,EAAE,OAAO,OAAO,OAAO,8BAA8B,YAAY,GAAG;AAAA,YAC7E;AAAA,UACF;AAAA,QACF;AAGA,YAAI,UAAU,YAAY;AACxB,qBAAW,CAAC,UAAU,UAAU,KAAK,OAAO,QAAQ,UAAU,UAAU,GAAG;AACzE,gBAAI,YAAY,OAAO;AACrB,oBAAM,WAAY,WAAmB;AACrC,oBAAM,aAAa,OAAO,MAAM,QAAQ;AAExC,kBAAI,YAAY,aAAa,YAAY;AACvC,uBAAO,EAAE,OAAO,OAAO,OAAO,aAAa,QAAQ,qBAAqB,QAAQ,GAAG;AAAA,cACrF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,aAAO,EAAE,OAAO,MAAM,WAAW,MAAM;AAAA,IACzC,SAAS,OAAO;AACd,aAAO,EAAE,OAAO,OAAO,OAAO,4BAA4B,KAAK,GAAG;AAAA,IACpE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAKE;AACA,UAAM,WAAW,KAAK,SAAS,KAAK;AACpC,UAAM,QAAQ;AAAA,MACZ,eAAe,SAAS;AAAA,MACxB,iBAAiB;AAAA,MACjB,eAAe;AAAA,MACf,oBAAoB,CAAC;AAAA,IACvB;AAEA,eAAW,WAAW,UAAU;AAE9B,UAAI,QAAQ,SAAS;AACnB,cAAM;AAAA,MACR,OAAO;AACL,cAAM;AAAA,MACR;AAGA,YAAM,WAAW,QAAQ,YAAY;AACrC,YAAM,mBAAmB,QAAQ,KAAK,MAAM,mBAAmB,QAAQ,KAAK,KAAK;AAAA,IACnF;AAEA,WAAO;AAAA,EACT;AACF;AAKO,IAAM,iBAAiB,IAAI,sBAAsB;AAMxD,eAAsB,0BAAyC;AAC7D,QAAM,eAAe,WAAW;AAClC;;;ACnSA;AACA;AAFA,OAAOC,SAAQ;;;ACCf;AADA,OAAOC,SAAQ;AAGf,SAAS,kBAAkB,aAAa;AACxC,SAAS,uBAAuB;AAGhC,IAAI,OAAY;AAEhB,eAAe,SAAuB;AACpC,MAAI,KAAM,QAAO;AAEjB,MAAI;AACF,WAAO,MAAM,OAAO,UAAU;AAC9B,WAAO;AAAA,EACT,QAAQ;AACN,UAAM,EAAE,eAAAC,eAAc,IAAI,MAAM,OAAO,UAAU;AACjD,UAAMC,QAAO,MAAM,OAAO,WAAW;AACrC,UAAMC,MAAK,MAAM,OAAO,SAAS;AAEjC,UAAMC,aAAYF,MAAK,QAAQD,eAAc,YAAY,GAAG,CAAC;AAC7D,UAAM,aAAa;AAAA,MACjBC,MAAK,QAAQE,YAAW,MAAM,MAAM,MAAM,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,MAChGF,MAAK,QAAQE,YAAW,MAAM,MAAM,MAAM,MAAM,MAAM,WAAW,YAAY,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,MAC3IF,MAAK,QAAQC,IAAG,QAAQ,GAAG,WAAW,YAAY,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,IAClH;AAEA,eAAW,KAAK,YAAY;AAC1B,UAAI;AACF,eAAO,MAAM,OAAO;AACpB,eAAO;AAAA,MACT,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;AAOO,SAAS,qBAAqC;AACnD,MAAI,CAACH,IAAG,WAAW,MAAM,cAAc,GAAG;AACxC,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AAGA,MAAI;AACF,UAAM,WAAWA,IAAG,SAAS,MAAM,cAAc,EAAE,OAAO;AAC1D,QAAI,WAAW,IAAO;AACpB,cAAQ,MAAM,uBAAuB,MAAM,cAAc,yBAAyB,SAAS,SAAS,CAAC,CAAC,oBAAoB,MAAM,cAAc,EAAE;AAAA,IAClJ;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO,KAAK,MAAMA,IAAG,aAAa,MAAM,gBAAgB,OAAO,CAAC;AAClE;AAMA,eAAsB,eAA+D;AACnF,QAAM,WAAW,mBAAmB;AACpC,QAAM,MAAM,MAAM,OAAO;AACzB,QAAM,UAAU,IAAI,WAAW,QAAQ,SAAS,UAAU;AAC1D,SAAO,EAAE,aAAa,SAAS,aAAa,QAAQ;AACtD;AAUA,eAAsB,iBACpB,SACA,IACA,MACA,SACiB;AACjB,QAAM,MAAM,MAAM,OAAO;AACzB,QAAM,WAAW,KAAK,OAAO,KAAK,UAAU,OAAO;AACnD,QAAM,UAAU,IAAI,KAAK,OAAO,MAAM,KAAK,IAAI,YAAY,EAAE,OAAO,QAAQ,CAAC,CAAC;AAC9E,QAAM,MAAM,QAAQ,KAAK,OAAO;AAChC,SAAQ,MAAM,KAAK,IAAI,MAAM,CAAC,EAAe,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAClG;AAWA,eAAsB,qBACpB,SACA,IACA,MACA,SACA,cAC8C;AAC9C,MAAI,CAAC,aAAc,QAAO,EAAE,OAAO,OAAO,QAAQ,eAAe;AAEjE,MAAI;AACF,UAAM,MAAM,MAAM,OAAO;AACzB,UAAM,WAAW,KAAK,OAAO,KAAK,UAAU,OAAO;AACnD,UAAM,UAAU,IAAI,KAAK,OAAO,MAAM,KAAK,IAAI,YAAY,EAAE,OAAO,QAAQ,CAAC,CAAC;AAC9E,UAAM,WAAqB,CAAC;AAC5B,aAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK,GAAG;AAC/C,eAAS,KAAK,SAAS,aAAa,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAAA,IAC9D;AACA,UAAM,MAAM,IAAI,UAAU,QAAQ,QAAQ;AAC1C,UAAM,SAAS,IAAI,UAAU,WAAW,OAAO;AAC/C,WAAO,EAAE,OAAO,OAAO,OAAO,SAAS,GAAG,EAAE;AAAA,EAC9C,SAAS,KAAK;AACZ,WAAO,EAAE,OAAO,OAAO,QAAQ,OAAO,GAAG,EAAE;AAAA,EAC7C;AACF;AAWA,eAAsB,oBAAoB,SAAc,UAAkB,SAIvE;AAED,QAAM,aAAa,IAAI,iBAAiB,OAAO;AAC/C,QAAM,SAAS,WAAW;AAAA,IACxB;AAAA,IACA,MAAM,SAAS,MAAM,QAAQ,QAAQ,CAAC,IAAI,MAAM,MAAM,SAAS,MAAM,QAAQ,KAAK,CAAC;AAAA,IACnF;AAAA,IACA;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,UAAU,OAAO;AACxC,QAAM,UAAU,OAAO,KAAK,OAAO,OAAO,CAAC;AAE3C,SAAO,EAAE,SAAS,SAAS,OAAO;AACpC;;;ADtJA;AAEA,eAAe,oBAAoD;AACjE,SAAO;AACT;AAGA,IAAIK,QAAY;AAEhB,eAAeC,UAAuB;AACpC,MAAID,MAAM,QAAOA;AAEjB,MAAI;AACF,IAAAA,QAAO,MAAM,OAAO,UAAU;AAC9B,WAAOA;AAAA,EACT,QAAQ;AACN,UAAM,EAAE,eAAAE,eAAc,IAAI,MAAM,OAAO,UAAU;AACjD,UAAMC,QAAO,MAAM,OAAO,WAAW;AACrC,UAAMC,MAAK,MAAM,OAAO,SAAS;AAEjC,UAAMC,aAAYF,MAAK,QAAQD,eAAc,YAAY,GAAG,CAAC;AAC7D,UAAM,aAAa;AAAA,MACjBC,MAAK,QAAQE,YAAW,MAAM,MAAM,MAAM,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,MAChGF,MAAK,QAAQE,YAAW,MAAM,MAAM,MAAM,MAAM,MAAM,WAAW,YAAY,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,MAC3IF,MAAK,QAAQC,IAAG,QAAQ,GAAG,WAAW,YAAY,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,IAClH;AAEA,eAAW,KAAK,YAAY;AAC1B,UAAI;AACF,QAAAJ,QAAO,MAAM,OAAO;AACpB,eAAOA;AAAA,MACT,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;AAKA,eAAsB,WAAyB;AAC7C,QAAMM,kBAAiB,MAAM,kBAAkB;AAE/C,MAAIC,IAAG,WAAW,MAAM,cAAc,GAAG;AACvC,UAAMC,UAAS,MAAMF,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,UAAMG,eAAc,MAAMD,QAAO,eAAe;AAChD,UAAMA,QAAO,QAAQ;AAErB,WAAO,GAAG;AAAA,MACR,aAAAC;AAAA,MACA,WAAW;AAAA,MACX,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,EAAAF,IAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAC5C,QAAM,SAAS,MAAMD,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,cAAc,MAAM,OAAO,eAAe;AAChD,QAAM,OAAO,QAAQ;AAGrB,MAAIC,IAAG,WAAW,MAAM,cAAc,GAAG;AACvC,IAAAA,IAAG,UAAU,MAAM,gBAAgB,GAAK;AAAA,EAC1C;AAEA,SAAO,GAAG;AAAA,IACR;AAAA,IACA,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,EAClB,CAAC;AACH;AAKA,eAAsB,cAA4B;AAChD,QAAMD,kBAAiB,MAAM,kBAAkB;AAC/C,QAAM,SAAS,MAAMA,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,cAAc,MAAM,OAAO,eAAe;AAChD,QAAM,OAAO,QAAQ;AAErB,SAAO,GAAG,EAAE,YAAY,CAAC;AAC3B;AAKA,eAAsB,YAA0B;AAC9C,QAAMA,kBAAiB,MAAM,kBAAkB;AAC/C,QAAM,SAAS,MAAMA,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,cAAc,MAAM,OAAO,eAAe;AAChD,QAAM,QAAQ,MAAM,OAAO,WAAW;AACtC,QAAM,OAAO,QAAQ;AAErB,SAAO,GAAG;AAAA,IACR,UAAU,EAAE,aAAa,SAAS,QAAQ;AAAA,IAC1C,SAAS,EAAE,eAAe,MAAM;AAAA,EAClC,CAAC;AACH;AAKA,eAAsB,aAA2B;AAC/C,MAAI,CAACC,IAAG,WAAW,MAAM,cAAc,GAAG;AACxC,WAAO,KAAK,oCAAoC;AAAA,EAClD;AAEA,QAAM,MAAM,MAAMN,QAAO;AACzB,QAAM,WAAW,mBAAmB;AACpC,QAAM,UAAU,IAAI,WAAW,QAAQ,SAAS,UAAU;AAC1D,QAAM,EAAE,QAAQ,IAAI,MAAM,oBAAoB,OAAO;AAErD,SAAO,GAAG;AAAA,IACR;AAAA,IACA,SAAS;AAAA,IACT,aAAa,SAAS;AAAA,IACtB,MAAM,YAAY,YACd,sFAAiF,OAAO,KACxF,kHAA6G,OAAO;AAAA,EAC1H,CAAC;AACH;;;AEnIA;AACA;AAFA,OAAOS,SAAQ;;;ACAf;AAOA,eAAsB,SACpB,SACA,UAAuB,CAAC,GACxB,aAAa,GACb,YAAY,KACO;AACnB,QAAM,SAAS,YAAY,YAAY,SAAS;AAChD,QAAM,OAAO,uCAAuC,MAAM;AAC1D,QAAM,MAAM,QAAQ,WAAW,MAAM,IAAI,UAAU,GAAG,IAAI,GAAG,OAAO;AACpE,QAAM,UAAkC,EAAE,GAAI,QAAQ,WAAqC,CAAC,EAAG;AAC/F,MAAI,aAAa;AACf,YAAQ,eAAe,IAAI,UAAU,WAAW;AAAA,EAClD;AAEA,MAAI;AACJ,WAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACtD,QAAI;AACF,YAAM,aAAa,IAAI,gBAAgB;AACvC,YAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAE9D,YAAM,OAAO,MAAM,MAAM,KAAK,EAAE,GAAG,SAAS,SAAS,QAAQ,WAAW,OAAO,CAAC;AAChF,mBAAa,OAAO;AAGpB,WAAK,KAAK,WAAW,OAAO,KAAK,UAAU,QAAQ,UAAU,YAAY;AACvE,cAAM,UAAU,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,OAAO,GAAG,GAAI;AAC1D,cAAM,IAAI,QAAQ,OAAK,WAAW,GAAG,OAAO,CAAC;AAC7C;AAAA,MACF;AAEA,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,kBAAY;AACZ,UAAI,UAAU,YAAY;AACxB,cAAM,UAAU,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,OAAO,GAAG,GAAI;AAC1D,cAAM,IAAI,QAAQ,OAAK,WAAW,GAAG,OAAO,CAAC;AAC7C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,IAAI,MAAM,gCAAgC;AAC/D;AAKA,eAAsB,iBACpB,KACA,UAAuB,CAAC,GACxB,YAAY,MACO;AACnB,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAC9D,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,KAAK,EAAE,GAAG,SAAS,QAAQ,WAAW,OAAO,CAAC;AACvE,WAAO;AAAA,EACT,UAAE;AACA,iBAAa,OAAO;AAAA,EACtB;AACF;AAMA,eAAsB,iBAAiB,MAA0C;AAC/E,MAAI;AACF,UAAM,OAAO,MAAM,SAAS,OAAO,IAAI,OAAO;AAC9C,QAAI,CAAC,KAAK,GAAI,QAAO;AACrB,UAAM,UAAU,MAAM,KAAK,KAAK,GAAG,KAAK;AACxC,QAAI,CAAC,UAAU,OAAO,SAAS,EAAG,QAAO;AACzC,UAAM,QAAQ,OAAO,MAAM,OAAO,EAAG,IAAI,OAAK,SAAS,GAAG,EAAE,CAAC;AAC7D,WAAO,IAAI,WAAW,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaO,SAAS,qBAA6B;AAC3C,SAAO,YAAY,YACf,6BACA;AACN;;;AClGA,IAAI,cAA4C;AAEhD,eAAe,gBAAgD;AAC7D,MAAI,YAAa,QAAO;AACxB,QAAM,MAAM,MAAM,OAAO,UAAU;AACnC,gBAAc,IAAI;AAClB,SAAO;AACT;AASA,eAAsB,uBACpB,MACA,SACA,OACA,aACyB;AACzB,QAAM,aAAa,MAAM,cAAc;AACvC,QAAM,aAAa,MAAM;AACzB,QAAM,SAA+F,CAAC;AAGtG,QAAM,SAAwF;AAAA,IAC5F,EAAE,QAAQ,SAAS,MAAM,MAAM,MAAM,KAAK;AAAA,EAC5C;AACA,MAAI,MAAM,CAAC,MAAM,KAAK;AACpB,WAAO,KAAK,EAAE,QAAQ,UAAU,GAAG,WAAW,KAAK,CAAC;AAAA,EACtD,OAAO;AACL,WAAO,KAAK,EAAE,QAAQ,UAAU,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC;AAAA,EACrD;AACA,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AACzC,SAAO,KAAK,MAAM;AAGlB,WAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,UAAM,gBAAiB,WAAW,IAAK;AACvC,QAAI,MAAM,CAAC,MAAM,KAAK;AACpB,aAAO,KAAK,CAAC,EAAE,QAAQ,eAAe,WAAW,KAAK,CAAC,CAAC;AAAA,IAC1D,OAAO;AACL,aAAO,KAAK,CAAC,EAAE,QAAQ,eAAe,MAAM,MAAM,CAAC,EAAE,CAAC,CAAC;AAAA,IACzD;AAAA,EACF;AAEA,SAAO,IAAI,WAAW,aAAa,MAAM;AAC3C;;;AF5CA;AAEA,eAAeC,qBAAoD;AACjE,SAAO;AACT;AAGA,IAAIC,QAAY;AAEhB,eAAeC,UAAuB;AACpC,MAAID,MAAM,QAAOA;AAEjB,MAAI;AACF,IAAAA,QAAO,MAAM,OAAO,UAAU;AAC9B,WAAOA;AAAA,EACT,QAAQ;AACN,UAAM,EAAE,eAAAE,eAAc,IAAI,MAAM,OAAO,UAAU;AACjD,UAAMC,QAAO,MAAM,OAAO,WAAW;AACrC,UAAMC,MAAK,MAAM,OAAO,SAAS;AAEjC,UAAMC,aAAYF,MAAK,QAAQD,eAAc,YAAY,GAAG,CAAC;AAC7D,UAAM,aAAa;AAAA,MACjBC,MAAK,QAAQE,YAAW,MAAM,MAAM,MAAM,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,MAChGF,MAAK,QAAQE,YAAW,MAAM,MAAM,MAAM,MAAM,MAAM,WAAW,YAAY,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,MAC3IF,MAAK,QAAQC,IAAG,QAAQ,GAAG,WAAW,YAAY,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,IAClH;AAEA,eAAW,KAAK,YAAY;AAC1B,UAAI;AACF,QAAAJ,QAAO,MAAM,OAAO;AACpB,eAAOA;AAAA,MACT,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;AAKA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAKA,eAAsB,aAA2B;AAC/C,QAAMM,kBAAiB,MAAMP,mBAAkB;AAC/C,QAAM,MAAM,MAAME,QAAO;AAEzB,QAAM,SAAS,MAAMK,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,QAAQ,MAAM,OAAO,WAAW;AACtC,QAAM,OAAO,QAAQ;AAErB,SAAO,GAAG,EAAE,eAAe,MAAM,CAAC;AACpC;AAYA,eAAsB,UAAU,SAA6B,SAAgC;AAC3F,MAAI,CAAC,SAAS;AACZ,WAAO,KAAK,6BAA6B;AAAA,EAC3C;AAEA,QAAM,OAAO,SAAS,WAAW,KAAK,EAAE;AACxC,QAAM,OAAO,QAAQ,YAAY;AAEjC,MAAI,CAAC,iBAAiB,KAAK,IAAI,GAAG;AAChC,WAAO,KAAK,+CAA0C;AAAA,EACxD;AAEA,QAAM,MAAM,MAAML,QAAO;AACzB,QAAMK,kBAAiB,MAAMP,mBAAkB;AAI/C,MAAI,SAAc;AAClB,QAAM,YAAY;AAClB,QAAM,YAAY,KAAK,IAAI;AAC3B,MAAI,UAAU;AAEd,SAAO,KAAK,IAAI,IAAI,YAAY,WAAW;AACzC,UAAM,aAAa,MAAM,SAAS,OAAO,IAAI,IAAI,CAAC,GAAG,GAAG,GAAK;AAE7D,QAAI,WAAW,IAAI;AACjB,eAAS,MAAM,WAAW,KAAK;AAC/B;AAAA,IACF,WAAW,WAAW,WAAW,KAAK;AAEpC;AACA,YAAM,UAAU,KAAK,IAAI,MAAO,KAAK,IAAI,KAAK,OAAO,GAAG,GAAK;AAC7D,cAAQ,MAAM,gDAAgD,KAAK,MAAM,UAAQ,GAAI,CAAC,iBAAiB,OAAO,GAAG;AACjH,YAAM,MAAM,OAAO;AACnB;AAAA,IACF,OAAO;AACL,aAAO,KAAK,4BAA4B,WAAW,MAAM,EAAE;AAAA,IAC7D;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,WAAO,KAAK,eAAe,IAAI,oCAAoC,KAAK,OAAO,KAAK,IAAI,IAAI,aAAa,GAAI,CAAC,mFAAmF;AAAA,EACnM;AAEA,QAAM,cAAc,OAAO,iBAAiB,OAAO,iBAAiB;AACpE,QAAM,cAAc,OAAO;AAG3B,MAAI,CAAC,OAAO,QAAQ,CAAC,OAAO,KAAK,IAAI,GAAG;AACtC,WAAO,KAAK,gBAAgB,IAAI,kCAAkC,OAAO,MAAM,UAAU,CAAC,WAAW;AAAA,EACvG;AAEA,MAAI;AAIJ,QAAM,eAAe,MAAM,iBAAiB,IAAI;AAEhD,MAAI,cAAc;AAChB,QAAI;AACF,YAAM,UAAU,IAAI,KAAK,WAAW,MAAM,KAAK,YAAY,CAAC;AAC5D,YAAM,UAAU,QAAQ,SAAS,IAAI;AAErC,UAAI,SAAS;AAEX,cAAM,QAAQ,QAAQ,MAAM,QAAQ;AACpC,YAAI,OAAO;AACT,gBAAM,SAAS,MAAM,QAAQ,IAAI;AACjC,cAAI,CAAC,QAAQ;AACX,mBAAO,KAAK,gBAAgB,IAAI,uCAAuC,MAAM,QAAQ,MAAM,WAAW;AAAA,UACxG;AAAA,QACF;AACA,0BAAkB,QAAQ,eAAe,IAAI;AAAA,MAC/C;AAAA,IACF,SAAS,SAAc;AACrB,cAAQ,MAAM,mCAAmC,QAAQ,OAAO,EAAE;AAAA,IAEpE;AAAA,EACF;AAGA,MAAI,CAAC,mBAAmB,aAAa;AACnC,QAAI;AACF,YAAM,YAAY,MAAM,SAAS,OAAO,IAAI,MAAM;AAClD,UAAI,CAAC,UAAU,IAAI;AACjB,eAAO,KAAK,oCAAoC,UAAU,MAAM,EAAE;AAAA,MACpE;AACA,YAAM,WAAW,MAAM,UAAU,KAAK;AACtC,YAAM,WAAW,IAAI,YAAY,QAAQ,SAAS,KAAK,CAAC;AAExD,YAAM,YAAY,MAAM,SAAS,OAAO,IAAI,YAAY;AACxD,UAAI,CAAC,UAAU,IAAI;AACjB,eAAO,KAAK,iCAAiC,UAAU,MAAM,EAAE;AAAA,MACjE;AACA,YAAM,YAAY,MAAM,UAAU,KAAK;AAEvC,UAAI,CAAC,MAAM,QAAQ,SAAS,KAAK,UAAU,WAAW,GAAG;AACvD,eAAO,KAAK,qCAAqC;AAAA,MACnD;AAEA,YAAM,QAAQ,UAAU,CAAC;AACzB,YAAM,aAAa,MAAM,uBAAuB,MAAM,MAAM,OAAO,MAAM,OAAO,WAAW;AAC3F,eAAS,aAAa;AAEtB,YAAM,OAAO,IAAI,IAAI,KAAK;AAC1B,WAAK,iBAAiB,QAAQ;AAC9B,wBAAkB,KAAK,eAAe,IAAI;AAAA,IAC5C,SAAS,WAAgB;AACvB,aAAO,KAAK,sCAAsC,UAAU,OAAO,EAAE;AAAA,IACvE;AAAA,EACF;AAGA,MAAI,CAAC,iBAAiB;AACpB,QAAI,aAAa;AACf,aAAO,KAAK,eAAe,IAAI,iGAA4F;AAAA,IAC7H,OAAO;AAGL,aAAO;AAAA,QACL,eAAe,IAAI,oBAAoB,OAAO,iBAAiB,CAAC;AAAA;AAAA;AAAA;AAAA,MAGlE;AAAA,IACF;AAAA,EACF;AAGA,QAAM,iBAAiB,OAAO,KAAK,IAAI,EAAE,SAAS,OAC9C,KAAK,MAAM,OAAO,KAAK,IAAI,EAAE,QAAQ,GAAG,IACxC;AAGJ,QAAM,SAAS,MAAMO,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,cAAc,MAAM,OAAO,eAAe;AAEhD,MAAI;AACF,UAAM,OAAO,OAAO,OAAO,QAAQ,kBAAkB;AAAA,MACnD,IAAI,MAAM,KAAK,eAAe;AAAA,MAC9B,SAAS,CAAC;AAAA,QACR,aAAa;AAAA,QACb,UAAU;AAAA,QACV,mBAAmB;AAAA,UACjB,kBAAkB,IAAI,MAAM,SAAS,IAAI,MAAM,QAAQ,UAAU,MAAM,CAAC;AAAA,UACxE,kBAAkB,IAAI,MAAM,SAAS,IAAI,MAAM,QAAQ,OAAO,MAAM,CAAC;AAAA,UACrE,mBAAmB;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,MACD,aAAa;AAAA,IACf,CAAC;AAED,UAAM,UAAU,MAAM,OAAO,WAAW;AACxC,UAAM,OAAO,QAAQ;AAErB,UAAM,eAAe,mBAAmB;AACxC,WAAO,GAAG;AAAA,MACR;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,aAAa,eAAe;AAAA,MAC5B,eAAe,OAAO,iBAAiB;AAAA,MACvC,UAAU;AAAA,MACV,aAAa,CAAC;AAAA,MACd;AAAA,MACA,UAAU,GAAG,YAAY,OAAO,IAAI;AAAA,IACtC,CAAC;AAAA,EACH,SAAS,KAAU;AACjB,UAAM,OAAO,QAAQ;AAGrB,QAAI,IAAI,SAAS,SAAS,SAAS,KAAK,IAAI,SAAS,SAAS,WAAW,GAAG;AAC1E,aAAO,KAAK,QAAQ,IAAI,IAAI,IAAI,kCAAkC;AAAA,IACpE;AACA,QAAI,IAAI,SAAS,SAAS,QAAQ,KAAK,IAAI,SAAS,SAAS,SAAS,GAAG;AACvE,aAAO,KAAK,QAAQ,IAAI,IAAI,IAAI,uFAAuF;AAAA,IACzH;AAEA,WAAO,KAAK,0BAA0B,IAAI,OAAO,EAAE;AAAA,EACrD;AACF;;;AGxPA;AACA;AACA;AACA;AAGA;AAPA,OAAOC,SAAQ;AAKf,SAAS,eAAAC,cAAa,QAAAC,OAAM,QAAQ,YAAAC,iBAA8B;AAIlE,eAAeC,qBAAoD;AACjE,SAAO;AACT;AAKA,eAAsB,cAA4B;AAChD,MAAI,CAACJ,IAAG,WAAW,MAAM,cAAc,GAAG;AACxC,WAAO,KAAK,oCAAoC;AAAA,EAClD;AAEA,QAAMK,kBAAiB,MAAMD,mBAAkB;AAC/C,QAAM,SAAS,MAAMC,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,cAAc,MAAM,OAAO,eAAe;AAChD,QAAM,OAAO,QAAQ;AAErB,QAAM,cAAc,iBAAiB;AACrC,MAAI,eAAe,YAAY,gBAAgB,aAAa;AAC1D,WAAO,GAAG;AAAA,MACR,mBAAmB;AAAA,MACnB;AAAA,MACA,cAAc,YAAY;AAAA,MAC1B,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAGA,QAAM,YAAY;AAClB,QAAM,mBAAmB;AAGzB,QAAM,eAAyB,CAAC,UAAU;AAC1C,QAAM,WAAW,aAAa;AAC9B,MAAI,SAAS,KAAK,OAAK,EAAE,cAAc,WAAW,GAAG;AACnD,iBAAa,KAAK,OAAO;AAAA,EAC3B;AAIA,QAAM,kBAAkB;AAAA,IACtB,UAAU;AAAA,IACV,MAAM;AAAA,IACN;AAAA,IACA,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AAEA,MAAI;AACJ,MAAI;AACF,qBAAiB,MAAM,4BAA4B,iBAAiB,OAAO,QAAQ;AAAA,EACrF,SAAS,KAAU;AACjB,WAAO,KAAK,wBAAwB,IAAI,OAAO,EAAE;AAAA,EACnD;AAGA,MAAI,cAA6B;AAEjC,MAAI,SAAS,SAAS,GAAG;AAEvB,eAAW,WAAW,UAAU;AAC9B,YAAM,iBAAiB;AAAA,QACrB,UAAU;AAAA,QACV,MAAM;AAAA,QACN;AAAA,QACA,WAAW,QAAQ;AAAA,QACnB,MAAM,QAAQ;AAAA,QACd,aAAa,QAAQ;AAAA,QACrB,SAAS;AAAA,UACP,OAAO;AAAA,UACP,YAAY,QAAQ;AAAA,QACtB;AAAA,QACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC;AAEA,UAAI;AACF,cAAM,gBAAgB,MAAM,4BAA4B,gBAAgB,OAAO,QAAQ;AACvF,sBAAc,cAAc;AAAA,MAC9B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAGA,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,cAAc,eAAe;AAAA,IAC7B;AAAA,IACA,QAAQ,eAAe;AAAA,IACvB,eAAc,oBAAI,KAAK,GAAE,YAAY;AAAA,EACvC;AACA,mBAAiB,YAAY;AAE7B,SAAO,GAAG;AAAA,IACR,YAAY;AAAA,IACZ;AAAA,IACA,cAAc,eAAe;AAAA,IAC7B;AAAA,IACA,YAAY;AAAA,IACZ,QAAQ,eAAe;AAAA,IACvB,WAAW,MAAM;AAAA,EACnB,CAAC;AACH;AAKA,eAAsB,gBAA8B;AAElD,QAAM,SAAS,MAAM,eAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AACrF,QAAM,EAAE,SAAS,KAAK,IAAI,MAAM,OAAO,OAAO,OAAO,YAAY,EAAE,QAAQ,OAAO,UAAU,SAAS,sBAAsB,CAAC;AAE5H,QAAM,QAAQ,IAAIF,UAAS,OAAO,OAAO,MAAM;AAC/C,QAAM,0BAA0B,MAAM,MAAM,OAAO,CAAC,GAAG,WAAW,GAAG,KAAK,QAAQ,QAAQ,IAAI;AAC9F,QAAM,SAAS,IAAIF,aAAY;AAC/B,QAAM,OAAOC,MAAK,WAAW,IAAgB;AAC7C,UAAQ,QAAQ,CAAC,MAAoB;AACnC,UAAM,CAACI,OAAM,CAAC,IAAI,EAAE,SAAS,MAAM,GAAG;AACtC,UAAM,oBAAoB,OAAO,CAAC;AAClC,UAAM,oBAAoB,KAAK,0BAA0BA,KAAI;AAC7D,WAAO,SAAS;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACD,SAAO,UAAU;AAAA,IACf,eAAe,OAAO,QAAQ,2BAA2B;AAAA,IACzD,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,OAAO,KAAK;AAElB,QAAM,WAAW,MAAM,OAAO,OAAO,OAAO,aAAa;AAAA,IACvD,WAAW;AAAA,IACX,aAAa;AAAA,IACb,QAAQ,OAAO,OAAO,IAAI,QAAM;AAAA,MAC9B,kBAAkB;AAAA,MAClB,UAAU,EAAE,aAAa,MAAM,OAAO,EAAE,iBAAiB;AAAA,MACzD,iBAAiB,EAAE,iBAAiB,MAAM;AAAA,IAC5C,EAAE;AAAA,EACJ,CAAC;AAED,QAAM,OAAO,SAAS;AAItB,QAAM,aAAa,MAAM,MAAM,GAAG,WAAW,WAAW;AAAA,IACtD,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,YAAY,KAAK,UAAU,CAAC,OAAO,QAAQ,CAAC;AAAA,IAC9C;AAAA,IACA,MAAM,IAAI,WAAW,SAAS,EAAc;AAAA,EAC9C,CAAC;AAED,MAAI,CAAC,WAAW,IAAI;AAClB,UAAM,UAAU,MAAM,WAAW,KAAK;AACtC,UAAM,IAAI,MAAM,8BAA8B,WAAW,MAAM,WAAM,OAAO,EAAE;AAAA,EAChF;AAGA,qBAAmB;AAEnB,SAAO,GAAG;AAAA,IACR,cAAc;AAAA,IACd;AAAA,EACF,CAAC;AACH;;;AClMA;AACA;AACA;AAKA,eAAsB,YAAY,MAA8B;AAG9D,MAAI,gBAA+B;AACnC,MAAI,cAA6B;AAEjC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,KAAK,CAAC,MAAM,eAAe,KAAK,IAAI,CAAC,EAAG,iBAAgB,KAAK,EAAE,CAAC;AAAA,aAC3D,KAAK,CAAC,MAAM,aAAa,KAAK,IAAI,CAAC,EAAG,eAAc,KAAK,EAAE,CAAC;AAAA,EACvE;AAEA,QAAM,UAKF,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,EAAE;AAG/B,MAAI,CAAC,eAAe;AAClB,QAAI;AACF,YAAM,aAAa,cAAc,EAAE,MAAM,YAAY,IAAI,EAAE,MAAM,OAAO;AACxE,YAAM,cAAc,MAAM,cAAc,gBAAgB,QAAQ,UAAU;AAE1E,UAAI,YAAY,SAAS;AACvB,mBAAW,UAAU,YAAY,SAAS;AACxC,cAAI;AACF,kBAAM,EAAE,MAAM,KAAK,IAAI,MAAM,mBAAmB,OAAO,MAAM,OAAO,WAAW;AAC/E,gBAAI,MAAM,SAAS,YAAY;AAE7B,oBAAM,OAAO,KAAK,QAAQ,KAAK,aAAa;AAC5C,sBAAQ,OAAO,KAAK,EAAE,GAAG,MAAM,MAAM,KAAK,CAAC;AAAA,YAC7C;AAAA,UACF,QAAQ;AAAA,UAAe;AAAA,QACzB;AAAA,MACF;AAAA,IACF,SAAS,KAAU;AACjB,cAAQ,aAAa,OAAO,GAAG;AAAA,IACjC;AAAA,EACF;AAGA,MAAI,CAAC,aAAa;AAChB,QAAI;AACF,YAAM,eAAe,gBAAgB,EAAE,aAAa,cAAc,IAAI,CAAC;AACvE,YAAM,gBAAgB,MAAM,cAAc,gBAAgB,UAAU,YAAY;AAEhF,UAAI,cAAc,SAAS;AACzB,mBAAW,UAAU,cAAc,SAAS;AAC1C,cAAI;AACF,kBAAM,EAAE,MAAM,KAAK,IAAI,MAAM,mBAAmB,OAAO,MAAM,OAAO,WAAW;AAC/E,gBAAI,MAAM,SAAS,WAAW;AAC5B,sBAAQ,SAAS,KAAK,EAAE,GAAG,MAAM,KAAK,CAAC;AAAA,YACzC;AAAA,UACF,QAAQ;AAAA,UAAe;AAAA,QACzB;AAAA,MACF;AAAA,IACF,SAAS,KAAU;AACjB,cAAQ,eAAe,OAAO,GAAG;AAAA,IACnC;AAAA,EACF;AAEA,SAAO,GAAG;AAAA,IACR,YAAY;AAAA,IACZ,YAAY,QAAQ,OAAO;AAAA,IAC3B,cAAc,QAAQ,SAAS;AAAA,IAC/B,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,GAAI,QAAQ,cAAc,EAAE,YAAY,QAAQ,WAAW;AAAA,IAC3D,GAAI,QAAQ,gBAAgB,EAAE,cAAc,QAAQ,aAAa;AAAA,EACnE,CAAC;AACH;;;AC9EA;AACA;;;ACKA;AAGA;AAEA,eAAeC,qBAAoD;AACjE,SAAO;AACT;AAeA,eAAsB,mBACpB,iBACA,MACA,MACwB;AAExB,MAAI,CAAC,yBAAyB,KAAK,eAAe,GAAG;AACnD,UAAM,IAAI,MAAM,iFAAiF;AAAA,EACnG;AAEA,QAAMC,kBAAiB,MAAMD,mBAAkB;AAC/C,QAAM,SAAS,MAAMC,gBAAe,KAAK,EAAE,SAAS,SAAS,YAAY,WAAW,CAAC;AAErF,MAAI;AACF,UAAM,SAAS,MAAM,OAAO,cAAc;AAAA,MACxC,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,aAAa,QAAQ;AAAA,IACvB,CAAC;AAGD,WAAO;AAAA,MACL,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,MACjB,kBAAkB,OAAO;AAAA,MACzB,kBAAkB,OAAO;AAAA,MACzB,mBAAmB,OAAO;AAAA,IAC5B;AAAA,EACF,UAAE;AACA,UAAM,OAAO,QAAQ;AAAA,EACvB;AACF;;;ADpDA,eAAsB,kBACpB,WACA,WACA,SACA,cACc;AACd,MAAI,CAAC,aAAa,CAAC,WAAW;AAC5B,WAAO,KAAK,qEAAqE;AAAA,EACnF;AAEA,MAAI,CAAC,yBAAyB,KAAK,SAAS,GAAG;AAC7C,WAAO,KAAK,qEAAqE;AAAA,EACnF;AAEA,QAAM,EAAE,aAAa,QAAQ,IAAI,MAAM,aAAa;AACpD,QAAM,OAAO,SAAS,WAAW,KAAK,EAAE;AAGxC,MAAI,YAAqB;AACzB,MAAI,cAAc;AAChB,QAAI;AACF,kBAAY,KAAK,MAAM,YAAY;AAAA,IACrC,QAAQ;AACN,aAAO,KAAK,8BAA8B;AAAA,IAC5C;AAAA,EACF;AAGA,MAAI,cAAmB;AAEvB,MAAI,OAAO,GAAG;AACZ,QAAI;AACF,YAAM,UAAU,MAAM,mBAAmB,WAAW,MAAM,oBAAoB,SAAS,EAAE;AACzF,oBAAc;AAAA,QACZ,MAAM,QAAQ;AAAA,QACd,MAAM,QAAQ;AAAA,QACd,UAAU,QAAQ;AAAA,QAClB,kBAAkB,QAAQ;AAAA,QAC1B,kBAAkB,QAAQ;AAAA,QAC1B,mBAAmB,QAAQ;AAAA,MAC7B;AAAA,IACF,SAAS,KAAU;AAEjB,oBAAc,EAAE,OAAO,OAAO,IAAI,WAAW,GAAG,EAAE;AAAA,IACpD;AAAA,EACF;AAEA,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,GAAI,YAAY,EAAE,OAAO,UAAU,IAAI,CAAC;AAAA,IACxC,SAAS;AAAA,IACT,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,EACtC;AAEA,QAAM,YAAY,MAAM,iBAAiB,SAAS,WAAW,mBAAmB,cAAc;AAE9F,QAAM,OAAO,MAAM,MAAM,GAAG,WAAW,eAAe;AAAA,IACpD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU;AAAA,MACnB,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,MAAI,CAAC,KAAK,IAAI;AACZ,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,WAAO,KAAK,sBAAsB,KAAK,MAAM,MAAM,IAAI,EAAE;AAAA,EAC3D;AAEA,QAAM,SAAS,MAAM,KAAK,KAAK;AAE/B,SAAO,GAAG;AAAA,IACR,MAAM;AAAA,IACN,WAAW,OAAO;AAAA,IAClB,IAAI;AAAA,IACJ;AAAA,IACA,iBAAiB,eAAe,CAAC,YAAY;AAAA,IAC7C,aAAa,aAAa,QAAQ;AAAA,IAClC,UAAU,aAAa,YAAY;AAAA,IACnC,MAAM;AAAA,EACR,CAAC;AACH;;;AE5FA;AACA;AACA;AAHA,OAAOC,SAAQ;AAQf,eAAsB,kBAAgC;AACpD,MAAI,CAACA,IAAG,WAAW,MAAM,YAAY,GAAG;AACtC,WAAO,GAAG,EAAE,SAAS,CAAC,GAAG,OAAO,EAAE,CAAC;AAAA,EACrC;AAEA,QAAM,UAAU,UAAe,MAAM,YAAY;AACjD,QAAM,UAAU,QAAQ,OAAO,OAAK,EAAE,WAAW,SAAS;AAE1D,SAAO,GAAG,EAAE,SAAS,OAAO,QAAQ,QAAQ,OAAO,QAAQ,OAAO,CAAC;AACrE;;;AChBA;AACA;AAFA,OAAOC,UAAQ;AAIf;AAKA,eAAsB,kBACpB,WACA,cACA,WACA,YACc;AACd,MAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,aAAa,CAAC,YAAY;AAC5D,WAAO,KAAK,4EAA4E;AAAA,EAC1F;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,UAAU;AAAA,EAChC,QAAQ;AACN,WAAO,KAAK,+BAA+B;AAAA,EAC7C;AAEA,QAAM,EAAE,aAAa,QAAQ,IAAI,MAAM,aAAa;AAGpD,MAAIC,KAAG,WAAW,MAAM,YAAY,GAAG;AACrC,UAAM,QAAQA,KAAG,aAAa,MAAM,cAAc,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AAC5F,UAAM,gBAAgB,CAAC,aAAa,YAAY,mBAAmB,UAAU,OAAO;AAEpF,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,YAAI,MAAM,cAAc,aAAa,cAAc,SAAS,MAAM,MAAM,GAAG;AACzE,iBAAO,GAAG;AAAA,YACR,MAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA,IAAI;AAAA,YACJ,SAAS,0CAA0C,MAAM,MAAM;AAAA,YAC/D,kBAAkB;AAAA,YAClB,gBAAgB,MAAM;AAAA,UACxB,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AAEA,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,iBAAiB,SAAS,cAAc,oBAAoB,eAAe;AAC7F,QAAM,OAAO,MAAM,MAAM,GAAG,WAAW,eAAe;AAAA,IACpD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU;AAAA,MACnB,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,SAAS;AAAA,MACT,WAAW;AAAA,IACb,CAAC;AAAA,EACH,CAAC;AAED,MAAI,CAAC,KAAK,IAAI;AAEZ,6BAAyB,WAAW,UAAU;AAAA,MAC5C,UAAU,KAAK,IAAI;AAAA,MACnB,OAAO,sBAAsB,KAAK,MAAM;AAAA,IAC1C,CAAC;AACD,WAAO,KAAK,sBAAsB,KAAK,MAAM,EAAE;AAAA,EACjD;AAGA,2BAAyB,WAAW,aAAa;AAAA,IAC/C,aAAa,KAAK,IAAI;AAAA,EACxB,CAAC;AAED,SAAO,GAAG,EAAE,MAAM,MAAM,WAAW,WAAW,IAAI,aAAa,CAAC;AAClE;;;ACrFA;AACA;AAFA,OAAOC,UAAQ;;;ACCf;AADA,OAAOC,UAAQ;AAGf;AAsCA;AAhCA,IAAIC,QAAY;AAEhB,eAAeC,UAAuB;AACpC,MAAID,MAAM,QAAOA;AAEjB,MAAI;AACF,IAAAA,QAAO,MAAM,OAAO,UAAU;AAC9B,WAAOA;AAAA,EACT,QAAQ;AACN,UAAM,EAAE,eAAAE,eAAc,IAAI,MAAM,OAAO,UAAU;AACjD,UAAMC,QAAO,MAAM,OAAO,WAAW;AACrC,UAAMC,MAAK,MAAM,OAAO,SAAS;AAEjC,UAAMC,aAAYF,MAAK,QAAQD,eAAc,YAAY,GAAG,CAAC;AAC7D,UAAM,aAAa;AAAA,MACjBC,MAAK,QAAQE,YAAW,MAAM,MAAM,MAAM,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,MAChGF,MAAK,QAAQE,YAAW,MAAM,MAAM,MAAM,MAAM,MAAM,WAAW,YAAY,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,MAC3IF,MAAK,QAAQC,IAAG,QAAQ,GAAG,WAAW,YAAY,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,OAAO,QAAQ;AAAA,IAClH;AAEA,eAAW,KAAK,YAAY;AAC1B,UAAI;AACF,QAAAJ,QAAO,MAAM,OAAO;AACpB,eAAOA;AAAA,MACT,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;AAIA,eAAeM,qBAAoD;AACjE,SAAO;AACT;AAGA,eAAe,aAA6C;AAC1D,QAAM,SAAS,MAAM;AACrB,SAAO,OAAO;AAChB;AAMA,eAAsB,uBACpB,SACA,SACA,WACA,WACA,YAQC;AACD,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,UAAU,OAAO,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,gBAAgB,OAAO,OAAO,aAAa;AAAA,EAChH;AAEA,MAAI,QAAQ,OAAO;AACjB,WAAO,EAAE,UAAU,OAAO,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,gBAAgB,OAAO,OAAO,QAAQ,MAAM;AAAA,EACjH;AAEA,MAAI,CAAC,QAAQ,QAAQ,CAAC,QAAQ,UAAU;AACtC,WAAO,EAAE,UAAU,OAAO,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,gBAAgB,OAAO,OAAO,2BAA2B;AAAA,EAC9H;AAEA,MAAI,QAAQ,WAAW,SAAS;AAC9B,WAAO,EAAE,UAAU,OAAO,MAAM,QAAQ,QAAQ,MAAM,UAAU,QAAQ,UAAU,aAAa,GAAG,gBAAgB,OAAO,OAAO,yBAAyB,QAAQ,QAAQ,MAAM,OAAO,GAAG;AAAA,EAC3L;AAGA,QAAMC,kBAAiB,MAAMD,mBAAkB;AAC/C,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,SAAS,MAAMC,gBAAe,KAAK,EAAE,SAAS,YAAY,WAAW,CAAC;AAE5E,MAAI;AAEF,UAAM,eAAe,MAAM,OAAO,cAAc,EAAE,MAAM,QAAQ,KAAK,CAAC;AACtE,QAAI,CAAC,aAAa,OAAO;AACvB,YAAM,OAAO,QAAQ;AACrB,aAAO,EAAE,UAAU,OAAO,MAAM,QAAQ,QAAQ,MAAM,UAAU,QAAQ,UAAU,aAAa,GAAG,gBAAgB,OAAO,OAAO,wBAAwB,aAAa,OAAO,KAAK,IAAI,CAAC,GAAG;AAAA,IAC3L;AAGA,UAAM,eAAe,MAAM,OAAO,cAAc;AAAA,MAC9C,MAAM,QAAQ;AAAA,MACd,kBAAkB,QAAQ;AAAA,MAC1B,kBAAkB,QAAQ;AAAA,MAC1B,mBAAmB,QAAQ;AAAA,MAC3B,aAAa,eAAe,SAAS;AAAA,IACvC,CAAC;AAED,UAAM,OAAO,QAAQ;AAErB,QAAI,CAAC,aAAa,UAAU;AAC1B,aAAO,EAAE,UAAU,OAAO,MAAM,QAAQ,QAAQ,MAAM,UAAU,QAAQ,UAAU,aAAa,GAAG,gBAAgB,OAAO,OAAO,0BAA0B;AAAA,IAC5J;AAEA,WAAO;AAAA,MACL,UAAU;AAAA,MACV,MAAM,QAAQ;AAAA,MACd,UAAU,QAAQ;AAAA,MAClB,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACT;AAAA,EACF,SAAS,KAAU;AACjB,UAAM,OAAO,QAAQ;AACrB,WAAO,EAAE,UAAU,OAAO,MAAM,QAAQ,QAAQ,MAAM,UAAU,QAAQ,UAAU,aAAa,GAAG,gBAAgB,OAAO,OAAO,IAAI,QAAQ;AAAA,EAC9I;AACF;AAKA,eAAe,cACb,KACA,aACA,SACA,WAC+B;AAE/B,MAAIC,KAAG,WAAW,MAAM,YAAY,GAAG;AACrC,UAAM,QAAQA,KAAG,aAAa,MAAM,cAAc,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AAC5F,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,YAAI,MAAM,cAAc,IAAI,IAAI;AAE9B,iBAAO;AAAA,YACL,IAAI,IAAI;AAAA,YACR,MAAM;AAAA,YACN;AAAA,YACA,QAAQ,MAAM,WAAW,YAAY,mBAAmB,WAAW,MAAM,MAAM;AAAA,YAC/E,iBAAiB;AAAA,YACjB,aAAa,MAAM;AAAA,YACnB,kBAAkB,MAAM;AAAA,YACxB,MAAM,IAAI;AAAA,YACV,KAAK;AAAA,UACP;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AAEA,QAAM,MAAM,MAAMP,QAAO;AACzB,QAAM,UAAU,IAAI,SAAS;AAC7B,QAAM,QAAQ,IAAI,SAAS,SAAS,IAAI;AAGxC,QAAM,iBAAiB,mBAAmB;AAC1C,QAAM,aAAa,IAAI,KAAK,QAAQ,IAAI,WAAW,QAAQ,eAAe,UAAU,EAAE,YAAY,EAAE,OAAO,IAAI,CAAC;AAGhH,QAAM,oBAAoB,eAAe,SAAS,IAAI,SAAS;AAC/D,MAAI,WAAW;AAEf,MAAI,mBAAmB;AACrB,eAAW,kBAAkB;AAG7B,UAAM,aAAa,eAAe,SAAS,WAAW,KAAK;AAC3D,QAAI,CAAC,WAAW,OAAO;AAErB,YAAM,gBAAgB;AAAA,QACpB,WAAW,IAAI;AAAA,QACf;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,4BAA4B,WAAW,KAAK;AAAA,MACtD;AACA,YAAM,MAAM,MAAM,iBAAiB,SAAS,IAAI,MAAM,oBAAoB,aAAa;AACvF,YAAM,iBAAiB,GAAG,WAAW,eAAe;AAAA,QAClD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,aAAa,IAAI,IAAI,MAAM,MAAM,oBAAoB,SAAS,eAAe,WAAW,IAAI,CAAC;AAAA,MAC5H,CAAC;AAGD,YAAM,gBAAgB;AAAA,QACpB,QAAQ;AAAA,QACR,WAAW,IAAI;AAAA,QACf;AAAA,QACA,MAAM,IAAI;AAAA,QACV;AAAA,QACA;AAAA,QACA,aAAa;AAAA,QACb,kBAAkB;AAAA,QAClB,gBAAgB;AAAA,QAChB,OAAO,WAAW;AAAA,QAClB,KAAK,KAAK,IAAI;AAAA,MAChB;AACA,oBAAc,MAAM,cAAc,aAAa;AAE/C,aAAO;AAAA,QACL,IAAI,IAAI;AAAA,QACR,MAAM;AAAA,QACN;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,WAAW,SAAS;AAAA,QAC5B,MAAM,IAAI;AAAA,QACV,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF,OAAO;AAEL,UAAM,WAAW,aAAa;AAC9B,UAAM,MAAM,SAAS,KAAK,OAAK,EAAE,cAAc,SAAS;AACxD,eAAW,KAAK,aAAa;AAAA,EAC/B;AAEA,QAAM,YAAY,MAAM,uBAAuB,SAAS,UAAU,IAAI,MAAM,WAAW,UAAU;AACjG,MAAI,CAAC,UAAU,UAAU;AAEvB,UAAM,gBAAgB,EAAE,WAAW,IAAI,IAAI,WAAW,QAAQ,YAAY,QAAQ,qBAAqB,UAAU,KAAK,GAAG;AACzH,UAAM,MAAM,MAAM,iBAAiB,SAAS,IAAI,MAAM,oBAAoB,aAAa;AACvF,UAAM,iBAAiB,GAAG,WAAW,eAAe;AAAA,MAClD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,aAAa,IAAI,IAAI,MAAM,MAAM,oBAAoB,SAAS,eAAe,WAAW,IAAI,CAAC;AAAA,IAC5H,CAAC;AAGD,UAAM,gBAAgB;AAAA,MACpB,QAAQ;AAAA,MACR,WAAW,IAAI;AAAA,MACf;AAAA,MACA,MAAM,IAAI;AAAA,MACV;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,OAAO,UAAU;AAAA,MACjB,KAAK,KAAK,IAAI;AAAA,IAChB;AACA,kBAAc,MAAM,cAAc,aAAa;AAE/C,WAAO,EAAE,IAAI,IAAI,IAAI,MAAM,mBAAmB,WAAW,QAAQ,YAAY,QAAQ,UAAU,SAAS,oBAAoB,MAAM,IAAI,MAAM,KAAK,KAAK;AAAA,EACxJ;AAGA,QAAM,aAAa;AAAA,IACjB,QAAQ;AAAA,IACR,WAAW,IAAI;AAAA,IACf;AAAA,IACA,MAAM,IAAI;AAAA,IACV;AAAA,IACA;AAAA,IACA,aAAa,UAAU;AAAA,IACvB,kBAAkB,UAAU;AAAA,IAC5B,gBAAgB,UAAU;AAAA,IAC1B,KAAK,KAAK,IAAI;AAAA,EAChB;AAEA,gBAAc,MAAM,cAAc,UAAU;AAE5C,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,MAAM;AAAA,IACN;AAAA,IACA,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,aAAa,UAAU;AAAA,IACvB,kBAAkB,UAAU;AAAA,IAC5B,MAAM,IAAI;AAAA,IACV,KAAK;AAAA,EACP;AACF;AAMA,eAAsB,eACpB,KACA,aACA,SAC+B;AAE/B,QAAM,WAAuD,IAAI,YAC7D,MAAM,qBAAqB,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,SAAS,IAAI,SAAS,IACjF,EAAE,OAAO,KAAK;AAGlB,MAAI,IAAI,SAAS,qBAAqB,SAAS,UAAU,MAAM;AAC7D,YAAQ,MAAM,KAAK,UAAU,EAAE,OAAO,sBAAsB,MAAM,IAAI,MAAM,MAAM,IAAI,MAAM,QAAQ,SAAS,UAAU,oBAAoB,CAAC,CAAC;AAC7I,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,gBAAgB,SAAS;AAAA,MACzB,KAAK;AAAA,IACP;AAAA,EACF;AAEA,MAAI,IAAI,SAAS,QAAQ;AAEvB,UAAM,cAAc;AAAA,MAClB,MAAM;AAAA,MACN,WAAW,IAAI;AAAA,MACf,cAAe,IAAI,SAAiB,QAAQ;AAAA,IAC9C;AACA,UAAM,UAAU,MAAM,iBAAiB,SAAS,IAAI,MAAM,QAAQ,WAAW;AAC7E,UAAM,MAAM,GAAG,WAAW,eAAe;AAAA,MACvC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM;AAAA,QACN,IAAI,IAAI;AAAA,QACR,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,MACb,CAAC;AAAA,IACH,CAAC;AACD,WAAO,EAAE,IAAI,IAAI,IAAI,MAAM,QAAQ,QAAQ,gBAAgB,MAAM,IAAI,MAAM,KAAK,KAAK;AAAA,EACvF;AAEA,MAAI,IAAI,SAAS,mBAAmB;AAClC,UAAM,YAAa,IAAI,SAAiB;AAGxC,QAAK,QAAgB,KAAU,EAAE,iBAAiB,QAAQ;AACxD,aAAO,MAAM,cAAc,KAAK,aAAa,SAAS,SAAS;AAAA,IACjE;AAGA,WAAO,MAAM,cAAc,KAAK,aAAa,SAAS,SAAS;AAAA,EACjE;AAEA,MAAI,IAAI,SAAS,QAAQ;AACvB,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,MAAM,IAAI;AAAA,MACV,MAAO,IAAI,SAAiB;AAAA,MAC5B,WAAY,IAAI,SAAiB;AAAA,MACjC,KAAK;AAAA,IACP;AAAA,EACF;AAEA,MAAI,IAAI,SAAS,oBAAoB;AACnC,UAAM,YAAa,IAAI,SAAiB;AACxC,UAAM,SAAU,IAAI,SAAiB;AACrC,UAAM,SAAU,IAAI,SAAiB;AAErC,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,MAAM,IAAI;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAY,IAAI,SAAiB;AAAA,MACjC,WAAW;AAAA,MACX,KAAK;AAAA,IACP;AAAA,EACF;AAGA,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,MAAM,IAAI;AAAA,IACV,MAAM,IAAI;AAAA,IACV,SAAS,IAAI;AAAA,IACb,gBAAgB,SAAS;AAAA,IACzB,QAAQ;AAAA,IACR,KAAK;AAAA,EACP;AACF;;;ADhYA;AACA,OAAOQ,YAAW;AAElB,IAAMC,OAAMD,OAAM,iCAAiC;AAMnD,eAAsB,WAAW,WAAiC,QAAqC;AACrG,MAAI;AACJ,MAAI;AACF,UAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,sBAAkB,GAAG,WAAY,GAAW,aAAa;AAAA,EAC3D,QAAQ;AACN,WAAO,KAAK,4DAA4D;AAAA,EAC1E;AAEA,QAAM,EAAE,aAAa,QAAQ,IAAI,MAAM,aAAa;AACpD,QAAM,QAAQ,YAAY,QAAQ,SAAS,IAAI,IAAI,+BAA+B;AAClF,EAAAC,KAAI,qCAAqC,KAAK;AAE9C,MAAI,iBAAiB;AACrB,MAAI,kBAAkB;AACtB,MAAI,YAAiB;AAErB,WAAS,WAAW;AAClB,sBAAkB;AAClB,QAAI,WAAW;AACb,UAAI;AAAE,kBAAU,MAAM;AAAA,MAAG,QAAQ;AAAA,MAAC;AAAA,IACpC;AAEA,QAAI,CAAC,WAAW;AACd,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,CAAC,WAAW;AACd,YAAQ,GAAG,UAAU,QAAQ;AAC7B,YAAQ,GAAG,WAAW,QAAQ;AAAA,EAChC;AAEA,MAAI,QAAQ;AACV,WAAO,iBAAiB,SAAS,MAAM;AACrC,wBAAkB;AAClB,UAAI,WAAW;AACb,YAAI;AAAE,oBAAU,MAAM;AAAA,QAAG,QAAQ;AAAA,QAAC;AAAA,MACpC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,WAAS,UAAU;AACjB,QAAI,QAAQ,QAAS;AACrB,UAAM,KAAK,IAAI,gBAAgB,KAAK;AACpC,gBAAY;AAEZ,OAAG,GAAG,QAAQ,MAAM;AAClB,MAAAA,KAAI,mCAAmC;AACvC,uBAAiB;AACjB,YAAM,SAAS,EAAE,OAAO,aAAa,UAAU,aAAa,SAAS,YAAY;AACjF,UAAI,UAAW,WAAU,MAAM;AAAA,UAC1B,SAAQ,MAAM,KAAK,UAAU,MAAM,CAAC;AAAA,IAC3C,CAAC;AAED,OAAG,GAAG,WAAW,OAAO,SAAc;AACpC,MAAAA,KAAI,qCAAqC;AACzC,UAAI;AACF,cAAM,WAAW,KAAK,MAAM,KAAK,SAAS,CAAC;AAC3C,QAAAA,KAAI,oBAAoB,SAAS,IAAI;AACrC,YAAI,SAAS,SAAS,WAAW;AAC/B,gBAAM,SAAS,MAAM,eAAe,SAAS,SAAS,aAAa,OAAO;AAC1E,UAAAA,KAAI,yBAAyB,OAAO,EAAE;AAEtC,cAAI,UAAW,WAAU,MAAM;AAAA,cAC1B,SAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AAGvC,yBAAe;AACf,cAAI;AACF,YAAAC,KAAG,eAAe,MAAM,eAAe,KAAK,UAAU,EAAE,GAAG,QAAQ,KAAK,KAAK,IAAI,EAAE,CAAC,IAAI,IAAI;AAAA,UAC9F,QAAQ;AAAA,UAAC;AAGT,cAAI,OAAO,KAAK;AACd,gBAAI;AACF,oBAAM,MAAM,cAAc,cAAc;AAAA,gBACtC,QAAQ;AAAA,gBACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,gBAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,aAAa,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;AAAA,cACzE,CAAC;AAAA,YACH,SAAS,QAAa;AACpB,oBAAMD,OAAM,EAAE,OAAO,aAAa,IAAI,OAAO,IAAI,SAAS,OAAO,MAAM,EAAE;AACzE,kBAAI,UAAW,WAAUA,IAAG;AAAA,kBACvB,SAAQ,MAAM,KAAK,UAAUA,IAAG,CAAC;AAAA,YACxC;AAAA,UACF;AAAA,QACF;AAGA,YAAI,SAAS,SAAS,qBAAqB;AACzC,gBAAM,MAAM,SAAS,WAAW,CAAC;AACjC,gBAAM,eAAe;AAAA,YACnB,OAAO;AAAA,YACP,WAAW,IAAI;AAAA,YACf,MAAM,IAAI;AAAA,YACV,aAAa,IAAI;AAAA,YACjB,WAAW,IAAI;AAAA,YACf,UAAU,IAAI;AAAA,YACd,MAAM,SAAS;AAAA,YACf,KAAK,KAAK,IAAI;AAAA,UAChB;AACA,cAAI,UAAW,WAAU,YAAY;AAAA,cAChC,SAAQ,IAAI,KAAK,UAAU,YAAY,CAAC;AAE7C,yBAAe;AACf,cAAI;AACF,YAAAC,KAAG,eAAe,MAAM,eAAe,KAAK,UAAU,YAAY,IAAI,IAAI;AAAA,UAC5E,QAAQ;AAAA,UAAC;AAAA,QACX;AAAA,MACF,SAAS,KAAU;AACjB,cAAMD,OAAM,EAAE,OAAO,iBAAiB,SAAS,OAAO,GAAG,EAAE;AAC3D,YAAI,UAAW,WAAUA,IAAG;AAAA,YACvB,SAAQ,MAAM,KAAK,UAAUA,IAAG,CAAC;AAAA,MACxC;AAAA,IACF,CAAC;AAED,OAAG,GAAG,SAAS,MAAM;AACnB,kBAAY;AACZ,UAAI,mBAAmB,CAAC,QAAQ,SAAS;AACvC,cAAMA,OAAM,EAAE,OAAO,gBAAgB,aAAa,eAAe;AACjE,YAAI,UAAW,WAAUA,IAAG;AAAA,YACvB,SAAQ,MAAM,KAAK,UAAUA,IAAG,CAAC;AAEtC,mBAAW,SAAS,cAAc;AAClC,yBAAiB,KAAK,IAAI,iBAAiB,GAAG,GAAK;AAAA,MACrD;AAAA,IACF,CAAC;AAED,OAAG,GAAG,SAAS,CAAC,QAAa;AAC3B,YAAMA,OAAM,EAAE,OAAO,SAAS,SAAS,IAAI,QAAQ;AACnD,UAAI,UAAW,WAAUA,IAAG;AAAA,UACvB,SAAQ,MAAM,KAAK,UAAUA,IAAG,CAAC;AAAA,IACxC,CAAC;AAAA,EACH;AAEA,UAAQ;AAER,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,QAAQ;AACV,aAAO,iBAAiB,SAAS,MAAM,QAAQ,CAAC;AAAA,IAClD;AAAA,EACF,CAAC;AACH;;;AhBnJA;AACA,OAAOE,YAAW;AAElB,IAAMC,OAAMD,OAAM,yBAAyB;AAE3C,IAAI,gBAAgB;AAGpB,IAAI,iBAAiB;AACrB,IAAI,kBAA0C;AAG9C,IAAI,qBAA0B;AAC9B,IAAI,aAA0B,oBAAI,IAAI;AAGtC,IAAI,gBAA6B,oBAAI,IAAI;AACzC,IAAI,yBAA8B;AAGlC,IAAM,cAAc;AAQpB,SAAS,cAAc,WAA2B;AAChD,SAAOE,MAAK,KAAK,WAAW,WAAW;AACzC;AAEA,SAAS,kBAAkB,WAAkC;AAC3D,QAAM,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAClD,QAAM,aAAa,cAAc,SAAS;AAC1C,MAAI;AACF,QAAIC,KAAG,WAAW,UAAU,GAAG;AAC7B,YAAM,OAAO,KAAK,MAAMA,KAAG,aAAa,YAAY,OAAO,CAAC;AAC5D,UAAI,KAAK,SAAS,MAAO,QAAO;AAAA,IAClC;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,EAAE,MAAM,OAAO,WAAW,GAAG,cAAc,CAAC,EAAE;AACvD;AAEA,SAAS,YAAY,WAAmB,MAAc,SAAiB,UAAkB;AACvF,QAAM,WAAW,kBAAkB,SAAS;AAC5C,WAAS,aAAa;AACtB,WAAS,aAAa,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,SAAS,SAAS,CAAC;AACtE,EAAAA,KAAG,cAAc,cAAc,SAAS,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC9E;AAEA,SAAS,YAAY,WAAmB,eAAuB,YAA4E;AACzI,QAAM,WAAW,kBAAkB,SAAS;AAC5C,QAAM,YAAY,aAAa,SAAS;AACxC,SAAO;AAAA,IACL,SAAS,aAAa;AAAA,IACtB;AAAA,IACA,OAAO,SAAS;AAAA,EAClB;AACF;AAEA,SAAS,iBAAiB,QAAa;AACrC,EAAC,QAAgB,KAAK,EAAE,iBAAiB,OAAO,aAAaD,MAAK,KAAKE,IAAG,QAAQ,GAAG,aAAa,YAAY;AAC9G,EAAC,QAAgB,KAAK,EAAE,cAAc,OAAO,cAAc;AAC3D,EAAC,QAAgB,KAAK,EAAE,cAAc,OAAO,WAAY,QAAgB,KAAK,EAAE,eAAe;AAC/F,EAAC,QAAgB,KAAK,EAAE,cAAc,OAAO,WAAY,QAAgB,KAAK,EAAE,gBAAgB,YAAY,uCAAuC;AACnJ,EAAC,QAAgB,KAAK,EAAE,aAAa,OAAO,aAAa;AACzD,YAAU,IAAI;AAChB;AAEA,SAAS,UAAU,MAAc,QAAa,MAAc,OAAe,UAAmC,CAAC,GAAG;AAChH,QAAM,aAAa,QAAQ,cAAc,yBAAyB,KAAK,IAAI,CAAC;AAC5E,MAAI,CAAC,MAAO;AAIZ,QAAM,SAAS,oBAAoB,IAAI;AAEvC,QAAM,QAAQ;AAAA,IACZ,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oBAAoB,oBAAoB,MAAM;AAAA,IACzE,MAAM,KAAK,UAAU,EAAE,QAAQ,MAAM,WAAW,CAAC;AAAA,EACnD,CAAC,EAAE,MAAM,MAAM;AAAA,EAAC,CAAC;AACnB;AAEA,eAAe,gBAAgB,QAAa,KAAU,MAAc,OAAe;AACjF,MAAI;AACF,qBAAiB,MAAM;AACvB,UAAM,aAAa,MAAM,WAAW;AACpC,QAAI,CAAC,WAAW,QAAS;AACzB,UAAM,UAAU,WAAW,MAAM;AACjC,QAAI,CAAC,QAAS;AAEd,yBAAqB,YAAY,YAAY;AAC3C,UAAI;AACF,cAAM,UAAW,QAAgB,KAAK,EAAE,gBAAgB,YAAY,SAAS;AAC7E,cAAM,aAAa,IAAI,gBAAgB;AACvC,cAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,IAAK;AAC1D,cAAM,OAAO,MAAM,MAAM,uCAAuC,OAAO,YAAY,OAAO,gBAAgB,EAAE,QAAQ,WAAW,OAAO,CAAC;AACvI,qBAAa,OAAO;AACpB,YAAI,CAAC,KAAK,GAAI;AACd,cAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,cAAM,QAAQ,KAAK,UAAU,CAAC;AAE9B,mBAAW,QAAQ,OAAO;AACxB,gBAAM,MAAM,GAAG,KAAK,OAAO,IAAI,KAAK,MAAM;AAC1C,cAAI,WAAW,IAAI,GAAG,EAAG;AACzB,cAAI,KAAK,QAAQ,IAAK;AAEtB,cAAI,QAAQ,OAAO,2CAA2C,KAAK,OAAO,IAAI,KAAK,MAAM,KAAK,KAAK,KAAK,QAAQ;AAChH,cAAI;AACF,6BAAiB,MAAM;AACvB,kBAAM,eAAe,MAAM,UAAU,KAAK,SAAS,OAAO,KAAK,MAAM,CAAC;AACtE,gBAAI,aAAa,SAAS;AACxB,yBAAW,IAAI,GAAG;AAClB,kBAAI,QAAQ,OAAO,oCAAoC,KAAK,KAAK,cAAc,KAAK,OAAO,EAAE;AAG7F,wBAAU;AAAA;AAAA,gBAA0C,KAAK,KAAK,0BAA0B,KAAK,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA;AAAA,gDAAwD,IAAI,QAAQ,MAAM,OAAO,EAAE,YAAY,+BAA+B,CAAC;AAGhP,kBAAI;AACF,sBAAM,UAAUF,MAAK,KAAKE,IAAG,QAAQ,GAAG,aAAa,oBAAoB,mBAAmB;AAC5F,oBAAI,CAACD,KAAG,WAAW,OAAO,GAAG;AAC3B,sBAAI,QAAQ,OAAO,kEAA6D;AAChF,mCAAiB,MAAM;AACvB,wBAAM,YAAY,MAAM,YAAY;AACpC,sBAAI,UAAU,SAAS;AACrB,wBAAI,QAAQ,OAAO,wDAAwD;AAC3E,0BAAM,sBAAsB,QAAQ,IAAI,MAAM;AAAA,kBAChD;AAAA,gBACF;AAAA,cACF,SAAS,KAAU;AACjB,oBAAI,QAAQ,OAAO,gDAAgD,IAAI,OAAO;AAAA,cAChF;AAAA,YACF;AAAA,UACF,SAAS,KAAK;AACZ,uBAAW,IAAI,GAAG;AAAA,UACpB;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AAAA,MAEd;AAAA,IACF,GAAG,GAAK;AAAA,EACV,SAAS,KAAU;AACjB,QAAI,QAAQ,OAAO,gDAAgD,IAAI,OAAO;AAAA,EAChF;AACF;AAEA,eAAe,sBAAsB,QAAa,QAAa;AAC7D,MAAI;AACF,QAAI,sBAAgC,CAAC;AACrC,QAAI,QAAQ,YAAY,MAAM,QAAQ,OAAO,QAAQ,GAAG;AACtD,4BAAsB,OAAO;AAAA,IAC/B;AAEA,QAAI,oBAAoB,WAAW,EAAG;AAEtC,eAAW,aAAa,qBAAqB;AAC3C,YAAM,cAAc,eAAe,SAAS,IAAI,SAAS;AACzD,UAAI,CAAC,YAAa;AAClB,UAAI;AACF,cAAM,EAAE,cAAAE,cAAa,IAAI,MAAM;AAC/B,yBAAiB,MAAM;AACvB,cAAMA,cAAa,WAAW,YAAY,MAAM,OAAO,YAAY,YAAY,GAAG,YAAY,WAAW;AAAA,MAC3G,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF,SAAS,KAAU;AACjB,YAAQ,OAAO,+CAA+C,IAAI,OAAO;AAAA,EAC3E;AACF;AAEA,eAAe,uBAAuB,QAAa,KAAU,MAAc,OAAe;AACxF,MAAI,eAAgB;AACpB,mBAAiB;AACjB,oBAAkB,IAAI,gBAAgB;AAEtC,2BAAyB,YAAY,MAAM;AACzC,QAAI,eAAgB,eAAc,MAAM;AAAA,EAC1C,GAAG,IAAI,KAAK,GAAI;AAEhB,mBAAiB,MAAM;AAEvB,aAAW,CAAC,UAAe;AACzB,SAAK,MAAM,WAAW,sBAAsB,MAAM,WAAW,qBAAqB,MAAM,WAAW;AACjG,YAAM,MAAM,MAAM,MAAM,GAAG,MAAM,IAAI,IAAI,KAAK,IAAI,CAAC;AACnD,UAAI,cAAc,IAAI,GAAG,EAAG;AAC5B,oBAAc,IAAI,GAAG;AACrB,YAAM,WAAW;AAAA;AAAA,WAAmD,MAAM,SAAS;AAAA,QAAW,MAAM,IAAI;AAAA,QAAW,MAAM,oBAAoB,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,8CAA6I,MAAM,EAAE,qBAAqB,MAAM,IAAI,kBAAkB,MAAM,SAAS;AACrW,gBAAU,UAAU,IAAI,QAAQ,MAAM,OAAO,EAAE,YAAY,yBAAyB,GAAG,GAAG,CAAC;AAAA,IAC7F;AACA,QAAI,MAAM,SAAS,sBAAsB,MAAM,WAAW,YAAY;AACpE,YAAM,WAAW;AAAA;AAAA,WAAqD,MAAM,SAAS;AAAA,QAAW,MAAM,IAAI;AAAA,UAAa,MAAM,MAAM;AAAA;AAAA;AAAA,EAAqB,KAAK,UAAU,MAAM,QAAQ,MAAM,CAAC,CAAC;AAC7L,gBAAU,UAAU,IAAI,QAAQ,MAAM,OAAO,EAAE,YAAY,8BAA8B,MAAM,aAAa,KAAK,IAAI,CAAC,GAAG,CAAC;AAAA,IAC5H;AAAA,EACF,GAAG,gBAAgB,MAAM,EAAE,MAAM,CAAC,QAAQ;AACxC,QAAI,kBAAkB,CAAC,iBAAiB,OAAO,SAAS;AACtD,UAAI,QAAQ,QAAQ,uCAAuC,IAAI,OAAO,EAAE;AAAA,IAC1E;AAAA,EACF,CAAC;AACH;AAEA,SAAS,wBAAwB;AAC/B,mBAAiB;AACjB,MAAI,iBAAiB;AACnB,oBAAgB,MAAM;AACtB,sBAAkB;AAAA,EACpB;AACA,MAAI,wBAAwB;AAAE,kBAAc,sBAAsB;AAAG,6BAAyB;AAAA,EAAM;AACpG,gBAAc,MAAM;AACpB,MAAI,oBAAoB;AAAE,kBAAc,kBAAkB;AAAG,yBAAqB;AAAA,EAAM;AAC1F;AAEO,SAAS,SAAS,KAAU;AACjC,QAAM,UAAU;AAChB,MAAI,cAAe;AACnB,kBAAgB;AAEhB,MAAI,QAAQ,OAAO,2CAA2C,OAAO,EAAE;AACvE,QAAM,UAAU,IAAI,YAAY,GAAG,SAAS,WAAW,CAAC;AACxD,QAAM,QAAQ,QAAQ,yBAAyB,KAAK,QAAQ,kBAAkB,KAAK,CAAC;AACpF,QAAM,eAAe,EAAE,GAAG,OAAO,GAAI,MAAM,UAAU,CAAC,GAAI,GAAI,IAAI,UAAU,CAAC,EAAG;AAGhF,MAAI,aAAa;AAAA,IACf,MAAM;AAAA,IACN,aAAa;AAAA,IACb,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,QAAQ,EAAE,MAAM,UAAU,MAAM,CAAC,WAAW,YAAY,WAAW,UAAU,OAAO,WAAW,oBAAoB,WAAW,YAAY,EAAE;AAAA,QAC5I,SAAS,EAAE,MAAM,SAAS;AAAA,QAC1B,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,aAAa,EAAE,MAAM,SAAS;AAAA,QAC9B,MAAM,EAAE,MAAM,SAAS;AAAA,QACvB,WAAW,EAAE,MAAM,SAAS;AAAA,QAC5B,cAAc,EAAE,MAAM,SAAS;AAAA,QAC/B,WAAW,EAAE,MAAM,SAAS;AAAA,QAC5B,QAAQ,EAAE,MAAM,SAAS;AAAA,MAC3B;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACrB;AAAA,IACA,MAAM,QAAQ,KAAa,QAAa;AACtC,MAAAJ,KAAI,6CAA6C,OAAO,QAAQ,MAAM;AACtE,UAAI;AACF,eAAO,MAAM,qBAAqB,QAAQ,cAAc,GAAG;AAAA,MAC7D,SAAS,OAAY;AACnB,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,MAAM,OAAO,GAAG,CAAC,EAAE;AAAA,MACxE;AAAA,IACF;AAAA,EACF,CAAC;AAGD,MAAI,gBAAgB;AAAA,IAClB,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,IACb,SAAS,OAAO,QAAa;AAC3B,UAAI;AACF,YAAI,QAAQ,OAAO,kDAAkD,KAAK,UAAU,IAAI,IAAI,CAAC,WAAW,OAAO,IAAI,IAAI,GAAG;AAC1H,cAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,OAAQ,OAAO,IAAI,SAAS,WAAW,IAAI,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO,IAAI,CAAC;AACzH,cAAM,SAAS,KAAK,CAAC,KAAK;AAC1B,cAAM,SAAS,MAAM,qBAAqB,EAAE,OAAO,GAAG,cAAc,GAAG;AACvE,eAAO,EAAE,MAAM,aAAa,OAAO,YAAY,CAAC;AAAA;AAAA,EAAS,OAAO,WAAW,WAAW,SAAS,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC,GAAG;AAAA,MACnI,SAAS,OAAY;AACnB,eAAO,EAAE,MAAM,iBAAY,MAAM,OAAO,GAAG;AAAA,MAC7C;AAAA,IACF;AAAA,EACF,CAAC;AAGD,MAAI,gBAAgB;AAAA,IAClB,IAAI;AAAA,IACJ,OAAO,YAAY;AACjB,UAAI;AAAE,cAAM,wBAAwB;AAAA,MAAG,QAAQ;AAAA,MAAC;AAEhD,YAAM,cAAe,QAAgB,KAAK,EAAE,yBAAyB;AACrE,YAAM,YAAa,QAAgB,KAAK,EAAE,wBAAwB;AAClE,YAAM,uBAAuB,cAAc,KAAK,aAAa,SAAS;AACtE,YAAM,gBAAgB,cAAc,KAAK,aAAa,SAAS;AAAA,IACjE;AAAA,IACA,MAAM,MAAM,sBAAsB;AAAA,EACpC,CAAC;AAGD,MAAI,YAAY,CAAC,EAAE,QAAQ,MAAW;AACpC,UAAM,UAAU,QAAQ,QAAQ,SAAS,EAAE,YAAY,gCAAgC;AACvF,YAAQ,QAAQ,QAAQ,EAAE,YAAY,2BAA2B,EAAE,OAAO,YAAY;AACpF,uBAAiB,YAAY;AAC7B,YAAM,MAAM,MAAM,UAAU;AAC5B,cAAQ,IAAI,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAAA,IAC/C,CAAC;AACD,YAAQ,QAAQ,SAAS,EAAE,YAAY,6BAA6B,EAAE,OAAO,YAAY;AACvF,uBAAiB,YAAY;AAC7B,YAAM,MAAM,MAAM,WAAW;AAC7B,cAAQ,IAAI,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAAA,IAC/C,CAAC;AACD,YAAQ,QAAQ,UAAU,EAAE,YAAY,0BAA0B,EAAE,OAAO,wBAAwB,wBAAwB,EAAE,OAAO,sBAAsB,sBAAsB,EAAE,OAAO,OAAO,YAAiB;AAC/M,uBAAiB,YAAY;AAC7B,YAAM,OAAiB,CAAC;AACxB,UAAI,QAAQ,QAAS,MAAK,KAAK,aAAa,QAAQ,OAAO;AAC3D,UAAI,QAAQ,MAAO,MAAK,KAAK,WAAW,QAAQ,KAAK;AACrD,YAAM,MAAM,MAAM,YAAY,IAAI;AAClC,cAAQ,IAAI,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAAA,IAC/C,CAAC;AAAA,EACH,GAAG,EAAE,UAAU,CAAC,SAAS,EAAE,CAAC;AAC9B;AAEA,eAAe,qBAAqB,QAAa,QAAa,KAAU;AACtE,QAAM,EAAE,OAAO,IAAI;AACnB,mBAAiB,MAAM;AAEvB,UAAQ,QAAQ;AAAA,IACd,KAAK,WAAW;AACd,YAAM,EAAE,SAAS,MAAM,IAAI;AAC3B,YAAM,YAAY,QAAQ,aAAaC,MAAK,KAAKE,IAAG,QAAQ,GAAG,aAAa,YAAY;AACxF,YAAM,iBAAiB,MAAM,YAAY,CAAC,aAAa,OAAO,CAAC;AAC/D,YAAM,YAAY,eAAe,KAAK;AACtC,UAAI,CAAC,aAAa,UAAU,WAAW,EAAG,OAAM,IAAI,MAAM,0BAA0B,OAAO,EAAE;AAC7F,gBAAU,KAAK,CAAC,GAAQ,OAAY,EAAE,SAAS,cAAc,MAAM,EAAE,SAAS,cAAc,EAAE;AAC9F,YAAM,OAAO,UAAU,CAAC;AACxB,YAAM,QAAQ,KAAK,SAAS,cAAc;AAC1C,YAAM,SAAS,YAAY,WAAW,OAAO,OAAO,mBAAmB,GAAI;AAC3E,UAAI,CAAC,OAAO,QAAS,OAAM,IAAI,MAAM,iBAAiB;AAEtD,YAAM,SAAS,MAAM,kBAAkB,KAAK,aAAa,SAAS,MAAM,SAAS,GAAG,QAAQ,KAAK,UAAU,KAAK,IAAI,MAAS;AAC7H,kBAAY,WAAW,OAAO,SAAS,KAAK,IAAI;AAChD,aAAO,EAAE,QAAQ,QAAQ,WAAW,OAAO,MAAM,WAAW,SAAS,mBAAmB,KAAK,IAAI,QAAQ,KAAK,SAAS;AAAA,IACzH;AAAA,IACA,KAAK;AAAY,cAAQ,MAAM,YAAY,OAAO,UAAU,CAAC,aAAa,OAAO,OAAO,IAAI,CAAC,CAAC,GAAG;AAAA,IACjG,KAAK;AAAW,cAAQ,MAAM,WAAW,GAAG;AAAA,IAC5C,KAAK,UAAU;AACb,YAAM,WAAW,MAAM,YAAY;AACnC,YAAM,UAAU,MAAM,WAAW;AACjC,aAAO,EAAE,UAAU,SAAS,MAAM,SAAS,QAAQ,KAAK;AAAA,IAC1D;AAAA,IACA,KAAK,WAAW;AACd,YAAM,SAAS;AACf,YAAM,QAAQ,MAAM,WAAW,GAAG,KAAK;AACvC,YAAM,OAAO,MAAM,WAAW,GAAG,KAAK;AACtC,UAAI,MAAM,IAAM,QAAO,EAAE,QAAQ,OAAO,SAAS,MAAM,SAAS,yBAAyB;AACzF,YAAM,YAAY;AAClB,aAAO,EAAE,QAAQ,MAAM,YAAY,MAAM,SAAS,uBAAuB;AAAA,IAC3E;AAAA,IACA,KAAK;AAAoB,cAAQ,MAAM,gBAAgB,GAAG;AAAA,IAC1D,KAAK,WAAW;AACd,YAAM,EAAE,WAAW,cAAc,WAAW,OAAO,IAAI;AACvD,cAAQ,MAAM,kBAAkB,WAAW,cAAc,WAAW,KAAK,UAAU,MAAM,CAAC,GAAG;AAAA,IAC/F;AAAA,IACA,KAAK;AAAc,cAAQ,MAAM,cAAc,GAAG;AAAA,IAClD;AAAS,YAAM,IAAI,MAAM,mBAAmB,MAAM,EAAE;AAAA,EACtD;AACF;AAEO,IAAM,SAAS;AAAA,EACpB,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,UAAU;AAAA,EACV;AACF;AAEA,IAAO,gBAAQ;",
6
6
  "names": ["path", "fs", "init_config", "Utils", "Beef", "Utils", "normalizeDescription", "CachedKeyDeriver", "path", "fs", "init_config", "init_config", "fs", "Utils", "getBSVAgentWallet", "BSVAgentWallet", "path", "os", "fs", "ServiceCategory", "fs", "fs", "fileURLToPath", "path", "os", "__dirname", "_sdk", "getSdk", "fileURLToPath", "path", "os", "__dirname", "BSVAgentWallet", "fs", "wallet", "identityKey", "fs", "getBSVAgentWallet", "_sdk", "getSdk", "fileURLToPath", "path", "os", "__dirname", "BSVAgentWallet", "fs", "Transaction", "Beef", "PushDrop", "getBSVAgentWallet", "BSVAgentWallet", "txid", "getBSVAgentWallet", "BSVAgentWallet", "fs", "fs", "fs", "fs", "fs", "_sdk", "getSdk", "fileURLToPath", "path", "os", "__dirname", "getBSVAgentWallet", "BSVAgentWallet", "fs", "debug", "log", "fs", "debug", "log", "path", "fs", "os", "cmdAdvertise"]
7
7
  }
package/index.ts CHANGED
@@ -228,7 +228,7 @@ function stopBackgroundService() {
228
228
  }
229
229
 
230
230
  export function register(api: any) {
231
- const version = "0.8.17";
231
+ const version = "0.8.18";
232
232
  if (isInitialized) return;
233
233
  isInitialized = true;
234
234
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-overlay-plugin",
3
- "version": "0.8.17",
3
+ "version": "0.8.18",
4
4
  "description": "Openclaw BSV Overlay — agent discovery, service marketplace, and micropayments on the BSV blockchain",
5
5
  "publishConfig": {
6
6
  "access": "public"