@johngalt5/bsv-overlay 0.2.12 → 0.2.13
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/index.ts +72 -10
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -42,6 +42,14 @@ function loadDailySpending(walletDir: string): DailySpending {
|
|
|
42
42
|
return { date: today, totalSats: 0, transactions: [] };
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
function writeActivityEvent(event) {
|
|
46
|
+
const alertDir = path.join(process.env.HOME || '', '.clawdbot', 'bsv-overlay');
|
|
47
|
+
try {
|
|
48
|
+
fs.mkdirSync(alertDir, { recursive: true });
|
|
49
|
+
fs.appendFileSync(path.join(alertDir, 'activity-feed.jsonl'), JSON.stringify({ ...event, ts: Date.now() }) + '\n');
|
|
50
|
+
} catch {}
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
function recordSpend(walletDir: string, sats: number, service: string, provider: string) {
|
|
46
54
|
const spending = loadDailySpending(walletDir);
|
|
47
55
|
spending.totalSats += sats;
|
|
@@ -132,6 +140,33 @@ function stopAutoImport() {
|
|
|
132
140
|
}
|
|
133
141
|
}
|
|
134
142
|
|
|
143
|
+
// Categorize WebSocket events into notification types
|
|
144
|
+
function categorizeEvent(event) {
|
|
145
|
+
const base = { ts: Date.now(), from: event.from?.slice(0, 16), fullFrom: event.from };
|
|
146
|
+
|
|
147
|
+
// 💰 Incoming payment — someone paid us for a service
|
|
148
|
+
if (event.action === 'queued-for-agent' && event.satoshisReceived) {
|
|
149
|
+
return { ...base, type: 'incoming_payment', emoji: '💰', serviceId: event.serviceId, sats: event.satoshisReceived, requestId: event.id, message: `Received ${event.satoshisReceived} sats for ${event.serviceId}` };
|
|
150
|
+
}
|
|
151
|
+
if (event.action === 'fulfilled' && event.satoshisReceived) {
|
|
152
|
+
return { ...base, type: 'incoming_payment', emoji: '💰', serviceId: event.serviceId, sats: event.satoshisReceived, message: `Received ${event.satoshisReceived} sats for ${event.serviceId} (auto-fulfilled)` };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 📬 Response received — a service we requested came back
|
|
156
|
+
if (event.type === 'service-response' && event.action === 'received') {
|
|
157
|
+
const payload = event.payload || {};
|
|
158
|
+
return { ...base, type: 'response_received', emoji: '📬', serviceId: payload.serviceId, status: payload.status, message: `Response received for ${payload.serviceId}: ${payload.status}` };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ❌ Request rejected
|
|
162
|
+
if (event.action === 'rejected' && event.serviceId) {
|
|
163
|
+
return { ...base, type: 'request_rejected', emoji: '❌', serviceId: event.serviceId, reason: event.reason, message: `Rejected ${event.serviceId} request: ${event.reason}` };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Skip pings/pongs and other noise
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
135
170
|
function startBackgroundService(env, cliPath, logger) {
|
|
136
171
|
if (backgroundProcess) return;
|
|
137
172
|
serviceRunning = true;
|
|
@@ -153,16 +188,14 @@ function startBackgroundService(env, cliPath, logger) {
|
|
|
153
188
|
const event = JSON.parse(line);
|
|
154
189
|
logger?.debug?.(`[bsv-overlay] ${event.event || event.type || 'message'}:`, JSON.stringify(event).slice(0, 200));
|
|
155
190
|
|
|
156
|
-
|
|
191
|
+
const alertDir = path.join(process.env.HOME || '', '.clawdbot', 'bsv-overlay');
|
|
192
|
+
fs.mkdirSync(alertDir, { recursive: true });
|
|
193
|
+
|
|
194
|
+
// Detect queued-for-agent events — write fulfillment alert
|
|
157
195
|
if (event.action === 'queued-for-agent' && event.serviceId) {
|
|
158
|
-
logger?.info?.(`[bsv-overlay] ⚡ Incoming ${event.serviceId} request from ${event.from?.slice(0, 12)}
|
|
159
|
-
|
|
160
|
-
// Write alert file for the cron-based fulfillment checker to pick up
|
|
161
|
-
const alertDir = path.join(process.env.HOME || '', '.clawdbot', 'bsv-overlay');
|
|
162
|
-
const alertPath = path.join(alertDir, 'pending-alert.jsonl');
|
|
196
|
+
logger?.info?.(`[bsv-overlay] ⚡ Incoming ${event.serviceId} request from ${event.from?.slice(0, 12)}...`);
|
|
163
197
|
try {
|
|
164
|
-
fs.
|
|
165
|
-
fs.appendFileSync(alertPath, JSON.stringify({
|
|
198
|
+
fs.appendFileSync(path.join(alertDir, 'pending-alert.jsonl'), JSON.stringify({
|
|
166
199
|
requestId: event.id,
|
|
167
200
|
serviceId: event.serviceId,
|
|
168
201
|
from: event.from,
|
|
@@ -171,6 +204,14 @@ function startBackgroundService(env, cliPath, logger) {
|
|
|
171
204
|
}) + '\n');
|
|
172
205
|
} catch {}
|
|
173
206
|
}
|
|
207
|
+
|
|
208
|
+
// Write payment/activity notifications for ALL significant events
|
|
209
|
+
const notifEvent = categorizeEvent(event);
|
|
210
|
+
if (notifEvent) {
|
|
211
|
+
try {
|
|
212
|
+
fs.appendFileSync(path.join(alertDir, 'activity-feed.jsonl'), JSON.stringify(notifEvent) + '\n');
|
|
213
|
+
} catch {}
|
|
214
|
+
}
|
|
174
215
|
} catch {}
|
|
175
216
|
}
|
|
176
217
|
});
|
|
@@ -601,6 +642,9 @@ async function executeOverlayAction(params, config, api) {
|
|
|
601
642
|
case "pending-requests":
|
|
602
643
|
return await handlePendingRequests(env, cliPath);
|
|
603
644
|
|
|
645
|
+
case "activity":
|
|
646
|
+
return handleActivity();
|
|
647
|
+
|
|
604
648
|
case "fulfill":
|
|
605
649
|
return await handleFulfill(params, env, cliPath);
|
|
606
650
|
|
|
@@ -698,8 +742,9 @@ async function handleServiceRequest(params, env, cliPath, config, api) {
|
|
|
698
742
|
for (const msg of messages) {
|
|
699
743
|
if (msg.type === 'service-response' && msg.from === bestProvider.identityKey) {
|
|
700
744
|
api.logger.info(`Received response from ${bestProvider.agentName}`);
|
|
701
|
-
// Record the spending
|
|
702
745
|
recordSpend(walletDir, price, service, bestProvider.agentName);
|
|
746
|
+
writeActivityEvent({ type: 'outgoing_payment', emoji: '💸', sats: price, service, provider: bestProvider.agentName, message: `Paid ${price} sats to ${bestProvider.agentName} for ${service}` });
|
|
747
|
+
writeActivityEvent({ type: 'response_received', emoji: '📬', service, provider: bestProvider.agentName, status: msg.payload?.status, message: `${service} response received from ${bestProvider.agentName}` });
|
|
703
748
|
return {
|
|
704
749
|
provider: bestProvider.agentName,
|
|
705
750
|
cost: price,
|
|
@@ -715,8 +760,8 @@ async function handleServiceRequest(params, env, cliPath, config, api) {
|
|
|
715
760
|
}
|
|
716
761
|
|
|
717
762
|
// Don't throw — the response may still arrive via WebSocket
|
|
718
|
-
// Record the spend since payment was already sent
|
|
719
763
|
recordSpend(walletDir, price, service, bestProvider.agentName);
|
|
764
|
+
writeActivityEvent({ type: 'outgoing_payment', emoji: '💸', sats: price, service, provider: bestProvider.agentName, message: `Paid ${price} sats to ${bestProvider.agentName} for ${service} (awaiting response)` });
|
|
720
765
|
return {
|
|
721
766
|
provider: bestProvider.agentName,
|
|
722
767
|
cost: price,
|
|
@@ -812,6 +857,7 @@ async function handleDirectPay(params, env, cliPath, config) {
|
|
|
812
857
|
|
|
813
858
|
// Record the spending
|
|
814
859
|
recordSpend(walletDir, sats, 'direct-payment', identityKey);
|
|
860
|
+
writeActivityEvent({ type: 'outgoing_payment', emoji: '💸', sats, service: 'direct-payment', provider: identityKey?.slice(0, 16), message: `Direct payment: ${sats} sats sent` });
|
|
815
861
|
|
|
816
862
|
return output.data;
|
|
817
863
|
}
|
|
@@ -1080,6 +1126,19 @@ async function handlePendingRequests(env, cliPath) {
|
|
|
1080
1126
|
return output.data;
|
|
1081
1127
|
}
|
|
1082
1128
|
|
|
1129
|
+
function handleActivity() {
|
|
1130
|
+
const feedPath = path.join(process.env.HOME || '', '.clawdbot', 'bsv-overlay', 'activity-feed.jsonl');
|
|
1131
|
+
if (!fs.existsSync(feedPath)) return { events: [], count: 0 };
|
|
1132
|
+
|
|
1133
|
+
const lines = fs.readFileSync(feedPath, 'utf-8').trim().split('\n').filter(Boolean);
|
|
1134
|
+
const events = lines.map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
1135
|
+
|
|
1136
|
+
// Clear the feed after reading
|
|
1137
|
+
fs.writeFileSync(feedPath, '');
|
|
1138
|
+
|
|
1139
|
+
return { events, count: events.length };
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1083
1142
|
async function handleFulfill(params, env, cliPath) {
|
|
1084
1143
|
const { requestId, recipientKey, serviceId, result } = params;
|
|
1085
1144
|
if (!requestId || !recipientKey || !serviceId || !result) {
|
|
@@ -1091,6 +1150,9 @@ async function handleFulfill(params, env, cliPath) {
|
|
|
1091
1150
|
], { env });
|
|
1092
1151
|
const output = parseCliOutput(cliResult.stdout);
|
|
1093
1152
|
if (!output.success) throw new Error(`Fulfill failed: ${output.error}`);
|
|
1153
|
+
|
|
1154
|
+
writeActivityEvent({ type: 'service_fulfilled', emoji: '✅', serviceId, recipientKey: recipientKey?.slice(0, 16), message: `Fulfilled ${serviceId} request — response sent` });
|
|
1155
|
+
|
|
1094
1156
|
return output.data;
|
|
1095
1157
|
}
|
|
1096
1158
|
|