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