openclaw-overlay-plugin 0.7.22
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 +406 -0
- package/SKILL.md +78 -0
- package/clawdbot.plugin.json +106 -0
- package/dist/cli-main.d.ts +7 -0
- package/dist/cli-main.js +192 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +14 -0
- package/dist/core/config.d.ts +11 -0
- package/dist/core/config.js +13 -0
- package/dist/core/index.d.ts +25 -0
- package/dist/core/index.js +26 -0
- package/dist/core/payment.d.ts +16 -0
- package/dist/core/payment.js +94 -0
- package/dist/core/types.d.ts +94 -0
- package/dist/core/types.js +4 -0
- package/dist/core/verify.d.ts +28 -0
- package/dist/core/verify.js +104 -0
- package/dist/core/wallet.d.ts +99 -0
- package/dist/core/wallet.js +219 -0
- package/dist/scripts/baemail/commands.d.ts +64 -0
- package/dist/scripts/baemail/commands.js +258 -0
- package/dist/scripts/baemail/handler.d.ts +36 -0
- package/dist/scripts/baemail/handler.js +284 -0
- package/dist/scripts/baemail/index.d.ts +5 -0
- package/dist/scripts/baemail/index.js +5 -0
- package/dist/scripts/config.d.ts +48 -0
- package/dist/scripts/config.js +68 -0
- package/dist/scripts/index.d.ts +7 -0
- package/dist/scripts/index.js +7 -0
- package/dist/scripts/messaging/connect.d.ts +8 -0
- package/dist/scripts/messaging/connect.js +114 -0
- package/dist/scripts/messaging/handlers.d.ts +21 -0
- package/dist/scripts/messaging/handlers.js +334 -0
- package/dist/scripts/messaging/inbox.d.ts +11 -0
- package/dist/scripts/messaging/inbox.js +51 -0
- package/dist/scripts/messaging/index.d.ts +8 -0
- package/dist/scripts/messaging/index.js +8 -0
- package/dist/scripts/messaging/poll.d.ts +7 -0
- package/dist/scripts/messaging/poll.js +52 -0
- package/dist/scripts/messaging/send.d.ts +7 -0
- package/dist/scripts/messaging/send.js +43 -0
- package/dist/scripts/output.d.ts +12 -0
- package/dist/scripts/output.js +19 -0
- package/dist/scripts/overlay/discover.d.ts +7 -0
- package/dist/scripts/overlay/discover.js +72 -0
- package/dist/scripts/overlay/index.d.ts +7 -0
- package/dist/scripts/overlay/index.js +7 -0
- package/dist/scripts/overlay/registration.d.ts +19 -0
- package/dist/scripts/overlay/registration.js +176 -0
- package/dist/scripts/overlay/services.d.ts +29 -0
- package/dist/scripts/overlay/services.js +167 -0
- package/dist/scripts/overlay/transaction.d.ts +42 -0
- package/dist/scripts/overlay/transaction.js +103 -0
- package/dist/scripts/payment/build.d.ts +24 -0
- package/dist/scripts/payment/build.js +54 -0
- package/dist/scripts/payment/commands.d.ts +15 -0
- package/dist/scripts/payment/commands.js +73 -0
- package/dist/scripts/payment/index.d.ts +6 -0
- package/dist/scripts/payment/index.js +6 -0
- package/dist/scripts/payment/types.d.ts +56 -0
- package/dist/scripts/payment/types.js +4 -0
- package/dist/scripts/services/index.d.ts +6 -0
- package/dist/scripts/services/index.js +6 -0
- package/dist/scripts/services/queue.d.ts +11 -0
- package/dist/scripts/services/queue.js +28 -0
- package/dist/scripts/services/request.d.ts +7 -0
- package/dist/scripts/services/request.js +82 -0
- package/dist/scripts/services/respond.d.ts +11 -0
- package/dist/scripts/services/respond.js +132 -0
- package/dist/scripts/types.d.ts +107 -0
- package/dist/scripts/types.js +4 -0
- package/dist/scripts/utils/index.d.ts +6 -0
- package/dist/scripts/utils/index.js +6 -0
- package/dist/scripts/utils/merkle.d.ts +12 -0
- package/dist/scripts/utils/merkle.js +47 -0
- package/dist/scripts/utils/storage.d.ts +66 -0
- package/dist/scripts/utils/storage.js +211 -0
- package/dist/scripts/utils/woc.d.ts +26 -0
- package/dist/scripts/utils/woc.js +91 -0
- package/dist/scripts/wallet/balance.d.ts +22 -0
- package/dist/scripts/wallet/balance.js +240 -0
- package/dist/scripts/wallet/identity.d.ts +70 -0
- package/dist/scripts/wallet/identity.js +151 -0
- package/dist/scripts/wallet/index.d.ts +6 -0
- package/dist/scripts/wallet/index.js +6 -0
- package/dist/scripts/wallet/setup.d.ts +15 -0
- package/dist/scripts/wallet/setup.js +105 -0
- package/dist/scripts/x-verification/commands.d.ts +27 -0
- package/dist/scripts/x-verification/commands.js +222 -0
- package/dist/scripts/x-verification/index.d.ts +4 -0
- package/dist/scripts/x-verification/index.js +4 -0
- package/dist/services/built-in/api-proxy/index.d.ts +6 -0
- package/dist/services/built-in/api-proxy/index.js +23 -0
- package/dist/services/built-in/code-develop/index.d.ts +6 -0
- package/dist/services/built-in/code-develop/index.js +23 -0
- package/dist/services/built-in/code-review/index.d.ts +10 -0
- package/dist/services/built-in/code-review/index.js +51 -0
- package/dist/services/built-in/image-analysis/index.d.ts +6 -0
- package/dist/services/built-in/image-analysis/index.js +33 -0
- package/dist/services/built-in/memory-store/index.d.ts +6 -0
- package/dist/services/built-in/memory-store/index.js +22 -0
- package/dist/services/built-in/roulette/index.d.ts +6 -0
- package/dist/services/built-in/roulette/index.js +27 -0
- package/dist/services/built-in/summarize/index.d.ts +6 -0
- package/dist/services/built-in/summarize/index.js +21 -0
- package/dist/services/built-in/tell-joke/handler.d.ts +7 -0
- package/dist/services/built-in/tell-joke/handler.js +122 -0
- package/dist/services/built-in/tell-joke/index.d.ts +9 -0
- package/dist/services/built-in/tell-joke/index.js +31 -0
- package/dist/services/built-in/translate/index.d.ts +6 -0
- package/dist/services/built-in/translate/index.js +21 -0
- package/dist/services/built-in/web-research/index.d.ts +9 -0
- package/dist/services/built-in/web-research/index.js +51 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.js +14 -0
- package/dist/services/loader.d.ts +77 -0
- package/dist/services/loader.js +292 -0
- package/dist/services/manager.d.ts +86 -0
- package/dist/services/manager.js +255 -0
- package/dist/services/registry.d.ts +98 -0
- package/dist/services/registry.js +204 -0
- package/dist/services/types.d.ts +230 -0
- package/dist/services/types.js +30 -0
- package/dist/test/cli.test.d.ts +7 -0
- package/dist/test/cli.test.js +329 -0
- package/dist/test/comprehensive-overlay.test.d.ts +13 -0
- package/dist/test/comprehensive-overlay.test.js +593 -0
- package/dist/test/key-derivation.test.d.ts +12 -0
- package/dist/test/key-derivation.test.js +86 -0
- package/dist/test/overlay-submit.test.d.ts +10 -0
- package/dist/test/overlay-submit.test.js +460 -0
- package/dist/test/request-response-flow.test.d.ts +5 -0
- package/dist/test/request-response-flow.test.js +209 -0
- package/dist/test/service-system.test.d.ts +5 -0
- package/dist/test/service-system.test.js +190 -0
- package/dist/test/utils/server-logic.d.ts +98 -0
- package/dist/test/utils/server-logic.js +286 -0
- package/dist/test/wallet.test.d.ts +7 -0
- package/dist/test/wallet.test.js +146 -0
- package/index.ts +1965 -0
- package/openclaw.plugin.json +106 -0
- package/package.json +73 -0
- package/src/cli-main.ts +230 -0
- package/src/cli.ts +16 -0
- package/src/core/README.md +246 -0
- package/src/core/config.ts +21 -0
- package/src/core/index.ts +42 -0
- package/src/core/payment.ts +111 -0
- package/src/core/types.ts +102 -0
- package/src/core/verify.ts +119 -0
- package/src/core/wallet.ts +282 -0
- package/src/scripts/baemail/commands.ts +326 -0
- package/src/scripts/baemail/handler.ts +338 -0
- package/src/scripts/baemail/index.ts +6 -0
- package/src/scripts/config.ts +81 -0
- package/src/scripts/index.ts +8 -0
- package/src/scripts/messaging/connect.ts +121 -0
- package/src/scripts/messaging/handlers.ts +394 -0
- package/src/scripts/messaging/inbox.ts +64 -0
- package/src/scripts/messaging/index.ts +9 -0
- package/src/scripts/messaging/poll.ts +59 -0
- package/src/scripts/messaging/send.ts +54 -0
- package/src/scripts/output.ts +21 -0
- package/src/scripts/overlay/discover.ts +81 -0
- package/src/scripts/overlay/index.ts +8 -0
- package/src/scripts/overlay/registration.ts +199 -0
- package/src/scripts/overlay/services.ts +199 -0
- package/src/scripts/overlay/transaction.ts +124 -0
- package/src/scripts/payment/build.ts +65 -0
- package/src/scripts/payment/commands.ts +92 -0
- package/src/scripts/payment/index.ts +7 -0
- package/src/scripts/payment/types.ts +62 -0
- package/src/scripts/services/index.ts +7 -0
- package/src/scripts/services/queue.ts +35 -0
- package/src/scripts/services/request.ts +98 -0
- package/src/scripts/services/respond.ts +149 -0
- package/src/scripts/types.ts +121 -0
- package/src/scripts/utils/index.ts +7 -0
- package/src/scripts/utils/merkle.ts +57 -0
- package/src/scripts/utils/storage.ts +231 -0
- package/src/scripts/utils/woc.ts +106 -0
- package/src/scripts/wallet/balance.ts +277 -0
- package/src/scripts/wallet/identity.ts +203 -0
- package/src/scripts/wallet/index.ts +7 -0
- package/src/scripts/wallet/setup.ts +121 -0
- package/src/scripts/x-verification/commands.ts +256 -0
- package/src/scripts/x-verification/index.ts +5 -0
- package/src/services/built-in/api-proxy/index.ts +26 -0
- package/src/services/built-in/api-proxy/prompt.md +26 -0
- package/src/services/built-in/code-develop/index.ts +26 -0
- package/src/services/built-in/code-develop/prompt.md +35 -0
- package/src/services/built-in/code-review/index.ts +54 -0
- package/src/services/built-in/code-review/prompt.md +105 -0
- package/src/services/built-in/image-analysis/index.ts +36 -0
- package/src/services/built-in/image-analysis/prompt.md +42 -0
- package/src/services/built-in/memory-store/index.ts +25 -0
- package/src/services/built-in/memory-store/prompt.md +45 -0
- package/src/services/built-in/roulette/index.ts +30 -0
- package/src/services/built-in/roulette/prompt.md +35 -0
- package/src/services/built-in/summarize/index.ts +24 -0
- package/src/services/built-in/summarize/prompt.md +27 -0
- package/src/services/built-in/tell-joke/handler.ts +134 -0
- package/src/services/built-in/tell-joke/index.ts +34 -0
- package/src/services/built-in/tell-joke/prompt.md +59 -0
- package/src/services/built-in/translate/index.ts +24 -0
- package/src/services/built-in/translate/prompt.md +23 -0
- package/src/services/built-in/web-research/index.ts +54 -0
- package/src/services/built-in/web-research/prompt.md +110 -0
- package/src/services/index.ts +16 -0
- package/src/services/loader.ts +344 -0
- package/src/services/manager.ts +304 -0
- package/src/services/registry.ts +246 -0
- package/src/services/types.ts +259 -0
- package/src/test/cli.test.ts +352 -0
- package/src/test/comprehensive-overlay.test.ts +729 -0
- package/src/test/key-derivation.test.ts +102 -0
- package/src/test/overlay-submit.test.ts +570 -0
- package/src/test/request-response-flow.test.ts +252 -0
- package/src/test/service-system.test.ts +241 -0
- package/src/test/utils/server-logic.ts +368 -0
- package/src/test/wallet.test.ts +166 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baemail service handler - processes incoming paid messages.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import { OVERLAY_URL, PATHS } from '../config.js';
|
|
8
|
+
import { loadBaemailConfig } from './commands.js';
|
|
9
|
+
import { signRelayMessage } from '../wallet/identity.js';
|
|
10
|
+
import { verifyAndAcceptPayment } from '../messaging/handlers.js';
|
|
11
|
+
import { fetchWithTimeout } from '../utils/woc.js';
|
|
12
|
+
import { ensureStateDir } from '../utils/storage.js';
|
|
13
|
+
// Dynamic SDK import
|
|
14
|
+
let _sdk = null;
|
|
15
|
+
async function getSdk() {
|
|
16
|
+
if (_sdk)
|
|
17
|
+
return _sdk;
|
|
18
|
+
try {
|
|
19
|
+
_sdk = await import('@bsv/sdk');
|
|
20
|
+
return _sdk;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
throw new Error('Cannot load @bsv/sdk');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Process incoming baemail service request.
|
|
28
|
+
*/
|
|
29
|
+
export async function processBaemail(msg, identityKey, privKey) {
|
|
30
|
+
const input = (msg.payload?.input || msg.payload);
|
|
31
|
+
const payment = msg.payload?.payment;
|
|
32
|
+
// Load config
|
|
33
|
+
const config = loadBaemailConfig();
|
|
34
|
+
if (!config) {
|
|
35
|
+
const rejectPayload = {
|
|
36
|
+
requestId: msg.id,
|
|
37
|
+
serviceId: 'baemail',
|
|
38
|
+
status: 'rejected',
|
|
39
|
+
reason: 'Baemail service not configured on this agent.',
|
|
40
|
+
};
|
|
41
|
+
const sig = signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
|
|
42
|
+
await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: { 'Content-Type': 'application/json' },
|
|
45
|
+
body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
|
|
46
|
+
});
|
|
47
|
+
return { id: msg.id, type: 'service-request', serviceId: 'baemail', action: 'rejected', reason: 'not configured', from: msg.from, ack: true };
|
|
48
|
+
}
|
|
49
|
+
// Check blocklist
|
|
50
|
+
if (config.blocklist?.includes(msg.from)) {
|
|
51
|
+
const rejectPayload = {
|
|
52
|
+
requestId: msg.id,
|
|
53
|
+
serviceId: 'baemail',
|
|
54
|
+
status: 'rejected',
|
|
55
|
+
reason: 'Sender is blocked.',
|
|
56
|
+
};
|
|
57
|
+
const sig = signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
|
|
58
|
+
await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: { 'Content-Type': 'application/json' },
|
|
61
|
+
body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
|
|
62
|
+
});
|
|
63
|
+
return { id: msg.id, type: 'service-request', serviceId: 'baemail', action: 'rejected', reason: 'blocked', from: msg.from, ack: true };
|
|
64
|
+
}
|
|
65
|
+
// Validate message
|
|
66
|
+
const message = input?.message;
|
|
67
|
+
if (!message || typeof message !== 'string' || message.trim().length === 0) {
|
|
68
|
+
const rejectPayload = {
|
|
69
|
+
requestId: msg.id,
|
|
70
|
+
serviceId: 'baemail',
|
|
71
|
+
status: 'rejected',
|
|
72
|
+
reason: 'Missing or empty message. Send {message: "your message"}',
|
|
73
|
+
};
|
|
74
|
+
const sig = signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
|
|
75
|
+
await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
|
|
79
|
+
});
|
|
80
|
+
return { id: msg.id, type: 'service-request', serviceId: 'baemail', action: 'rejected', reason: 'missing message', from: msg.from, ack: true };
|
|
81
|
+
}
|
|
82
|
+
if (message.length > (config.maxMessageLength || 4000)) {
|
|
83
|
+
const rejectPayload = {
|
|
84
|
+
requestId: msg.id,
|
|
85
|
+
serviceId: 'baemail',
|
|
86
|
+
status: 'rejected',
|
|
87
|
+
reason: `Message too long. Max ${config.maxMessageLength || 4000} characters.`,
|
|
88
|
+
};
|
|
89
|
+
const sig = signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
|
|
90
|
+
await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: { 'Content-Type': 'application/json' },
|
|
93
|
+
body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
|
|
94
|
+
});
|
|
95
|
+
return { id: msg.id, type: 'service-request', serviceId: 'baemail', action: 'rejected', reason: 'message too long', from: msg.from, ack: true };
|
|
96
|
+
}
|
|
97
|
+
// Load wallet identity
|
|
98
|
+
const sdk = await getSdk();
|
|
99
|
+
const { PrivateKey, Hash } = sdk;
|
|
100
|
+
let walletIdentity;
|
|
101
|
+
try {
|
|
102
|
+
walletIdentity = JSON.parse(fs.readFileSync(PATHS.walletIdentity, 'utf-8'));
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const rejectPayload = {
|
|
106
|
+
requestId: msg.id,
|
|
107
|
+
serviceId: 'baemail',
|
|
108
|
+
status: 'rejected',
|
|
109
|
+
reason: 'Service temporarily unavailable (wallet error)',
|
|
110
|
+
};
|
|
111
|
+
const sig = signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
|
|
112
|
+
await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: { 'Content-Type': 'application/json' },
|
|
115
|
+
body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
|
|
116
|
+
});
|
|
117
|
+
return { id: msg.id, type: 'service-request', serviceId: 'baemail', action: 'rejected', reason: 'wallet error', from: msg.from, ack: true };
|
|
118
|
+
}
|
|
119
|
+
// Sender info
|
|
120
|
+
const senderName = input?.senderName || 'Anonymous';
|
|
121
|
+
const replyKey = input?.replyIdentityKey || msg.from;
|
|
122
|
+
// Check hooks configured
|
|
123
|
+
let hookToken = null;
|
|
124
|
+
let hookPort = 18789;
|
|
125
|
+
const openclawConfigPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
126
|
+
if (fs.existsSync(openclawConfigPath)) {
|
|
127
|
+
try {
|
|
128
|
+
const openclawConfig = JSON.parse(fs.readFileSync(openclawConfigPath, 'utf-8'));
|
|
129
|
+
hookToken = openclawConfig?.hooks?.token;
|
|
130
|
+
hookPort = openclawConfig?.gateway?.port || 18789;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Ignore parse errors
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!hookToken) {
|
|
137
|
+
const rejectPayload = {
|
|
138
|
+
requestId: msg.id,
|
|
139
|
+
serviceId: 'baemail',
|
|
140
|
+
status: 'rejected',
|
|
141
|
+
reason: 'OpenClaw hooks not configured. Payment NOT accepted.',
|
|
142
|
+
};
|
|
143
|
+
const sig = signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
|
|
144
|
+
await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
headers: { 'Content-Type': 'application/json' },
|
|
147
|
+
body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
|
|
148
|
+
});
|
|
149
|
+
return { id: msg.id, type: 'service-request', serviceId: 'baemail', action: 'rejected', reason: 'hooks not configured', from: msg.from, ack: true };
|
|
150
|
+
}
|
|
151
|
+
// Verify and accept payment
|
|
152
|
+
const ourHash160 = Hash.hash160(PrivateKey.fromHex(walletIdentity.rootKeyHex).toPublicKey().encode(true));
|
|
153
|
+
const minPrice = config.tiers.standard;
|
|
154
|
+
const payResult = await verifyAndAcceptPayment(payment, minPrice, msg.from, 'baemail', ourHash160);
|
|
155
|
+
if (!payResult.accepted) {
|
|
156
|
+
const rejectPayload = {
|
|
157
|
+
requestId: msg.id,
|
|
158
|
+
serviceId: 'baemail',
|
|
159
|
+
status: 'rejected',
|
|
160
|
+
reason: `Payment rejected: ${payResult.error}. Minimum: ${minPrice} sats.`,
|
|
161
|
+
};
|
|
162
|
+
const sig = signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
|
|
163
|
+
await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: { 'Content-Type': 'application/json' },
|
|
166
|
+
body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
|
|
167
|
+
});
|
|
168
|
+
return { id: msg.id, type: 'service-request', serviceId: 'baemail', action: 'rejected', reason: payResult.error, from: msg.from, ack: true };
|
|
169
|
+
}
|
|
170
|
+
// Determine tier
|
|
171
|
+
const paidSats = payResult.satoshis;
|
|
172
|
+
let tier = 'standard';
|
|
173
|
+
let tierEmoji = '📧';
|
|
174
|
+
if (paidSats >= config.tiers.urgent) {
|
|
175
|
+
tier = 'urgent';
|
|
176
|
+
tierEmoji = '🚨';
|
|
177
|
+
}
|
|
178
|
+
else if (paidSats >= config.tiers.priority) {
|
|
179
|
+
tier = 'priority';
|
|
180
|
+
tierEmoji = '⚡';
|
|
181
|
+
}
|
|
182
|
+
// Format message
|
|
183
|
+
const formattedMessage = `${tierEmoji} **Baemail** (${tier.toUpperCase()})
|
|
184
|
+
|
|
185
|
+
**From:** ${senderName}
|
|
186
|
+
**Paid:** ${paidSats} sats
|
|
187
|
+
**Reply to:** \`${replyKey.slice(0, 16)}...\`
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
${message}
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
_Reply via overlay: \`cli send ${replyKey} ping "your reply"\`_`;
|
|
195
|
+
// Deliver via hooks
|
|
196
|
+
let deliverySuccess = false;
|
|
197
|
+
let deliveryError = null;
|
|
198
|
+
try {
|
|
199
|
+
const hookHost = process.env.CLAWDBOT_HOST || process.env.OPENCLAW_HOST || '127.0.0.1';
|
|
200
|
+
const hookUrl = `http://${hookHost}:${hookPort}/hooks/agent`;
|
|
201
|
+
const hookResp = await fetchWithTimeout(hookUrl, {
|
|
202
|
+
method: 'POST',
|
|
203
|
+
headers: {
|
|
204
|
+
'Content-Type': 'application/json',
|
|
205
|
+
'Authorization': `Bearer ${hookToken}`,
|
|
206
|
+
'x-clawdbot-token': hookToken,
|
|
207
|
+
},
|
|
208
|
+
body: JSON.stringify({
|
|
209
|
+
message: formattedMessage,
|
|
210
|
+
name: 'Baemail',
|
|
211
|
+
sessionKey: `baemail:${msg.id}`,
|
|
212
|
+
wakeMode: 'now',
|
|
213
|
+
deliver: true,
|
|
214
|
+
channel: config.deliveryChannel,
|
|
215
|
+
}),
|
|
216
|
+
});
|
|
217
|
+
if (hookResp.ok) {
|
|
218
|
+
deliverySuccess = true;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
const body = await hookResp.text().catch(() => '');
|
|
222
|
+
deliveryError = `Hook failed: ${hookResp.status} ${body}`;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
deliveryError = err.message;
|
|
227
|
+
}
|
|
228
|
+
// Log delivery
|
|
229
|
+
ensureStateDir();
|
|
230
|
+
const logEntry = {
|
|
231
|
+
requestId: msg.id,
|
|
232
|
+
from: msg.from,
|
|
233
|
+
senderName,
|
|
234
|
+
tier,
|
|
235
|
+
paidSats,
|
|
236
|
+
messageLength: message.length,
|
|
237
|
+
deliveryChannel: config.deliveryChannel,
|
|
238
|
+
deliverySuccess,
|
|
239
|
+
deliveryError: deliveryError ?? null,
|
|
240
|
+
paymentTxid: payResult.txid || '',
|
|
241
|
+
refundStatus: deliverySuccess ? null : 'pending',
|
|
242
|
+
timestamp: new Date().toISOString(),
|
|
243
|
+
};
|
|
244
|
+
fs.appendFileSync(PATHS.baemailLog, JSON.stringify(logEntry) + '\n');
|
|
245
|
+
// Send response
|
|
246
|
+
const responsePayload = {
|
|
247
|
+
requestId: msg.id,
|
|
248
|
+
serviceId: 'baemail',
|
|
249
|
+
status: deliverySuccess ? 'fulfilled' : 'delivery_failed',
|
|
250
|
+
result: {
|
|
251
|
+
delivered: deliverySuccess,
|
|
252
|
+
tier,
|
|
253
|
+
channel: config.deliveryChannel,
|
|
254
|
+
paidSats,
|
|
255
|
+
error: deliveryError,
|
|
256
|
+
replyTo: identityKey,
|
|
257
|
+
refundable: !deliverySuccess,
|
|
258
|
+
note: deliverySuccess ? undefined : 'Delivery failed. Run: baemail-refund ' + msg.id,
|
|
259
|
+
},
|
|
260
|
+
paymentAccepted: true,
|
|
261
|
+
paymentTxid: payResult.txid,
|
|
262
|
+
satoshisReceived: payResult.satoshis,
|
|
263
|
+
};
|
|
264
|
+
const respSig = signRelayMessage(privKey, msg.from, 'service-response', responsePayload);
|
|
265
|
+
await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
|
|
266
|
+
method: 'POST',
|
|
267
|
+
headers: { 'Content-Type': 'application/json' },
|
|
268
|
+
body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: responsePayload, signature: respSig }),
|
|
269
|
+
});
|
|
270
|
+
return {
|
|
271
|
+
id: msg.id,
|
|
272
|
+
type: 'service-request',
|
|
273
|
+
serviceId: 'baemail',
|
|
274
|
+
action: deliverySuccess ? 'fulfilled' : 'delivery_failed',
|
|
275
|
+
tier,
|
|
276
|
+
deliverySuccess,
|
|
277
|
+
deliveryError: deliveryError === null ? undefined : deliveryError,
|
|
278
|
+
paymentAccepted: true,
|
|
279
|
+
paymentTxid: payResult.txid || undefined,
|
|
280
|
+
satoshisReceived: payResult.satoshis,
|
|
281
|
+
from: msg.from,
|
|
282
|
+
ack: true,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration constants and environment variables for the overlay CLI.
|
|
3
|
+
*/
|
|
4
|
+
/** Wallet storage directory */
|
|
5
|
+
export declare const WALLET_DIR: string;
|
|
6
|
+
/** Network to use (mainnet or testnet) */
|
|
7
|
+
export declare const NETWORK: 'mainnet' | 'testnet';
|
|
8
|
+
/** Overlay server URL */
|
|
9
|
+
export declare const OVERLAY_URL: string;
|
|
10
|
+
/** Agent display name on the overlay network */
|
|
11
|
+
export declare const AGENT_NAME: string;
|
|
12
|
+
/** Agent description for the overlay identity */
|
|
13
|
+
export declare const AGENT_DESCRIPTION: string;
|
|
14
|
+
/** WhatsOnChain API key (optional, for rate limit bypass) */
|
|
15
|
+
export declare const WOC_API_KEY: string;
|
|
16
|
+
/** Overlay state directory for registration, services, etc. */
|
|
17
|
+
export declare const OVERLAY_STATE_DIR: string;
|
|
18
|
+
/** Protocol identifier for overlay transactions */
|
|
19
|
+
export declare const PROTOCOL_ID = "clawdbot overlay v1";
|
|
20
|
+
/** Topic managers for overlay submissions */
|
|
21
|
+
export declare const TOPICS: {
|
|
22
|
+
readonly IDENTITY: "tm_clawdbot_identity";
|
|
23
|
+
readonly SERVICES: "tm_clawdbot_services";
|
|
24
|
+
readonly X_VERIFICATION: "tm_clawdbot_x_verification";
|
|
25
|
+
};
|
|
26
|
+
/** Lookup services for overlay queries */
|
|
27
|
+
export declare const LOOKUP_SERVICES: {
|
|
28
|
+
readonly AGENTS: "ls_clawdbot_agents";
|
|
29
|
+
readonly SERVICES: "ls_clawdbot_services";
|
|
30
|
+
readonly X_VERIFICATIONS: "ls_clawdbot_x_verifications";
|
|
31
|
+
};
|
|
32
|
+
/** Paths derived from config */
|
|
33
|
+
export declare const PATHS: {
|
|
34
|
+
readonly walletIdentity: string;
|
|
35
|
+
readonly registration: string;
|
|
36
|
+
readonly services: string;
|
|
37
|
+
readonly latestChange: string;
|
|
38
|
+
readonly receivedPayments: string;
|
|
39
|
+
readonly researchQueue: string;
|
|
40
|
+
readonly serviceQueue: string;
|
|
41
|
+
readonly notifications: string;
|
|
42
|
+
readonly xVerifications: string;
|
|
43
|
+
readonly pendingXVerification: string;
|
|
44
|
+
readonly xEngagementQueue: string;
|
|
45
|
+
readonly memoryStore: string;
|
|
46
|
+
readonly baemailConfig: string;
|
|
47
|
+
readonly baemailLog: string;
|
|
48
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration constants and environment variables for the overlay CLI.
|
|
3
|
+
*/
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
// Auto-load .env from overlay state dir if it exists
|
|
8
|
+
const overlayEnvPath = path.join(os.homedir(), '.clawdbot', 'bsv-overlay', '.env');
|
|
9
|
+
try {
|
|
10
|
+
if (fs.existsSync(overlayEnvPath)) {
|
|
11
|
+
for (const line of fs.readFileSync(overlayEnvPath, 'utf-8').split('\n')) {
|
|
12
|
+
const match = line.match(/^([A-Z_]+)=(.+)$/);
|
|
13
|
+
if (match && !process.env[match[1]]) {
|
|
14
|
+
process.env[match[1]] = match[2].trim();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Ignore errors loading .env
|
|
21
|
+
}
|
|
22
|
+
/** Wallet storage directory */
|
|
23
|
+
export const WALLET_DIR = process.env.BSV_WALLET_DIR
|
|
24
|
+
|| path.join(os.homedir(), '.clawdbot', 'bsv-wallet');
|
|
25
|
+
/** Network to use (mainnet or testnet) */
|
|
26
|
+
export const NETWORK = process.env.BSV_NETWORK || 'mainnet';
|
|
27
|
+
/** Overlay server URL */
|
|
28
|
+
export const OVERLAY_URL = process.env.OVERLAY_URL || 'https://clawoverlay.com';
|
|
29
|
+
/** Agent display name on the overlay network */
|
|
30
|
+
export const AGENT_NAME = process.env.AGENT_NAME || 'clawdbot-agent';
|
|
31
|
+
/** Agent description for the overlay identity */
|
|
32
|
+
export const AGENT_DESCRIPTION = process.env.AGENT_DESCRIPTION ||
|
|
33
|
+
`AI agent on the OpenClaw Overlay Network. Offers services for BSV micropayments.`;
|
|
34
|
+
/** WhatsOnChain API key (optional, for rate limit bypass) */
|
|
35
|
+
export const WOC_API_KEY = process.env.WOC_API_KEY || '';
|
|
36
|
+
/** Overlay state directory for registration, services, etc. */
|
|
37
|
+
export const OVERLAY_STATE_DIR = path.join(os.homedir(), '.clawdbot', 'bsv-overlay');
|
|
38
|
+
/** Protocol identifier for overlay transactions */
|
|
39
|
+
export const PROTOCOL_ID = 'clawdbot overlay v1';
|
|
40
|
+
/** Topic managers for overlay submissions */
|
|
41
|
+
export const TOPICS = {
|
|
42
|
+
IDENTITY: 'tm_clawdbot_identity',
|
|
43
|
+
SERVICES: 'tm_clawdbot_services',
|
|
44
|
+
X_VERIFICATION: 'tm_clawdbot_x_verification',
|
|
45
|
+
};
|
|
46
|
+
/** Lookup services for overlay queries */
|
|
47
|
+
export const LOOKUP_SERVICES = {
|
|
48
|
+
AGENTS: 'ls_clawdbot_agents',
|
|
49
|
+
SERVICES: 'ls_clawdbot_services',
|
|
50
|
+
X_VERIFICATIONS: 'ls_clawdbot_x_verifications',
|
|
51
|
+
};
|
|
52
|
+
/** Paths derived from config */
|
|
53
|
+
export const PATHS = {
|
|
54
|
+
walletIdentity: path.join(WALLET_DIR, 'wallet-identity.json'),
|
|
55
|
+
registration: path.join(OVERLAY_STATE_DIR, 'registration.json'),
|
|
56
|
+
services: path.join(OVERLAY_STATE_DIR, 'services.json'),
|
|
57
|
+
latestChange: path.join(OVERLAY_STATE_DIR, 'latest-change.json'),
|
|
58
|
+
receivedPayments: path.join(OVERLAY_STATE_DIR, 'received-payments.jsonl'),
|
|
59
|
+
researchQueue: path.join(OVERLAY_STATE_DIR, 'research-queue.jsonl'),
|
|
60
|
+
serviceQueue: path.join(OVERLAY_STATE_DIR, 'service-queue.jsonl'),
|
|
61
|
+
notifications: path.join(OVERLAY_STATE_DIR, 'notifications.jsonl'),
|
|
62
|
+
xVerifications: path.join(OVERLAY_STATE_DIR, 'x-verifications.json'),
|
|
63
|
+
pendingXVerification: path.join(OVERLAY_STATE_DIR, 'pending-x-verification.json'),
|
|
64
|
+
xEngagementQueue: path.join(OVERLAY_STATE_DIR, 'x-engagement-queue.jsonl'),
|
|
65
|
+
memoryStore: path.join(WALLET_DIR, 'memory-store.json'),
|
|
66
|
+
baemailConfig: path.join(OVERLAY_STATE_DIR, 'baemail-config.json'),
|
|
67
|
+
baemailLog: path.join(OVERLAY_STATE_DIR, 'baemail-log.jsonl'),
|
|
68
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connect command: WebSocket real-time message processing.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Connect command: establish WebSocket connection for real-time messaging.
|
|
6
|
+
* Note: This function never returns normally - it runs until SIGINT/SIGTERM.
|
|
7
|
+
*/
|
|
8
|
+
export declare function cmdConnect(): Promise<void>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connect command: WebSocket real-time message processing.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { OVERLAY_URL, PATHS } from '../config.js';
|
|
6
|
+
import { fail } from '../output.js';
|
|
7
|
+
import { loadIdentity } from '../wallet/identity.js';
|
|
8
|
+
import { processMessage } from './handlers.js';
|
|
9
|
+
import { ensureStateDir } from '../utils/storage.js';
|
|
10
|
+
/**
|
|
11
|
+
* Connect command: establish WebSocket connection for real-time messaging.
|
|
12
|
+
* Note: This function never returns normally - it runs until SIGINT/SIGTERM.
|
|
13
|
+
*/
|
|
14
|
+
export async function cmdConnect() {
|
|
15
|
+
let WebSocketClient;
|
|
16
|
+
try {
|
|
17
|
+
const ws = await import('ws');
|
|
18
|
+
WebSocketClient = ws.default || ws.WebSocket || ws;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return fail('WebSocket client not available. Install it: npm install ws');
|
|
22
|
+
}
|
|
23
|
+
const { identityKey, privKey } = await loadIdentity();
|
|
24
|
+
const wsUrl = OVERLAY_URL.replace(/^http/, 'ws') + '/relay/subscribe?identity=' + identityKey;
|
|
25
|
+
let reconnectDelay = 1000;
|
|
26
|
+
let shouldReconnect = true;
|
|
27
|
+
let currentWs = null;
|
|
28
|
+
function shutdown() {
|
|
29
|
+
shouldReconnect = false;
|
|
30
|
+
if (currentWs) {
|
|
31
|
+
try {
|
|
32
|
+
currentWs.close();
|
|
33
|
+
}
|
|
34
|
+
catch { }
|
|
35
|
+
}
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
process.on('SIGINT', shutdown);
|
|
39
|
+
process.on('SIGTERM', shutdown);
|
|
40
|
+
function connect() {
|
|
41
|
+
const ws = new WebSocketClient(wsUrl);
|
|
42
|
+
currentWs = ws;
|
|
43
|
+
ws.on('open', () => {
|
|
44
|
+
reconnectDelay = 1000; // reset on successful connect
|
|
45
|
+
console.error(JSON.stringify({ event: 'connected', identity: identityKey, overlay: OVERLAY_URL }));
|
|
46
|
+
});
|
|
47
|
+
ws.on('message', async (data) => {
|
|
48
|
+
try {
|
|
49
|
+
const envelope = JSON.parse(data.toString());
|
|
50
|
+
if (envelope.type === 'message') {
|
|
51
|
+
const result = await processMessage(envelope.message, identityKey, privKey);
|
|
52
|
+
// Output the result as a JSON line to stdout
|
|
53
|
+
console.log(JSON.stringify(result));
|
|
54
|
+
// Also append to notification log
|
|
55
|
+
ensureStateDir();
|
|
56
|
+
try {
|
|
57
|
+
fs.appendFileSync(PATHS.notifications, JSON.stringify({ ...result, _ts: Date.now() }) + '\n');
|
|
58
|
+
}
|
|
59
|
+
catch { }
|
|
60
|
+
// Ack the message
|
|
61
|
+
if (result.ack) {
|
|
62
|
+
try {
|
|
63
|
+
await fetch(OVERLAY_URL + '/relay/ack', {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: { 'Content-Type': 'application/json' },
|
|
66
|
+
body: JSON.stringify({ identity: identityKey, messageIds: [result.id] }),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch (ackErr) {
|
|
70
|
+
console.error(JSON.stringify({ event: 'ack-error', id: result.id, message: String(ackErr) }));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Handle service announcements
|
|
75
|
+
if (envelope.type === 'service-announced') {
|
|
76
|
+
const svc = envelope.service || {};
|
|
77
|
+
const announcement = {
|
|
78
|
+
event: 'service-announced',
|
|
79
|
+
serviceId: svc.serviceId,
|
|
80
|
+
name: svc.name,
|
|
81
|
+
description: svc.description,
|
|
82
|
+
priceSats: svc.pricingSats,
|
|
83
|
+
provider: svc.identityKey,
|
|
84
|
+
txid: envelope.txid,
|
|
85
|
+
_ts: Date.now(),
|
|
86
|
+
};
|
|
87
|
+
console.log(JSON.stringify(announcement));
|
|
88
|
+
ensureStateDir();
|
|
89
|
+
try {
|
|
90
|
+
fs.appendFileSync(PATHS.notifications, JSON.stringify(announcement) + '\n');
|
|
91
|
+
}
|
|
92
|
+
catch { }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
console.error(JSON.stringify({ event: 'process-error', message: String(err) }));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
ws.on('close', () => {
|
|
100
|
+
currentWs = null;
|
|
101
|
+
if (shouldReconnect) {
|
|
102
|
+
console.error(JSON.stringify({ event: 'disconnected', reconnectMs: reconnectDelay }));
|
|
103
|
+
setTimeout(connect, reconnectDelay);
|
|
104
|
+
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
ws.on('error', (err) => {
|
|
108
|
+
console.error(JSON.stringify({ event: 'error', message: err.message }));
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
connect();
|
|
112
|
+
// Keep the process alive — never resolves
|
|
113
|
+
await new Promise(() => { });
|
|
114
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message type handlers and processMessage function.
|
|
3
|
+
*/
|
|
4
|
+
import type { RelayMessage, ProcessMessageResult } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Verify and accept a payment from a service request.
|
|
7
|
+
* Uses a2a-bsv wallet.acceptPayment() for proper BRC-29 handling.
|
|
8
|
+
*/
|
|
9
|
+
export declare function verifyAndAcceptPayment(payment: any, minSats: number, senderKey: string, serviceId: string, ourHash160: Uint8Array): Promise<{
|
|
10
|
+
accepted: boolean;
|
|
11
|
+
txid: string | null;
|
|
12
|
+
satoshis: number;
|
|
13
|
+
outputIndex: number;
|
|
14
|
+
walletAccepted: boolean;
|
|
15
|
+
error: string | null;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Process a single relay message.
|
|
19
|
+
* Handles pings, service requests, pongs, and service responses.
|
|
20
|
+
*/
|
|
21
|
+
export declare function processMessage(msg: RelayMessage, identityKey: string, privKey: any): Promise<ProcessMessageResult>;
|