openclaw-overlay-plugin 0.8.6 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +32 -49
- package/dist/src/core/wallet.js +1 -1
- package/dist/src/scripts/messaging/connect.d.ts +2 -2
- package/dist/src/scripts/messaging/connect.js +63 -16
- package/index.ts +33 -47
- package/openclaw.plugin.json +1 -1
- package/package.json +7 -6
- package/src/core/wallet.ts +1 -1
- package/src/scripts/messaging/connect.ts +49 -16
package/dist/index.js
CHANGED
|
@@ -10,10 +10,11 @@ import { cmdDiscover } from './src/scripts/overlay/discover.js';
|
|
|
10
10
|
import { cmdRequestService } from './src/scripts/services/request.js';
|
|
11
11
|
import { cmdServiceQueue } from './src/scripts/services/queue.js';
|
|
12
12
|
import { cmdRespondService } from './src/scripts/services/respond.js';
|
|
13
|
+
import { cmdConnect } from './src/scripts/messaging/connect.js';
|
|
13
14
|
import { setNoExit } from './src/scripts/output.js';
|
|
14
|
-
// Track background
|
|
15
|
-
let backgroundProcess = null;
|
|
15
|
+
// Track background service state
|
|
16
16
|
let serviceRunning = false;
|
|
17
|
+
let abortController = null;
|
|
17
18
|
// Auto-import tracking
|
|
18
19
|
let autoImportInterval = null;
|
|
19
20
|
let knownTxids = new Set();
|
|
@@ -164,60 +165,42 @@ function wakeAgent(text, logger, options = {}) {
|
|
|
164
165
|
body: JSON.stringify({ prompt: text, sessionKey })
|
|
165
166
|
}).catch(() => { });
|
|
166
167
|
}
|
|
167
|
-
function startBackgroundService(config, api) {
|
|
168
|
-
if (
|
|
168
|
+
async function startBackgroundService(config, api) {
|
|
169
|
+
if (serviceRunning)
|
|
169
170
|
return;
|
|
170
171
|
serviceRunning = true;
|
|
172
|
+
abortController = new AbortController();
|
|
171
173
|
requestCleanupInterval = setInterval(() => {
|
|
172
174
|
if (serviceRunning)
|
|
173
175
|
wokenRequests.clear();
|
|
174
176
|
}, 5 * 60 * 1000);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
wokenRequests.add(rid);
|
|
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: { ... } })`;
|
|
198
|
-
wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:${rid}` });
|
|
199
|
-
}
|
|
200
|
-
if (event.type === 'service-response' && event.action === 'received') {
|
|
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)}`;
|
|
202
|
-
wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
catch { }
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
proc.on('exit', () => {
|
|
209
|
-
backgroundProcess = null;
|
|
210
|
-
if (serviceRunning)
|
|
211
|
-
setTimeout(spawnConnect, 5000);
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
spawnConnect();
|
|
177
|
+
applyConfigToEnv(config);
|
|
178
|
+
// Start the connection directly as a library call
|
|
179
|
+
// This bypasses the child_process detection and is more efficient
|
|
180
|
+
cmdConnect((event) => {
|
|
181
|
+
if ((event.action === 'queued-for-agent' || event.action === 'already-queued') && event.serviceId) {
|
|
182
|
+
const rid = event.id || `${event.from}-${Date.now()}`;
|
|
183
|
+
if (wokenRequests.has(rid))
|
|
184
|
+
return;
|
|
185
|
+
wokenRequests.add(rid);
|
|
186
|
+
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: { ... } })`;
|
|
187
|
+
wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:${rid}` });
|
|
188
|
+
}
|
|
189
|
+
if (event.type === 'service-response' && event.action === 'received') {
|
|
190
|
+
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)}`;
|
|
191
|
+
wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });
|
|
192
|
+
}
|
|
193
|
+
}, abortController.signal).catch((err) => {
|
|
194
|
+
if (serviceRunning && !abortController?.signal.aborted) {
|
|
195
|
+
api.logger?.error?.(`[openclaw-overlay] WebSocket error: ${err.message}`);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
215
198
|
}
|
|
216
199
|
function stopBackgroundService() {
|
|
217
200
|
serviceRunning = false;
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
|
|
201
|
+
if (abortController) {
|
|
202
|
+
abortController.abort();
|
|
203
|
+
abortController = null;
|
|
221
204
|
}
|
|
222
205
|
if (requestCleanupInterval) {
|
|
223
206
|
clearInterval(requestCleanupInterval);
|
|
@@ -285,8 +268,8 @@ export function register(api) {
|
|
|
285
268
|
await initializeServiceSystem();
|
|
286
269
|
}
|
|
287
270
|
catch { }
|
|
288
|
-
startBackgroundService(pluginConfig, api);
|
|
289
|
-
startAutoImport(pluginConfig, api);
|
|
271
|
+
await startBackgroundService(pluginConfig, api);
|
|
272
|
+
await startAutoImport(pluginConfig, api);
|
|
290
273
|
},
|
|
291
274
|
stop: () => stopBackgroundService()
|
|
292
275
|
});
|
package/dist/src/core/wallet.js
CHANGED
|
@@ -217,7 +217,7 @@ export class BSVAgentWallet {
|
|
|
217
217
|
// 6. SQLite storage via knex
|
|
218
218
|
const filePath = path.join(config.storageDir, `${DEFAULT_DB_NAME}.sqlite`);
|
|
219
219
|
const knex = knexLib({
|
|
220
|
-
client: 'sqlite3',
|
|
220
|
+
client: 'better-sqlite3',
|
|
221
221
|
connection: { filename: filePath },
|
|
222
222
|
useNullAsDefault: true,
|
|
223
223
|
});
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
5
|
* Connect command: establish WebSocket connection for real-time messaging.
|
|
6
|
-
*
|
|
6
|
+
* Supports being used as a library with onMessage callback and AbortSignal.
|
|
7
7
|
*/
|
|
8
|
-
export declare function cmdConnect(): Promise<void>;
|
|
8
|
+
export declare function cmdConnect(onMessage?: (data: any) => void, signal?: AbortSignal): Promise<void>;
|
|
@@ -9,9 +9,9 @@ import { processMessage } from './handlers.js';
|
|
|
9
9
|
import { ensureStateDir } from '../utils/storage.js';
|
|
10
10
|
/**
|
|
11
11
|
* Connect command: establish WebSocket connection for real-time messaging.
|
|
12
|
-
*
|
|
12
|
+
* Supports being used as a library with onMessage callback and AbortSignal.
|
|
13
13
|
*/
|
|
14
|
-
export async function cmdConnect() {
|
|
14
|
+
export async function cmdConnect(onMessage, signal) {
|
|
15
15
|
let WebSocketClient;
|
|
16
16
|
try {
|
|
17
17
|
const ws = await import('ws');
|
|
@@ -33,24 +33,48 @@ export async function cmdConnect() {
|
|
|
33
33
|
}
|
|
34
34
|
catch { }
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
// Only exit if we're not running as a library
|
|
37
|
+
if (!onMessage) {
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!onMessage) {
|
|
42
|
+
process.on('SIGINT', shutdown);
|
|
43
|
+
process.on('SIGTERM', shutdown);
|
|
44
|
+
}
|
|
45
|
+
if (signal) {
|
|
46
|
+
signal.addEventListener('abort', () => {
|
|
47
|
+
shouldReconnect = false;
|
|
48
|
+
if (currentWs) {
|
|
49
|
+
try {
|
|
50
|
+
currentWs.close();
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
}
|
|
54
|
+
});
|
|
37
55
|
}
|
|
38
|
-
process.on('SIGINT', shutdown);
|
|
39
|
-
process.on('SIGTERM', shutdown);
|
|
40
56
|
function connect() {
|
|
57
|
+
if (signal?.aborted)
|
|
58
|
+
return;
|
|
41
59
|
const ws = new WebSocketClient(wsUrl);
|
|
42
60
|
currentWs = ws;
|
|
43
61
|
ws.on('open', () => {
|
|
44
62
|
reconnectDelay = 1000; // reset on successful connect
|
|
45
|
-
|
|
63
|
+
const log = { event: 'connected', identity: identityKey, overlay: OVERLAY_URL };
|
|
64
|
+
if (onMessage)
|
|
65
|
+
onMessage(log);
|
|
66
|
+
else
|
|
67
|
+
console.error(JSON.stringify(log));
|
|
46
68
|
});
|
|
47
69
|
ws.on('message', async (data) => {
|
|
48
70
|
try {
|
|
49
71
|
const envelope = JSON.parse(data.toString());
|
|
50
72
|
if (envelope.type === 'message') {
|
|
51
73
|
const result = await processMessage(envelope.message, identityKey, privKey);
|
|
52
|
-
|
|
53
|
-
|
|
74
|
+
if (onMessage)
|
|
75
|
+
onMessage(result);
|
|
76
|
+
else
|
|
77
|
+
console.log(JSON.stringify(result));
|
|
54
78
|
// Also append to notification log
|
|
55
79
|
ensureStateDir();
|
|
56
80
|
try {
|
|
@@ -67,7 +91,11 @@ export async function cmdConnect() {
|
|
|
67
91
|
});
|
|
68
92
|
}
|
|
69
93
|
catch (ackErr) {
|
|
70
|
-
|
|
94
|
+
const log = { event: 'ack-error', id: result.id, message: String(ackErr) };
|
|
95
|
+
if (onMessage)
|
|
96
|
+
onMessage(log);
|
|
97
|
+
else
|
|
98
|
+
console.error(JSON.stringify(log));
|
|
71
99
|
}
|
|
72
100
|
}
|
|
73
101
|
}
|
|
@@ -84,7 +112,10 @@ export async function cmdConnect() {
|
|
|
84
112
|
txid: envelope.txid,
|
|
85
113
|
_ts: Date.now(),
|
|
86
114
|
};
|
|
87
|
-
|
|
115
|
+
if (onMessage)
|
|
116
|
+
onMessage(announcement);
|
|
117
|
+
else
|
|
118
|
+
console.log(JSON.stringify(announcement));
|
|
88
119
|
ensureStateDir();
|
|
89
120
|
try {
|
|
90
121
|
fs.appendFileSync(PATHS.notifications, JSON.stringify(announcement) + '\n');
|
|
@@ -93,22 +124,38 @@ export async function cmdConnect() {
|
|
|
93
124
|
}
|
|
94
125
|
}
|
|
95
126
|
catch (err) {
|
|
96
|
-
|
|
127
|
+
const log = { event: 'process-error', message: String(err) };
|
|
128
|
+
if (onMessage)
|
|
129
|
+
onMessage(log);
|
|
130
|
+
else
|
|
131
|
+
console.error(JSON.stringify(log));
|
|
97
132
|
}
|
|
98
133
|
});
|
|
99
134
|
ws.on('close', () => {
|
|
100
135
|
currentWs = null;
|
|
101
|
-
if (shouldReconnect) {
|
|
102
|
-
|
|
136
|
+
if (shouldReconnect && !signal?.aborted) {
|
|
137
|
+
const log = { event: 'disconnected', reconnectMs: reconnectDelay };
|
|
138
|
+
if (onMessage)
|
|
139
|
+
onMessage(log);
|
|
140
|
+
else
|
|
141
|
+
console.error(JSON.stringify(log));
|
|
103
142
|
setTimeout(connect, reconnectDelay);
|
|
104
143
|
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
|
|
105
144
|
}
|
|
106
145
|
});
|
|
107
146
|
ws.on('error', (err) => {
|
|
108
|
-
|
|
147
|
+
const log = { event: 'error', message: err.message };
|
|
148
|
+
if (onMessage)
|
|
149
|
+
onMessage(log);
|
|
150
|
+
else
|
|
151
|
+
console.error(JSON.stringify(log));
|
|
109
152
|
});
|
|
110
153
|
}
|
|
111
154
|
connect();
|
|
112
|
-
// Keep
|
|
113
|
-
|
|
155
|
+
// Keep alive
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
if (signal) {
|
|
158
|
+
signal.addEventListener('abort', () => resolve());
|
|
159
|
+
}
|
|
160
|
+
});
|
|
114
161
|
}
|
package/index.ts
CHANGED
|
@@ -11,11 +11,12 @@ import { cmdDiscover } from './src/scripts/overlay/discover.js';
|
|
|
11
11
|
import { cmdRequestService } from './src/scripts/services/request.js';
|
|
12
12
|
import { cmdServiceQueue } from './src/scripts/services/queue.js';
|
|
13
13
|
import { cmdRespondService } from './src/scripts/services/respond.js';
|
|
14
|
+
import { cmdConnect } from './src/scripts/messaging/connect.js';
|
|
14
15
|
import { setNoExit } from './src/scripts/output.js';
|
|
15
16
|
|
|
16
|
-
// Track background
|
|
17
|
-
let backgroundProcess: any = null;
|
|
17
|
+
// Track background service state
|
|
18
18
|
let serviceRunning = false;
|
|
19
|
+
let abortController: AbortController | null = null;
|
|
19
20
|
|
|
20
21
|
// Auto-import tracking
|
|
21
22
|
let autoImportInterval: any = null;
|
|
@@ -176,59 +177,44 @@ function wakeAgent(text: string, logger: any, options: { sessionKey?: string } =
|
|
|
176
177
|
}).catch(() => {});
|
|
177
178
|
}
|
|
178
179
|
|
|
179
|
-
function startBackgroundService(config: any, api: any) {
|
|
180
|
-
if (
|
|
180
|
+
async function startBackgroundService(config: any, api: any) {
|
|
181
|
+
if (serviceRunning) return;
|
|
181
182
|
serviceRunning = true;
|
|
183
|
+
abortController = new AbortController();
|
|
182
184
|
|
|
183
185
|
requestCleanupInterval = setInterval(() => {
|
|
184
186
|
if (serviceRunning) wokenRequests.clear();
|
|
185
187
|
}, 5 * 60 * 1000);
|
|
186
188
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (wokenRequests.has(rid)) return;
|
|
209
|
-
wokenRequests.add(rid);
|
|
210
|
-
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: { ... } })`;
|
|
211
|
-
wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:${rid}` });
|
|
212
|
-
}
|
|
213
|
-
if (event.type === 'service-response' && event.action === 'received') {
|
|
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)}`;
|
|
215
|
-
wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });
|
|
216
|
-
}
|
|
217
|
-
} catch {}
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
proc.on('exit', () => {
|
|
222
|
-
backgroundProcess = null;
|
|
223
|
-
if (serviceRunning) setTimeout(spawnConnect, 5000);
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
spawnConnect();
|
|
189
|
+
applyConfigToEnv(config);
|
|
190
|
+
|
|
191
|
+
// Start the connection directly as a library call
|
|
192
|
+
// This bypasses the child_process detection and is more efficient
|
|
193
|
+
cmdConnect((event: any) => {
|
|
194
|
+
if ((event.action === 'queued-for-agent' || event.action === 'already-queued') && event.serviceId) {
|
|
195
|
+
const rid = event.id || `${event.from}-${Date.now()}`;
|
|
196
|
+
if (wokenRequests.has(rid)) return;
|
|
197
|
+
wokenRequests.add(rid);
|
|
198
|
+
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: { ... } })`;
|
|
199
|
+
wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:${rid}` });
|
|
200
|
+
}
|
|
201
|
+
if (event.type === 'service-response' && event.action === 'received') {
|
|
202
|
+
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)}`;
|
|
203
|
+
wakeAgent(wakeText, api.logger, { sessionKey: `hook:openclaw-overlay:resp-${event.requestId || Date.now()}` });
|
|
204
|
+
}
|
|
205
|
+
}, abortController.signal).catch((err) => {
|
|
206
|
+
if (serviceRunning && !abortController?.signal.aborted) {
|
|
207
|
+
api.logger?.error?.(`[openclaw-overlay] WebSocket error: ${err.message}`);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
227
210
|
}
|
|
228
211
|
|
|
229
212
|
function stopBackgroundService() {
|
|
230
213
|
serviceRunning = false;
|
|
231
|
-
if (
|
|
214
|
+
if (abortController) {
|
|
215
|
+
abortController.abort();
|
|
216
|
+
abortController = null;
|
|
217
|
+
}
|
|
232
218
|
if (requestCleanupInterval) { clearInterval(requestCleanupInterval); requestCleanupInterval = null; }
|
|
233
219
|
wokenRequests.clear();
|
|
234
220
|
if (autoImportInterval) { clearInterval(autoImportInterval); autoImportInterval = null; }
|
|
@@ -288,8 +274,8 @@ export function register(api: any) {
|
|
|
288
274
|
id: "openclaw-overlay-relay",
|
|
289
275
|
start: async () => {
|
|
290
276
|
try { await initializeServiceSystem(); } catch {}
|
|
291
|
-
startBackgroundService(pluginConfig, api);
|
|
292
|
-
startAutoImport(pluginConfig, api);
|
|
277
|
+
await startBackgroundService(pluginConfig, api);
|
|
278
|
+
await startAutoImport(pluginConfig, api);
|
|
293
279
|
},
|
|
294
280
|
stop: () => stopBackgroundService()
|
|
295
281
|
});
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-overlay-plugin",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
4
4
|
"description": "Openclaw BSV Overlay — agent discovery, service marketplace, and micropayments on the BSV blockchain",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -24,23 +24,24 @@
|
|
|
24
24
|
"cli": "node dist/src/cli.js",
|
|
25
25
|
"test": "npx tsx src/test/cli.test.ts && npx tsx src/test/taskflow.test.ts && npx tsx src/test/key-derivation.test.ts",
|
|
26
26
|
"postversion": "node ../sync_versions.js",
|
|
27
|
-
"lint": "eslint src/**/*.ts"
|
|
27
|
+
"lint": "eslint src/**/*.ts",
|
|
28
|
+
"postinstall": "node -e \"try{require('better-sqlite3')}catch{console.log('Note: better-sqlite3 requires build tools. If install failed, ensure python3 and a C++ compiler are available.')}\""
|
|
28
29
|
},
|
|
29
30
|
"dependencies": {
|
|
30
31
|
"@bsv/sdk": "^2.0.13",
|
|
31
32
|
"@bsv/wallet-toolbox": "^2.1.18",
|
|
33
|
+
"better-sqlite3": "^11.0.0",
|
|
32
34
|
"dotenv": "^17.3.1",
|
|
33
|
-
"knex": "^3.
|
|
34
|
-
"sqlite3": "^5.1.7"
|
|
35
|
+
"knex": "^3.1.0"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^
|
|
38
|
+
"@types/node": "^22.10.0",
|
|
38
39
|
"@types/ws": "^8.18.1",
|
|
39
40
|
"@typescript-eslint/eslint-plugin": "^8.58.0",
|
|
40
41
|
"@typescript-eslint/parser": "^8.58.0",
|
|
41
42
|
"eslint": "^10.1.0",
|
|
42
43
|
"globals": "^17.4.0",
|
|
43
|
-
"typescript": "^
|
|
44
|
+
"typescript": "^5.7.0"
|
|
44
45
|
},
|
|
45
46
|
"optionalDependencies": {
|
|
46
47
|
"ws": "^8.20.0"
|
package/src/core/wallet.ts
CHANGED
|
@@ -279,7 +279,7 @@ export class BSVAgentWallet {
|
|
|
279
279
|
// 6. SQLite storage via knex
|
|
280
280
|
const filePath = path.join(config.storageDir, `${DEFAULT_DB_NAME}.sqlite`);
|
|
281
281
|
const knex = knexLib({
|
|
282
|
-
client: 'sqlite3',
|
|
282
|
+
client: 'better-sqlite3',
|
|
283
283
|
connection: { filename: filePath },
|
|
284
284
|
useNullAsDefault: true,
|
|
285
285
|
});
|
|
@@ -11,9 +11,9 @@ import { ensureStateDir } from '../utils/storage.js';
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Connect command: establish WebSocket connection for real-time messaging.
|
|
14
|
-
*
|
|
14
|
+
* Supports being used as a library with onMessage callback and AbortSignal.
|
|
15
15
|
*/
|
|
16
|
-
export async function cmdConnect(): Promise<void> {
|
|
16
|
+
export async function cmdConnect(onMessage?: (data: any) => void, signal?: AbortSignal): Promise<void> {
|
|
17
17
|
let WebSocketClient: any;
|
|
18
18
|
try {
|
|
19
19
|
const ws = await import('ws');
|
|
@@ -34,19 +34,36 @@ export async function cmdConnect(): Promise<void> {
|
|
|
34
34
|
if (currentWs) {
|
|
35
35
|
try { currentWs.close(); } catch {}
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
// Only exit if we're not running as a library
|
|
38
|
+
if (!onMessage) {
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
if (!onMessage) {
|
|
44
|
+
process.on('SIGINT', shutdown);
|
|
45
|
+
process.on('SIGTERM', shutdown);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (signal) {
|
|
49
|
+
signal.addEventListener('abort', () => {
|
|
50
|
+
shouldReconnect = false;
|
|
51
|
+
if (currentWs) {
|
|
52
|
+
try { currentWs.close(); } catch {}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
42
56
|
|
|
43
57
|
function connect() {
|
|
58
|
+
if (signal?.aborted) return;
|
|
44
59
|
const ws = new WebSocketClient(wsUrl);
|
|
45
60
|
currentWs = ws;
|
|
46
61
|
|
|
47
62
|
ws.on('open', () => {
|
|
48
63
|
reconnectDelay = 1000; // reset on successful connect
|
|
49
|
-
|
|
64
|
+
const log = { event: 'connected', identity: identityKey, overlay: OVERLAY_URL };
|
|
65
|
+
if (onMessage) onMessage(log);
|
|
66
|
+
else console.error(JSON.stringify(log));
|
|
50
67
|
});
|
|
51
68
|
|
|
52
69
|
ws.on('message', async (data: any) => {
|
|
@@ -54,8 +71,9 @@ export async function cmdConnect(): Promise<void> {
|
|
|
54
71
|
const envelope = JSON.parse(data.toString());
|
|
55
72
|
if (envelope.type === 'message') {
|
|
56
73
|
const result = await processMessage(envelope.message, identityKey, privKey);
|
|
57
|
-
|
|
58
|
-
|
|
74
|
+
|
|
75
|
+
if (onMessage) onMessage(result);
|
|
76
|
+
else console.log(JSON.stringify(result));
|
|
59
77
|
|
|
60
78
|
// Also append to notification log
|
|
61
79
|
ensureStateDir();
|
|
@@ -72,7 +90,9 @@ export async function cmdConnect(): Promise<void> {
|
|
|
72
90
|
body: JSON.stringify({ identity: identityKey, messageIds: [result.id] }),
|
|
73
91
|
});
|
|
74
92
|
} catch (ackErr: any) {
|
|
75
|
-
|
|
93
|
+
const log = { event: 'ack-error', id: result.id, message: String(ackErr) };
|
|
94
|
+
if (onMessage) onMessage(log);
|
|
95
|
+
else console.error(JSON.stringify(log));
|
|
76
96
|
}
|
|
77
97
|
}
|
|
78
98
|
}
|
|
@@ -90,32 +110,45 @@ export async function cmdConnect(): Promise<void> {
|
|
|
90
110
|
txid: envelope.txid,
|
|
91
111
|
_ts: Date.now(),
|
|
92
112
|
};
|
|
93
|
-
|
|
113
|
+
if (onMessage) onMessage(announcement);
|
|
114
|
+
else console.log(JSON.stringify(announcement));
|
|
115
|
+
|
|
94
116
|
ensureStateDir();
|
|
95
117
|
try {
|
|
96
118
|
fs.appendFileSync(PATHS.notifications, JSON.stringify(announcement) + '\n');
|
|
97
119
|
} catch {}
|
|
98
120
|
}
|
|
99
121
|
} catch (err: any) {
|
|
100
|
-
|
|
122
|
+
const log = { event: 'process-error', message: String(err) };
|
|
123
|
+
if (onMessage) onMessage(log);
|
|
124
|
+
else console.error(JSON.stringify(log));
|
|
101
125
|
}
|
|
102
126
|
});
|
|
103
127
|
|
|
104
128
|
ws.on('close', () => {
|
|
105
129
|
currentWs = null;
|
|
106
|
-
if (shouldReconnect) {
|
|
107
|
-
|
|
130
|
+
if (shouldReconnect && !signal?.aborted) {
|
|
131
|
+
const log = { event: 'disconnected', reconnectMs: reconnectDelay };
|
|
132
|
+
if (onMessage) onMessage(log);
|
|
133
|
+
else console.error(JSON.stringify(log));
|
|
134
|
+
|
|
108
135
|
setTimeout(connect, reconnectDelay);
|
|
109
136
|
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
|
|
110
137
|
}
|
|
111
138
|
});
|
|
112
139
|
|
|
113
140
|
ws.on('error', (err: any) => {
|
|
114
|
-
|
|
141
|
+
const log = { event: 'error', message: err.message };
|
|
142
|
+
if (onMessage) onMessage(log);
|
|
143
|
+
else console.error(JSON.stringify(log));
|
|
115
144
|
});
|
|
116
145
|
}
|
|
117
146
|
|
|
118
147
|
connect();
|
|
119
|
-
// Keep
|
|
120
|
-
|
|
148
|
+
// Keep alive
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
if (signal) {
|
|
151
|
+
signal.addEventListener('abort', () => resolve());
|
|
152
|
+
}
|
|
153
|
+
});
|
|
121
154
|
}
|