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