openclaw-overlay-plugin 0.7.65 → 0.7.67
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 +12 -2
- package/dist/index.js +97 -85
- package/dist/src/compatibility.test.d.ts +4 -0
- package/dist/src/compatibility.test.js +52 -0
- package/dist/src/scripts/baemail/commands.d.ts +18 -47
- package/dist/src/scripts/baemail/commands.js +112 -101
- package/dist/src/scripts/baemail/handler.js +2 -2
- package/dist/src/scripts/services/respond.js +1 -1
- package/dist/src/scripts/utils/storage.js +1 -1
- package/dist/src/test/request-response-flow.test.js +8 -7
- package/index.ts +106 -90
- package/openclaw.plugin.json +14 -50
- package/package.json +1 -1
- package/src/compatibility.test.ts +57 -0
- package/src/scripts/baemail/commands.ts +118 -146
- package/src/scripts/baemail/handler.ts +2 -2
- package/src/scripts/services/respond.ts +1 -1
- package/src/scripts/utils/storage.ts +1 -1
- package/src/test/request-response-flow.test.ts +8 -7
package/openclaw.plugin.json
CHANGED
|
@@ -2,10 +2,22 @@
|
|
|
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.7.
|
|
5
|
+
"version": "0.7.67",
|
|
6
6
|
"skills": [
|
|
7
7
|
"./SKILL.md"
|
|
8
8
|
],
|
|
9
|
+
"providerAuthEnvVars": {
|
|
10
|
+
"OVERLAY_URL": "Overlay server URL (defaults to https://clawoverlay.com)",
|
|
11
|
+
"BSV_NETWORK": "BSV network to use (mainnet/testnet)",
|
|
12
|
+
"BSV_WALLET_DIR": "Path to the shared BSV wallet directory"
|
|
13
|
+
},
|
|
14
|
+
"providerAuthChoices": [
|
|
15
|
+
{
|
|
16
|
+
"id": "standard-wallet",
|
|
17
|
+
"label": "Standard BSV Wallet (~/.openclaw/bsv-wallet)",
|
|
18
|
+
"description": "Uses the default shared agent identity key"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
9
21
|
"commands": [
|
|
10
22
|
{
|
|
11
23
|
"name": "overlay",
|
|
@@ -23,56 +35,8 @@
|
|
|
23
35
|
},
|
|
24
36
|
"configSchema": {
|
|
25
37
|
"type": "object",
|
|
26
|
-
"additionalProperties":
|
|
38
|
+
"additionalProperties": false,
|
|
27
39
|
"properties": {
|
|
28
|
-
"enabled": {
|
|
29
|
-
"type": "boolean"
|
|
30
|
-
},
|
|
31
|
-
"config": {
|
|
32
|
-
"type": "object",
|
|
33
|
-
"additionalProperties": true,
|
|
34
|
-
"properties": {
|
|
35
|
-
"overlayUrl": {
|
|
36
|
-
"type": "string"
|
|
37
|
-
},
|
|
38
|
-
"network": {
|
|
39
|
-
"type": "string"
|
|
40
|
-
},
|
|
41
|
-
"chaintracksUrl": {
|
|
42
|
-
"type": "string"
|
|
43
|
-
},
|
|
44
|
-
"arcUrl": {
|
|
45
|
-
"type": "string"
|
|
46
|
-
},
|
|
47
|
-
"agentName": {
|
|
48
|
-
"type": "string"
|
|
49
|
-
},
|
|
50
|
-
"agentDescription": {
|
|
51
|
-
"type": "string"
|
|
52
|
-
},
|
|
53
|
-
"walletDir": {
|
|
54
|
-
"type": "string"
|
|
55
|
-
},
|
|
56
|
-
"maxAutoPaySats": {
|
|
57
|
-
"type": "number"
|
|
58
|
-
},
|
|
59
|
-
"dailyBudgetSats": {
|
|
60
|
-
"type": "number"
|
|
61
|
-
},
|
|
62
|
-
"autoAcceptPayments": {
|
|
63
|
-
"type": "boolean"
|
|
64
|
-
},
|
|
65
|
-
"preferCheapest": {
|
|
66
|
-
"type": "boolean"
|
|
67
|
-
},
|
|
68
|
-
"services": {
|
|
69
|
-
"type": "array",
|
|
70
|
-
"items": {
|
|
71
|
-
"type": "string"
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
40
|
"overlayUrl": {
|
|
77
41
|
"type": "string",
|
|
78
42
|
"default": "https://clawoverlay.com",
|
package/package.json
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compatibility Test for Node 24 and OpenClaw SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import sqlite3 from 'sqlite3';
|
|
6
|
+
import { plugin } from '../index.js';
|
|
7
|
+
|
|
8
|
+
async function testCompatibility() {
|
|
9
|
+
console.log('--- Overlay Compatibility Test ---');
|
|
10
|
+
|
|
11
|
+
// 1. Verify SQLite3 Native Bindings
|
|
12
|
+
try {
|
|
13
|
+
const db = new sqlite3.Database(':memory:');
|
|
14
|
+
console.log('✓ SQLite3 native bindings loaded successfully.');
|
|
15
|
+
db.close();
|
|
16
|
+
} catch (err: any) {
|
|
17
|
+
console.error('✗ SQLite3 failed to load bindings:', err.message);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 2. Verify Plugin Registration (SDK Descriptors)
|
|
22
|
+
let registeredCli = false;
|
|
23
|
+
let hasDescriptors = false;
|
|
24
|
+
|
|
25
|
+
const mockApi = {
|
|
26
|
+
logger: { info: () => {}, warn: () => {}, error: () => {} },
|
|
27
|
+
registerTool: () => {},
|
|
28
|
+
registerCommand: () => {},
|
|
29
|
+
registerService: () => {},
|
|
30
|
+
registerCli: (fn: any, options: any) => {
|
|
31
|
+
registeredCli = true;
|
|
32
|
+
if (options && options.descriptors && options.descriptors[0].name === 'overlay') {
|
|
33
|
+
hasDescriptors = true;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
getConfig: () => ({ plugins: { entries: {} } })
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
plugin.register(mockApi);
|
|
41
|
+
if (registeredCli && hasDescriptors) {
|
|
42
|
+
console.log('✓ CLI descriptors registered correctly for Phase 1 discovery.');
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error(`CLI registration missing or using old 'commands' array format.`);
|
|
45
|
+
}
|
|
46
|
+
} catch (err: any) {
|
|
47
|
+
console.error('✗ Plugin registration failed:', err.message);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log('--- All Compatibility Tests Passed ---\n');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
testCompatibility().catch(err => {
|
|
55
|
+
console.error(err);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|
|
@@ -1,172 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
5
4
|
import fs from 'node:fs';
|
|
6
|
-
import
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
import { Buffer } from 'node:buffer';
|
|
7
7
|
import { ok, fail } from '../output.js';
|
|
8
8
|
import { loadIdentity } from '../wallet/identity.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
maxMessageLength: number;
|
|
21
|
-
blocklist: string[];
|
|
22
|
-
createdAt: string;
|
|
23
|
-
updatedAt: string;
|
|
24
|
-
}
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
// Define paths relative to home directory
|
|
14
|
+
const PATHS = {
|
|
15
|
+
walletIdentity: path.join(os.homedir(), '.openclaw', 'bsv-wallet', 'wallet-identity.json'),
|
|
16
|
+
baemailLog: path.join(os.homedir(), '.openclaw', 'openclaw-overlay', 'baemail-deliveries.jsonl'),
|
|
17
|
+
baemailConfig: path.join(os.homedir(), '.openclaw', 'openclaw-overlay', 'baemail-config.json'),
|
|
18
|
+
baemailBlocklist: path.join(os.homedir(), '.openclaw', 'openclaw-overlay', 'baemail-blocklist.json'),
|
|
19
|
+
};
|
|
25
20
|
|
|
26
21
|
export interface BaemailLogEntry {
|
|
27
22
|
requestId: string;
|
|
28
23
|
from: string;
|
|
29
|
-
|
|
30
|
-
tier: string;
|
|
24
|
+
to?: string;
|
|
31
25
|
paidSats: number;
|
|
32
|
-
messageLength: number;
|
|
33
|
-
deliveryChannel: string;
|
|
34
26
|
deliverySuccess: boolean;
|
|
35
|
-
|
|
36
|
-
paymentTxid: string;
|
|
37
|
-
refundStatus: string | null;
|
|
27
|
+
refundStatus?: 'pending' | 'completed';
|
|
38
28
|
refundTxid?: string;
|
|
39
|
-
refundedAt?: string;
|
|
40
|
-
timestamp: string;
|
|
41
29
|
_lineIdx?: number;
|
|
30
|
+
ts?: number;
|
|
31
|
+
senderName?: string;
|
|
32
|
+
messageLength?: number;
|
|
33
|
+
tier?: string;
|
|
34
|
+
deliveryChannel?: string;
|
|
35
|
+
deliveryError?: string | null;
|
|
36
|
+
paymentTxid?: string;
|
|
37
|
+
timestamp?: string;
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
/**
|
|
45
|
-
*
|
|
41
|
+
* Log a baemail delivery event.
|
|
46
42
|
*/
|
|
47
|
-
export function
|
|
43
|
+
export function logBaemailDelivery(entry: BaemailLogEntry) {
|
|
48
44
|
try {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
} catch
|
|
53
|
-
console.warn(`[baemail] Warning: Could not read config: ${(err as Error).message}`);
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
45
|
+
const dir = path.dirname(PATHS.baemailLog);
|
|
46
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
47
|
+
fs.appendFileSync(PATHS.baemailLog, JSON.stringify({ ...entry, ts: Date.now() }) + '\n');
|
|
48
|
+
} catch {}
|
|
56
49
|
}
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Setup baemail service with delivery channel and tier pricing.
|
|
68
|
-
*/
|
|
69
|
-
export async function cmdBaemailSetup(
|
|
70
|
-
channel: string | undefined,
|
|
71
|
-
standardStr: string | undefined,
|
|
72
|
-
priorityStr?: string,
|
|
73
|
-
urgentStr?: string
|
|
74
|
-
): Promise<never> {
|
|
75
|
-
if (!channel || !standardStr) {
|
|
76
|
-
return fail('Usage: baemail-setup <channel> <standardSats> [prioritySats] [urgentSats]');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const standard = parseInt(standardStr, 10);
|
|
80
|
-
const priority = priorityStr ? parseInt(priorityStr, 10) : standard * 2;
|
|
81
|
-
const urgent = urgentStr ? parseInt(urgentStr, 10) : standard * 5;
|
|
82
|
-
|
|
83
|
-
if (isNaN(standard) || standard < 1) {
|
|
84
|
-
return fail('Standard rate must be a positive integer (sats)');
|
|
85
|
-
}
|
|
86
|
-
if (priority < standard) {
|
|
87
|
-
return fail('Priority rate must be >= standard rate');
|
|
88
|
-
}
|
|
89
|
-
if (urgent < priority) {
|
|
90
|
-
return fail('Urgent rate must be >= priority rate');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const config: BaemailConfig = {
|
|
94
|
-
deliveryChannel: channel,
|
|
95
|
-
tiers: { standard, priority, urgent },
|
|
51
|
+
export async function loadBaemailConfig() {
|
|
52
|
+
const defaults = {
|
|
53
|
+
enabled: true,
|
|
54
|
+
priceSats: 100,
|
|
55
|
+
autoRefund: true,
|
|
56
|
+
blocklist: [] as string[],
|
|
96
57
|
maxMessageLength: 4000,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
58
|
+
deliveryChannel: 'agent-hook',
|
|
59
|
+
tiers: {
|
|
60
|
+
standard: 100,
|
|
61
|
+
priority: 500,
|
|
62
|
+
urgent: 1000
|
|
63
|
+
}
|
|
100
64
|
};
|
|
101
65
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return ok({
|
|
105
|
-
configured: true,
|
|
106
|
-
deliveryChannel: channel,
|
|
107
|
-
tiers: config.tiers,
|
|
108
|
-
note: `Advertise with: cli advertise baemail "Baemail" "Paid message forwarding. Pay ${standard}+ sats to reach me." ${standard}`,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* View current baemail configuration.
|
|
114
|
-
*/
|
|
115
|
-
export async function cmdBaemailConfig(): Promise<never> {
|
|
116
|
-
const config = loadBaemailConfig();
|
|
117
|
-
if (!config) {
|
|
118
|
-
return fail('Baemail not configured. Run: baemail-setup <channel> <standardSats> [prioritySats] [urgentSats]');
|
|
66
|
+
if (!fs.existsSync(PATHS.baemailConfig)) {
|
|
67
|
+
return defaults;
|
|
119
68
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
*/
|
|
126
|
-
export async function cmdBaemailBlock(identityKey: string | undefined): Promise<never> {
|
|
127
|
-
if (!identityKey) return fail('Usage: baemail-block <identityKey>');
|
|
128
|
-
|
|
129
|
-
const config = loadBaemailConfig();
|
|
130
|
-
if (!config) {
|
|
131
|
-
return fail('Baemail not configured. Run baemail-setup first.');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (!config.blocklist) config.blocklist = [];
|
|
135
|
-
if (config.blocklist.includes(identityKey)) {
|
|
136
|
-
return fail('Identity already blocked');
|
|
69
|
+
try {
|
|
70
|
+
const config = JSON.parse(fs.readFileSync(PATHS.baemailConfig, 'utf-8'));
|
|
71
|
+
return { ...defaults, ...config };
|
|
72
|
+
} catch {
|
|
73
|
+
return defaults;
|
|
137
74
|
}
|
|
138
|
-
|
|
139
|
-
config.blocklist.push(identityKey);
|
|
140
|
-
config.updatedAt = new Date().toISOString();
|
|
141
|
-
saveBaemailConfig(config);
|
|
142
|
-
|
|
143
|
-
return ok({ blocked: identityKey, totalBlocked: config.blocklist.length });
|
|
144
75
|
}
|
|
145
76
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
77
|
+
async function fetchWithTimeout(url: string, options: any = {}) {
|
|
78
|
+
const { timeout = 15000 } = options;
|
|
79
|
+
const controller = new AbortController();
|
|
80
|
+
const id = setTimeout(() => controller.abort(), timeout);
|
|
81
|
+
try {
|
|
82
|
+
const response = await fetch(url, {
|
|
83
|
+
...options,
|
|
84
|
+
signal: controller.signal
|
|
85
|
+
});
|
|
86
|
+
clearTimeout(id);
|
|
87
|
+
return response;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
clearTimeout(id);
|
|
90
|
+
throw err;
|
|
159
91
|
}
|
|
160
|
-
|
|
161
|
-
config.blocklist = config.blocklist.filter(k => k !== identityKey);
|
|
162
|
-
config.updatedAt = new Date().toISOString();
|
|
163
|
-
saveBaemailConfig(config);
|
|
164
|
-
|
|
165
|
-
return ok({ unblocked: identityKey, totalBlocked: config.blocklist.length });
|
|
166
92
|
}
|
|
167
93
|
|
|
168
94
|
/**
|
|
169
|
-
*
|
|
95
|
+
* List recent baemail deliveries.
|
|
170
96
|
*/
|
|
171
97
|
export async function cmdBaemailLog(limitStr?: string): Promise<never> {
|
|
172
98
|
const limit = parseInt(limitStr || '20', 10) || 20;
|
|
@@ -175,8 +101,8 @@ export async function cmdBaemailLog(limitStr?: string): Promise<never> {
|
|
|
175
101
|
return ok({ log: [], count: 0 });
|
|
176
102
|
}
|
|
177
103
|
|
|
178
|
-
const lines = fs.readFileSync(PATHS.baemailLog, 'utf-8').split('\n').filter(l => l.trim());
|
|
179
|
-
const entries: BaemailLogEntry[] = lines.map(l => {
|
|
104
|
+
const lines = fs.readFileSync(PATHS.baemailLog, 'utf-8').split('\n').filter((l: string) => l.trim());
|
|
105
|
+
const entries: BaemailLogEntry[] = lines.map((l: string) => {
|
|
180
106
|
try { return JSON.parse(l); } catch { return null; }
|
|
181
107
|
}).filter(Boolean) as BaemailLogEntry[];
|
|
182
108
|
|
|
@@ -184,6 +110,52 @@ export async function cmdBaemailLog(limitStr?: string): Promise<never> {
|
|
|
184
110
|
return ok({ log: recent, count: entries.length, showing: recent.length });
|
|
185
111
|
}
|
|
186
112
|
|
|
113
|
+
export async function cmdBaemailSetup(priceSatsStr: string, prioritySats?: string, urgentSats?: string, channel?: string): Promise<never> {
|
|
114
|
+
const standard = parseInt(priceSatsStr, 10) || 100;
|
|
115
|
+
const priority = parseInt(prioritySats || '', 10) || (standard * 5);
|
|
116
|
+
const urgent = parseInt(urgentSats || '', 10) || (standard * 10);
|
|
117
|
+
|
|
118
|
+
const config = {
|
|
119
|
+
enabled: true,
|
|
120
|
+
priceSats: standard,
|
|
121
|
+
autoRefund: true,
|
|
122
|
+
deliveryChannel: channel || 'agent-hook',
|
|
123
|
+
tiers: { standard, priority, urgent }
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const dir = path.dirname(PATHS.baemailConfig);
|
|
127
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
128
|
+
fs.writeFileSync(PATHS.baemailConfig, JSON.stringify(config, null, 2));
|
|
129
|
+
return ok({ message: `Baemail setup complete.`, config });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function cmdBaemailConfig(): Promise<never> {
|
|
133
|
+
const config = await loadBaemailConfig();
|
|
134
|
+
return ok(config);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function cmdBaemailBlock(pubkey: string): Promise<never> {
|
|
138
|
+
if (!pubkey) return fail('Usage: baemail-block <pubkey>');
|
|
139
|
+
let blocklist: string[] = [];
|
|
140
|
+
if (fs.existsSync(PATHS.baemailBlocklist)) {
|
|
141
|
+
try { blocklist = JSON.parse(fs.readFileSync(PATHS.baemailBlocklist, 'utf-8')); } catch { blocklist = []; }
|
|
142
|
+
}
|
|
143
|
+
if (!blocklist.includes(pubkey)) blocklist.push(pubkey);
|
|
144
|
+
fs.writeFileSync(PATHS.baemailBlocklist, JSON.stringify(blocklist, null, 2));
|
|
145
|
+
return ok({ blocked: true, pubkey, count: blocklist.length });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function cmdBaemailUnblock(pubkey: string): Promise<never> {
|
|
149
|
+
if (!pubkey) return fail('Usage: baemail-unblock <pubkey>');
|
|
150
|
+
let blocklist: string[] = [];
|
|
151
|
+
if (fs.existsSync(PATHS.baemailBlocklist)) {
|
|
152
|
+
try { blocklist = JSON.parse(fs.readFileSync(PATHS.baemailBlocklist, 'utf-8')); } catch { blocklist = []; }
|
|
153
|
+
}
|
|
154
|
+
blocklist = blocklist.filter(p => p !== pubkey);
|
|
155
|
+
fs.writeFileSync(PATHS.baemailBlocklist, JSON.stringify(blocklist, null, 2));
|
|
156
|
+
return ok({ unblocked: true, pubkey, count: blocklist.length });
|
|
157
|
+
}
|
|
158
|
+
|
|
187
159
|
/**
|
|
188
160
|
* Refund a failed baemail delivery.
|
|
189
161
|
*/
|
|
@@ -195,8 +167,8 @@ export async function cmdBaemailRefund(requestId: string | undefined): Promise<n
|
|
|
195
167
|
}
|
|
196
168
|
|
|
197
169
|
// Find the entry
|
|
198
|
-
const lines = fs.readFileSync(PATHS.baemailLog, 'utf-8').split('\n').filter(l => l.trim());
|
|
199
|
-
const entries: BaemailLogEntry[] = lines.map((l, idx) => {
|
|
170
|
+
const lines = fs.readFileSync(PATHS.baemailLog, 'utf-8').split('\n').filter((l: string) => l.trim());
|
|
171
|
+
const entries: BaemailLogEntry[] = lines.map((l: string, idx: number) => {
|
|
200
172
|
try { return { ...JSON.parse(l), _lineIdx: idx }; } catch { return null; }
|
|
201
173
|
}).filter(Boolean) as BaemailLogEntry[];
|
|
202
174
|
|
|
@@ -289,7 +261,7 @@ export async function cmdBaemailRefund(requestId: string | undefined): Promise<n
|
|
|
289
261
|
await tx.sign();
|
|
290
262
|
|
|
291
263
|
// Broadcast using configured ARC/Arcade URL or fallback to WhatsOnChain
|
|
292
|
-
const arcUrl = (process as any)['
|
|
264
|
+
const arcUrl = (process as any)['env'].BSV_ARC_URL;
|
|
293
265
|
let broadcastResp;
|
|
294
266
|
|
|
295
267
|
if (arcUrl) {
|
|
@@ -314,7 +286,7 @@ export async function cmdBaemailRefund(requestId: string | undefined): Promise<n
|
|
|
314
286
|
const txid = tx.id('hex');
|
|
315
287
|
|
|
316
288
|
// Update log entry
|
|
317
|
-
const updatedLines = lines.map((l, idx) => {
|
|
289
|
+
const updatedLines = lines.map((l: string, idx: number) => {
|
|
318
290
|
if (idx === entry._lineIdx) {
|
|
319
291
|
const updated = { ...JSON.parse(l), refundStatus: 'completed', refundTxid: txid, refundedAt: new Date().toISOString() };
|
|
320
292
|
return JSON.stringify(updated);
|
|
@@ -68,7 +68,7 @@ export async function processBaemail(
|
|
|
68
68
|
const payment = msg.payload?.payment;
|
|
69
69
|
|
|
70
70
|
// Load config
|
|
71
|
-
const config = loadBaemailConfig();
|
|
71
|
+
const config = await loadBaemailConfig();
|
|
72
72
|
if (!config) {
|
|
73
73
|
const rejectPayload = {
|
|
74
74
|
requestId: msg.id,
|
|
@@ -289,7 +289,7 @@ _Reply via overlay: \`cli send ${replyKey} ping "your reply"\`_`;
|
|
|
289
289
|
deliverySuccess,
|
|
290
290
|
deliveryError: deliveryError ?? null,
|
|
291
291
|
paymentTxid: payResult.txid || '',
|
|
292
|
-
refundStatus: deliverySuccess ?
|
|
292
|
+
refundStatus: deliverySuccess ? undefined : 'pending',
|
|
293
293
|
timestamp: new Date().toISOString(),
|
|
294
294
|
};
|
|
295
295
|
fs.appendFileSync(PATHS.baemailLog, JSON.stringify(logEntry) + '\n');
|
|
@@ -139,7 +139,7 @@ export async function cmdResearchRespond(resultJsonPath: string | undefined): Pr
|
|
|
139
139
|
// Remove from queue
|
|
140
140
|
if (fs.existsSync(PATHS.researchQueue)) {
|
|
141
141
|
const lines = fs.readFileSync(PATHS.researchQueue, 'utf-8').trim().split('\n').filter(Boolean);
|
|
142
|
-
const remaining = lines.filter(l => {
|
|
142
|
+
const remaining = lines.filter((l: string) => {
|
|
143
143
|
try { return JSON.parse(l).requestId !== requestId; } catch { return true; }
|
|
144
144
|
});
|
|
145
145
|
fs.writeFileSync(PATHS.researchQueue, remaining.length ? remaining.join('\n') + '\n' : '');
|
|
@@ -104,7 +104,7 @@ export function appendToJsonl(filePath: string, entry: Record<string, unknown>):
|
|
|
104
104
|
export function readJsonl<T>(filePath: string): T[] {
|
|
105
105
|
if (!fs.existsSync(filePath)) return [];
|
|
106
106
|
const lines = fs.readFileSync(filePath, 'utf-8').trim().split('\n').filter(Boolean);
|
|
107
|
-
return lines.map(line => {
|
|
107
|
+
return lines.map((line: string) => {
|
|
108
108
|
try {
|
|
109
109
|
return JSON.parse(line);
|
|
110
110
|
} catch {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import process from 'node:process';
|
|
7
8
|
|
|
8
9
|
// Simple test runner (matching existing pattern)
|
|
9
10
|
let passed = 0;
|
|
@@ -127,13 +128,13 @@ async function run() {
|
|
|
127
128
|
|
|
128
129
|
// Check remaining entries
|
|
129
130
|
const lines = fs.readFileSync(TEST_PATHS.serviceQueue, 'utf-8').trim().split('\n').filter(Boolean);
|
|
130
|
-
const remaining = lines.map(line => JSON.parse(line));
|
|
131
|
+
const remaining = lines.map((line: string) => JSON.parse(line));
|
|
131
132
|
|
|
132
133
|
assert(remaining.length === 2, `Expected 2 remaining entries, got ${remaining.length}`);
|
|
133
|
-
assert(remaining.find(e => e.requestId === 'pending-1') !== undefined, 'Should keep recent pending');
|
|
134
|
-
assert(remaining.find(e => e.requestId === 'recent-fulfilled') !== undefined, 'Should keep recent fulfilled');
|
|
135
|
-
assert(remaining.find(e => e.requestId === 'old-fulfilled') === undefined, 'Should remove old fulfilled');
|
|
136
|
-
assert(remaining.find(e => e.requestId === 'old-rejected') === undefined, 'Should remove old rejected');
|
|
134
|
+
assert(remaining.find((e: any) => e.requestId === 'pending-1') !== undefined, 'Should keep recent pending');
|
|
135
|
+
assert(remaining.find((e: any) => e.requestId === 'recent-fulfilled') !== undefined, 'Should keep recent fulfilled');
|
|
136
|
+
assert(remaining.find((e: any) => e.requestId === 'old-fulfilled') === undefined, 'Should remove old fulfilled');
|
|
137
|
+
assert(remaining.find((e: any) => e.requestId === 'old-rejected') === undefined, 'Should remove old rejected');
|
|
137
138
|
|
|
138
139
|
cleanupTestEnv();
|
|
139
140
|
});
|
|
@@ -159,7 +160,7 @@ async function run() {
|
|
|
159
160
|
const lines = fs.readFileSync(TEST_PATHS.serviceQueue, 'utf-8').trim().split('\n').filter(Boolean);
|
|
160
161
|
let updated = false;
|
|
161
162
|
|
|
162
|
-
const updatedLines = lines.map(line => {
|
|
163
|
+
const updatedLines = lines.map((line: string) => {
|
|
163
164
|
try {
|
|
164
165
|
const entryData = JSON.parse(line);
|
|
165
166
|
if (entryData.requestId === requestId) {
|
|
@@ -215,7 +216,7 @@ async function run() {
|
|
|
215
216
|
const lines = fs.readFileSync(TEST_PATHS.serviceQueue, 'utf-8').trim().split('\n').filter(Boolean);
|
|
216
217
|
let updated = false;
|
|
217
218
|
|
|
218
|
-
lines.map(line => {
|
|
219
|
+
lines.map((line: string) => {
|
|
219
220
|
try {
|
|
220
221
|
const entry = JSON.parse(line);
|
|
221
222
|
if (entry.requestId === requestId) {
|