openclaw-overlay-plugin 0.7.30 → 0.7.32
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 +1 -0
- package/dist/index.js +1408 -0
- package/dist/src/cli-main.d.ts +7 -0
- package/dist/src/cli-main.js +192 -0
- package/dist/src/cli.d.ts +8 -0
- package/dist/src/cli.js +14 -0
- package/dist/src/core/config.d.ts +11 -0
- package/dist/src/core/config.js +13 -0
- package/dist/src/core/index.d.ts +25 -0
- package/dist/src/core/index.js +26 -0
- package/dist/src/core/payment.d.ts +16 -0
- package/dist/src/core/payment.js +94 -0
- package/dist/src/core/types.d.ts +94 -0
- package/dist/src/core/types.js +4 -0
- package/dist/src/core/verify.d.ts +28 -0
- package/dist/src/core/verify.js +104 -0
- package/dist/src/core/wallet.d.ts +99 -0
- package/dist/src/core/wallet.js +220 -0
- package/dist/src/scripts/baemail/commands.d.ts +64 -0
- package/dist/src/scripts/baemail/commands.js +259 -0
- package/dist/src/scripts/baemail/handler.d.ts +36 -0
- package/dist/src/scripts/baemail/handler.js +284 -0
- package/dist/src/scripts/baemail/index.d.ts +5 -0
- package/dist/src/scripts/baemail/index.js +5 -0
- package/dist/src/scripts/config.d.ts +48 -0
- package/dist/src/scripts/config.js +68 -0
- package/dist/src/scripts/index.d.ts +7 -0
- package/dist/src/scripts/index.js +7 -0
- package/dist/src/scripts/messaging/connect.d.ts +8 -0
- package/dist/src/scripts/messaging/connect.js +114 -0
- package/dist/src/scripts/messaging/handlers.d.ts +21 -0
- package/dist/src/scripts/messaging/handlers.js +334 -0
- package/dist/src/scripts/messaging/inbox.d.ts +11 -0
- package/dist/src/scripts/messaging/inbox.js +51 -0
- package/dist/src/scripts/messaging/index.d.ts +8 -0
- package/dist/src/scripts/messaging/index.js +8 -0
- package/dist/src/scripts/messaging/poll.d.ts +7 -0
- package/dist/src/scripts/messaging/poll.js +52 -0
- package/dist/src/scripts/messaging/send.d.ts +7 -0
- package/dist/src/scripts/messaging/send.js +43 -0
- package/dist/src/scripts/output.d.ts +12 -0
- package/dist/src/scripts/output.js +19 -0
- package/dist/src/scripts/overlay/discover.d.ts +7 -0
- package/dist/src/scripts/overlay/discover.js +72 -0
- package/dist/src/scripts/overlay/index.d.ts +7 -0
- package/dist/src/scripts/overlay/index.js +7 -0
- package/dist/src/scripts/overlay/registration.d.ts +19 -0
- package/dist/src/scripts/overlay/registration.js +176 -0
- package/dist/src/scripts/overlay/services.d.ts +29 -0
- package/dist/src/scripts/overlay/services.js +167 -0
- package/dist/src/scripts/overlay/transaction.d.ts +42 -0
- package/dist/src/scripts/overlay/transaction.js +103 -0
- package/dist/src/scripts/payment/build.d.ts +24 -0
- package/dist/src/scripts/payment/build.js +54 -0
- package/dist/src/scripts/payment/commands.d.ts +15 -0
- package/dist/src/scripts/payment/commands.js +73 -0
- package/dist/src/scripts/payment/index.d.ts +6 -0
- package/dist/src/scripts/payment/index.js +6 -0
- package/dist/src/scripts/payment/types.d.ts +56 -0
- package/dist/src/scripts/payment/types.js +4 -0
- package/dist/src/scripts/services/index.d.ts +6 -0
- package/dist/src/scripts/services/index.js +6 -0
- package/dist/src/scripts/services/queue.d.ts +11 -0
- package/dist/src/scripts/services/queue.js +28 -0
- package/dist/src/scripts/services/request.d.ts +7 -0
- package/dist/src/scripts/services/request.js +82 -0
- package/dist/src/scripts/services/respond.d.ts +11 -0
- package/dist/src/scripts/services/respond.js +132 -0
- package/dist/src/scripts/types.d.ts +107 -0
- package/dist/src/scripts/types.js +4 -0
- package/dist/src/scripts/utils/index.d.ts +6 -0
- package/dist/src/scripts/utils/index.js +6 -0
- package/dist/src/scripts/utils/merkle.d.ts +12 -0
- package/dist/src/scripts/utils/merkle.js +47 -0
- package/dist/src/scripts/utils/storage.d.ts +66 -0
- package/dist/src/scripts/utils/storage.js +211 -0
- package/dist/src/scripts/utils/woc.d.ts +26 -0
- package/dist/src/scripts/utils/woc.js +91 -0
- package/dist/src/scripts/wallet/balance.d.ts +22 -0
- package/dist/src/scripts/wallet/balance.js +240 -0
- package/dist/src/scripts/wallet/identity.d.ts +70 -0
- package/dist/src/scripts/wallet/identity.js +151 -0
- package/dist/src/scripts/wallet/index.d.ts +6 -0
- package/dist/src/scripts/wallet/index.js +6 -0
- package/dist/src/scripts/wallet/setup.d.ts +15 -0
- package/dist/src/scripts/wallet/setup.js +105 -0
- package/dist/src/scripts/x-verification/commands.d.ts +27 -0
- package/dist/src/scripts/x-verification/commands.js +222 -0
- package/dist/src/scripts/x-verification/index.d.ts +4 -0
- package/dist/src/scripts/x-verification/index.js +4 -0
- package/dist/src/services/built-in/api-proxy/index.d.ts +6 -0
- package/dist/src/services/built-in/api-proxy/index.js +23 -0
- package/dist/src/services/built-in/code-develop/index.d.ts +6 -0
- package/dist/src/services/built-in/code-develop/index.js +23 -0
- package/dist/src/services/built-in/code-review/index.d.ts +10 -0
- package/dist/src/services/built-in/code-review/index.js +51 -0
- package/dist/src/services/built-in/image-analysis/index.d.ts +6 -0
- package/dist/src/services/built-in/image-analysis/index.js +33 -0
- package/dist/src/services/built-in/memory-store/index.d.ts +6 -0
- package/dist/src/services/built-in/memory-store/index.js +22 -0
- package/dist/src/services/built-in/roulette/index.d.ts +6 -0
- package/dist/src/services/built-in/roulette/index.js +27 -0
- package/dist/src/services/built-in/summarize/index.d.ts +6 -0
- package/dist/src/services/built-in/summarize/index.js +21 -0
- package/dist/src/services/built-in/tell-joke/handler.d.ts +7 -0
- package/dist/src/services/built-in/tell-joke/handler.js +122 -0
- package/dist/src/services/built-in/tell-joke/index.d.ts +9 -0
- package/dist/src/services/built-in/tell-joke/index.js +31 -0
- package/dist/src/services/built-in/translate/index.d.ts +6 -0
- package/dist/src/services/built-in/translate/index.js +21 -0
- package/dist/src/services/built-in/web-research/index.d.ts +9 -0
- package/dist/src/services/built-in/web-research/index.js +51 -0
- package/dist/src/services/index.d.ts +13 -0
- package/dist/src/services/index.js +14 -0
- package/dist/src/services/loader.d.ts +77 -0
- package/dist/src/services/loader.js +292 -0
- package/dist/src/services/manager.d.ts +86 -0
- package/dist/src/services/manager.js +255 -0
- package/dist/src/services/registry.d.ts +98 -0
- package/dist/src/services/registry.js +204 -0
- package/dist/src/services/types.d.ts +230 -0
- package/dist/src/services/types.js +30 -0
- package/dist/src/test/cli.test.d.ts +7 -0
- package/dist/src/test/cli.test.js +329 -0
- package/dist/src/test/comprehensive-overlay.test.d.ts +13 -0
- package/dist/src/test/comprehensive-overlay.test.js +593 -0
- package/dist/src/test/key-derivation.test.d.ts +12 -0
- package/dist/src/test/key-derivation.test.js +86 -0
- package/dist/src/test/overlay-submit.test.d.ts +10 -0
- package/dist/src/test/overlay-submit.test.js +460 -0
- package/dist/src/test/request-response-flow.test.d.ts +5 -0
- package/dist/src/test/request-response-flow.test.js +209 -0
- package/dist/src/test/service-system.test.d.ts +5 -0
- package/dist/src/test/service-system.test.js +190 -0
- package/dist/src/test/utils/server-logic.d.ts +98 -0
- package/dist/src/test/utils/server-logic.js +286 -0
- package/dist/src/test/wallet.test.d.ts +7 -0
- package/dist/src/test/wallet.test.js +146 -0
- package/index.ts +260 -633
- package/package.json +2 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment CLI commands: pay, verify, accept.
|
|
3
|
+
*/
|
|
4
|
+
import { NETWORK, WALLET_DIR } from '../config.js';
|
|
5
|
+
import { ok, fail } from '../output.js';
|
|
6
|
+
import { buildDirectPayment } from './build.js';
|
|
7
|
+
import { BSVAgentWallet } from '../../core/index.js';
|
|
8
|
+
async function getBSVAgentWallet() {
|
|
9
|
+
return BSVAgentWallet;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Pay command: send satoshis to another agent.
|
|
13
|
+
*/
|
|
14
|
+
export async function cmdPay(pubkey, satoshis, description) {
|
|
15
|
+
if (!pubkey || !satoshis) {
|
|
16
|
+
return fail('Usage: pay <pubkey> <satoshis> [description]');
|
|
17
|
+
}
|
|
18
|
+
const sats = parseInt(satoshis, 10);
|
|
19
|
+
if (isNaN(sats) || sats <= 0) {
|
|
20
|
+
return fail('satoshis must be a positive integer');
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const payment = await buildDirectPayment(pubkey, sats, description || 'agent payment');
|
|
24
|
+
return ok(payment);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Verify command: verify an incoming payment BEEF.
|
|
32
|
+
*/
|
|
33
|
+
export async function cmdVerify(beefBase64) {
|
|
34
|
+
if (!beefBase64) {
|
|
35
|
+
return fail('Usage: verify <beef_base64>');
|
|
36
|
+
}
|
|
37
|
+
const BSVAgentWallet = await getBSVAgentWallet();
|
|
38
|
+
const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
|
|
39
|
+
try {
|
|
40
|
+
const result = wallet.verifyPayment({ beef: beefBase64 });
|
|
41
|
+
await wallet.destroy();
|
|
42
|
+
return ok(result);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
await wallet.destroy();
|
|
46
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Accept command: accept and internalize a payment.
|
|
51
|
+
*/
|
|
52
|
+
export async function cmdAccept(beef, derivationPrefix, derivationSuffix, senderIdentityKey, description) {
|
|
53
|
+
if (!beef || !derivationPrefix || !derivationSuffix || !senderIdentityKey) {
|
|
54
|
+
return fail('Usage: accept <beef> <prefix> <suffix> <senderKey> [description]');
|
|
55
|
+
}
|
|
56
|
+
const BSVAgentWallet = await getBSVAgentWallet();
|
|
57
|
+
const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
|
|
58
|
+
try {
|
|
59
|
+
const receipt = await wallet.acceptPayment({
|
|
60
|
+
beef,
|
|
61
|
+
derivationPrefix,
|
|
62
|
+
derivationSuffix,
|
|
63
|
+
senderIdentityKey,
|
|
64
|
+
description: description || undefined,
|
|
65
|
+
});
|
|
66
|
+
await wallet.destroy();
|
|
67
|
+
return ok(receipt);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
await wallet.destroy();
|
|
71
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment-specific type definitions.
|
|
3
|
+
*/
|
|
4
|
+
export interface PaymentResult {
|
|
5
|
+
/** Base64-encoded Atomic BEEF transaction data */
|
|
6
|
+
beef: string;
|
|
7
|
+
/** Transaction ID (hex) */
|
|
8
|
+
txid: string;
|
|
9
|
+
/** Amount paid in satoshis */
|
|
10
|
+
satoshis: number;
|
|
11
|
+
/** BRC-29 derivation prefix (base64) - needed by recipient */
|
|
12
|
+
derivationPrefix: string;
|
|
13
|
+
/** BRC-29 derivation suffix (base64) - needed by recipient */
|
|
14
|
+
derivationSuffix: string;
|
|
15
|
+
/** Sender's identity key (compressed hex) - needed by recipient */
|
|
16
|
+
senderIdentityKey: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PaymentParams {
|
|
19
|
+
/** Recipient's compressed public key (hex, 66 chars starting with 02/03) */
|
|
20
|
+
to: string;
|
|
21
|
+
/** Amount to pay in satoshis */
|
|
22
|
+
satoshis: number;
|
|
23
|
+
/** Optional human-readable description */
|
|
24
|
+
description?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface VerifyParams {
|
|
27
|
+
/** Base64-encoded BEEF */
|
|
28
|
+
beef: string;
|
|
29
|
+
/** Expected amount (optional) */
|
|
30
|
+
expectedAmount?: number;
|
|
31
|
+
/** Expected sender identity key (optional) */
|
|
32
|
+
expectedSender?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface VerifyResult {
|
|
35
|
+
valid: boolean;
|
|
36
|
+
txid: string;
|
|
37
|
+
outputCount: number;
|
|
38
|
+
errors: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface AcceptParams {
|
|
41
|
+
/** Base64-encoded Atomic BEEF */
|
|
42
|
+
beef: string;
|
|
43
|
+
/** Output index (default: 0) */
|
|
44
|
+
vout?: number;
|
|
45
|
+
/** BRC-29 derivation prefix from PaymentResult */
|
|
46
|
+
derivationPrefix: string;
|
|
47
|
+
/** BRC-29 derivation suffix from PaymentResult */
|
|
48
|
+
derivationSuffix: string;
|
|
49
|
+
/** Sender's identity key from PaymentResult */
|
|
50
|
+
senderIdentityKey: string;
|
|
51
|
+
/** Optional description */
|
|
52
|
+
description?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface AcceptResult {
|
|
55
|
+
accepted: boolean;
|
|
56
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service queue commands.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Service queue command: list pending service requests.
|
|
6
|
+
*/
|
|
7
|
+
export declare function cmdServiceQueue(): Promise<never>;
|
|
8
|
+
/**
|
|
9
|
+
* Research queue command: list pending research requests.
|
|
10
|
+
*/
|
|
11
|
+
export declare function cmdResearchQueue(): Promise<never>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service queue commands.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { PATHS } from '../config.js';
|
|
6
|
+
import { ok } from '../output.js';
|
|
7
|
+
import { readJsonl } from '../utils/storage.js';
|
|
8
|
+
/**
|
|
9
|
+
* Service queue command: list pending service requests.
|
|
10
|
+
*/
|
|
11
|
+
export async function cmdServiceQueue() {
|
|
12
|
+
if (!fs.existsSync(PATHS.serviceQueue)) {
|
|
13
|
+
return ok({ pending: [], count: 0 });
|
|
14
|
+
}
|
|
15
|
+
const entries = readJsonl(PATHS.serviceQueue);
|
|
16
|
+
const pending = entries.filter(e => e.status === 'pending');
|
|
17
|
+
return ok({ pending, count: pending.length, total: entries.length });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Research queue command: list pending research requests.
|
|
21
|
+
*/
|
|
22
|
+
export async function cmdResearchQueue() {
|
|
23
|
+
if (!fs.existsSync(PATHS.researchQueue)) {
|
|
24
|
+
return ok({ pending: [] });
|
|
25
|
+
}
|
|
26
|
+
const entries = readJsonl(PATHS.researchQueue);
|
|
27
|
+
return ok({ pending: entries, count: entries.length });
|
|
28
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service request command.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Request service command: send a service request with optional payment.
|
|
6
|
+
*/
|
|
7
|
+
export declare function cmdRequestService(targetKey: string | undefined, serviceId: string | undefined, satsStr?: string, inputJsonStr?: string): Promise<never>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service request command.
|
|
3
|
+
*/
|
|
4
|
+
import { OVERLAY_URL } from '../config.js';
|
|
5
|
+
import { ok, fail } from '../output.js';
|
|
6
|
+
import { loadIdentity, signRelayMessage } from '../wallet/identity.js';
|
|
7
|
+
import { buildDirectPayment } from '../payment/build.js';
|
|
8
|
+
/**
|
|
9
|
+
* Request service command: send a service request with optional payment.
|
|
10
|
+
*/
|
|
11
|
+
export async function cmdRequestService(targetKey, serviceId, satsStr, inputJsonStr) {
|
|
12
|
+
if (!targetKey || !serviceId) {
|
|
13
|
+
return fail('Usage: request-service <identityKey> <serviceId> [sats] [inputJson]');
|
|
14
|
+
}
|
|
15
|
+
if (!/^0[23][0-9a-fA-F]{64}$/.test(targetKey)) {
|
|
16
|
+
return fail('Target must be a compressed public key (66 hex chars, 02/03 prefix)');
|
|
17
|
+
}
|
|
18
|
+
const { identityKey, privKey } = await loadIdentity();
|
|
19
|
+
const sats = parseInt(satsStr || '5', 10);
|
|
20
|
+
// Parse optional input JSON
|
|
21
|
+
let inputData = null;
|
|
22
|
+
if (inputJsonStr) {
|
|
23
|
+
try {
|
|
24
|
+
inputData = JSON.parse(inputJsonStr);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return fail('inputJson must be valid JSON');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Build the service request payload
|
|
31
|
+
let paymentData = null;
|
|
32
|
+
if (sats > 0) {
|
|
33
|
+
try {
|
|
34
|
+
const payment = await buildDirectPayment(targetKey, sats, `service-request: ${serviceId}`);
|
|
35
|
+
paymentData = {
|
|
36
|
+
beef: payment.beef,
|
|
37
|
+
txid: payment.txid,
|
|
38
|
+
satoshis: payment.satoshis,
|
|
39
|
+
derivationPrefix: payment.derivationPrefix,
|
|
40
|
+
derivationSuffix: payment.derivationSuffix,
|
|
41
|
+
senderIdentityKey: payment.senderIdentityKey,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
// Payment failed — send request without payment
|
|
46
|
+
paymentData = { error: String(err.message || err) };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const requestPayload = {
|
|
50
|
+
serviceId,
|
|
51
|
+
...(inputData ? { input: inputData } : {}),
|
|
52
|
+
payment: paymentData,
|
|
53
|
+
requestedAt: new Date().toISOString(),
|
|
54
|
+
};
|
|
55
|
+
const signature = await signRelayMessage(privKey, targetKey, 'service-request', requestPayload);
|
|
56
|
+
const resp = await fetch(`${OVERLAY_URL}/relay/send`, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: { 'Content-Type': 'application/json' },
|
|
59
|
+
body: JSON.stringify({
|
|
60
|
+
from: identityKey,
|
|
61
|
+
to: targetKey,
|
|
62
|
+
type: 'service-request',
|
|
63
|
+
payload: requestPayload,
|
|
64
|
+
signature,
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
if (!resp.ok) {
|
|
68
|
+
const body = await resp.text();
|
|
69
|
+
return fail(`Relay send failed (${resp.status}): ${body}`);
|
|
70
|
+
}
|
|
71
|
+
const result = await resp.json();
|
|
72
|
+
return ok({
|
|
73
|
+
sent: true,
|
|
74
|
+
requestId: result.id,
|
|
75
|
+
to: targetKey,
|
|
76
|
+
serviceId,
|
|
77
|
+
paymentIncluded: paymentData && !paymentData.error,
|
|
78
|
+
paymentTxid: paymentData?.txid || null,
|
|
79
|
+
satoshis: paymentData?.satoshis || 0,
|
|
80
|
+
note: 'Poll for service-response to get the result',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service response commands.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Respond to a service request.
|
|
6
|
+
*/
|
|
7
|
+
export declare function cmdRespondService(requestId: string | undefined, recipientKey: string | undefined, serviceId: string | undefined, resultJson: string | undefined): Promise<never>;
|
|
8
|
+
/**
|
|
9
|
+
* Respond to a research request with results.
|
|
10
|
+
*/
|
|
11
|
+
export declare function cmdResearchRespond(resultJsonPath: string | undefined): Promise<never>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service response commands.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { OVERLAY_URL, PATHS } from '../config.js';
|
|
6
|
+
import { ok, fail } from '../output.js';
|
|
7
|
+
import { loadIdentity, signRelayMessage } from '../wallet/identity.js';
|
|
8
|
+
import { updateServiceQueueStatus } from '../utils/storage.js';
|
|
9
|
+
/**
|
|
10
|
+
* Respond to a service request.
|
|
11
|
+
*/
|
|
12
|
+
export async function cmdRespondService(requestId, recipientKey, serviceId, resultJson) {
|
|
13
|
+
if (!requestId || !recipientKey || !serviceId || !resultJson) {
|
|
14
|
+
return fail('Usage: respond-service <requestId> <recipientKey> <serviceId> <resultJson>');
|
|
15
|
+
}
|
|
16
|
+
let result;
|
|
17
|
+
try {
|
|
18
|
+
result = JSON.parse(resultJson);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return fail('resultJson must be valid JSON');
|
|
22
|
+
}
|
|
23
|
+
const { identityKey, privKey } = await loadIdentity();
|
|
24
|
+
// Check if already processed before sending response (idempotency)
|
|
25
|
+
if (fs.existsSync(PATHS.serviceQueue)) {
|
|
26
|
+
const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\n').filter(Boolean);
|
|
27
|
+
const finalStatuses = ['fulfilled', 'rejected', 'delivery_failed', 'failed', 'error'];
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
try {
|
|
30
|
+
const entry = JSON.parse(line);
|
|
31
|
+
if (entry.requestId === requestId && finalStatuses.includes(entry.status)) {
|
|
32
|
+
return ok({
|
|
33
|
+
sent: false,
|
|
34
|
+
requestId,
|
|
35
|
+
serviceId,
|
|
36
|
+
to: recipientKey,
|
|
37
|
+
message: `Request already processed with status: ${entry.status}`,
|
|
38
|
+
alreadyProcessed: true,
|
|
39
|
+
previousStatus: entry.status
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch { }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const responsePayload = {
|
|
47
|
+
requestId,
|
|
48
|
+
serviceId,
|
|
49
|
+
status: 'fulfilled',
|
|
50
|
+
result,
|
|
51
|
+
};
|
|
52
|
+
const sig = await signRelayMessage(privKey, recipientKey, 'service-response', responsePayload);
|
|
53
|
+
const resp = await fetch(`${OVERLAY_URL}/relay/send`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
from: identityKey,
|
|
58
|
+
to: recipientKey,
|
|
59
|
+
type: 'service-response',
|
|
60
|
+
payload: responsePayload,
|
|
61
|
+
signature: sig,
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
if (!resp.ok) {
|
|
65
|
+
// Mark as failed in queue using atomic update
|
|
66
|
+
updateServiceQueueStatus(requestId, 'failed', {
|
|
67
|
+
failedAt: Date.now(),
|
|
68
|
+
error: `Relay send failed: ${resp.status}`
|
|
69
|
+
});
|
|
70
|
+
return fail(`Relay send failed: ${resp.status}`);
|
|
71
|
+
}
|
|
72
|
+
// Mark as fulfilled in queue using atomic update
|
|
73
|
+
updateServiceQueueStatus(requestId, 'fulfilled', {
|
|
74
|
+
fulfilledAt: Date.now()
|
|
75
|
+
});
|
|
76
|
+
return ok({ sent: true, requestId, serviceId, to: recipientKey });
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Respond to a research request with results.
|
|
80
|
+
*/
|
|
81
|
+
export async function cmdResearchRespond(resultJsonPath) {
|
|
82
|
+
if (!resultJsonPath)
|
|
83
|
+
return fail('Usage: research-respond <resultJsonFile>');
|
|
84
|
+
if (!fs.existsSync(resultJsonPath))
|
|
85
|
+
return fail(`File not found: ${resultJsonPath}`);
|
|
86
|
+
const result = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
|
|
87
|
+
const { requestId, from: recipientKey, query, research } = result;
|
|
88
|
+
if (!requestId || !recipientKey || !research) {
|
|
89
|
+
return fail('Result JSON must have: requestId, from, query, research');
|
|
90
|
+
}
|
|
91
|
+
const { identityKey, privKey } = await loadIdentity();
|
|
92
|
+
const responsePayload = {
|
|
93
|
+
requestId,
|
|
94
|
+
serviceId: 'web-research',
|
|
95
|
+
status: 'fulfilled',
|
|
96
|
+
result: research,
|
|
97
|
+
paymentAccepted: true,
|
|
98
|
+
paymentTxid: result.paymentTxid || null,
|
|
99
|
+
satoshisReceived: result.satoshisReceived || 0,
|
|
100
|
+
walletAccepted: result.walletAccepted ?? true,
|
|
101
|
+
};
|
|
102
|
+
const sig = await signRelayMessage(privKey, recipientKey, 'service-response', responsePayload);
|
|
103
|
+
const sendResp = await fetch(`${OVERLAY_URL}/relay/send`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Content-Type': 'application/json' },
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
from: identityKey,
|
|
108
|
+
to: recipientKey,
|
|
109
|
+
type: 'service-response',
|
|
110
|
+
payload: responsePayload,
|
|
111
|
+
signature: sig,
|
|
112
|
+
}),
|
|
113
|
+
});
|
|
114
|
+
if (!sendResp.ok) {
|
|
115
|
+
return fail(`Failed to send response: ${await sendResp.text()}`);
|
|
116
|
+
}
|
|
117
|
+
const sendResult = await sendResp.json();
|
|
118
|
+
// Remove from queue
|
|
119
|
+
if (fs.existsSync(PATHS.researchQueue)) {
|
|
120
|
+
const lines = fs.readFileSync(PATHS.researchQueue, 'utf-8').trim().split('\n').filter(Boolean);
|
|
121
|
+
const remaining = lines.filter(l => {
|
|
122
|
+
try {
|
|
123
|
+
return JSON.parse(l).requestId !== requestId;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
fs.writeFileSync(PATHS.researchQueue, remaining.length ? remaining.join('\n') + '\n' : '');
|
|
130
|
+
}
|
|
131
|
+
return ok({ responded: true, requestId, to: recipientKey, query, pushed: sendResult.pushed });
|
|
132
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared TypeScript interfaces for the overlay CLI.
|
|
3
|
+
*/
|
|
4
|
+
export interface WalletIdentity {
|
|
5
|
+
rootKeyHex: string;
|
|
6
|
+
identityKey: string;
|
|
7
|
+
network: 'mainnet' | 'testnet';
|
|
8
|
+
}
|
|
9
|
+
export interface PaymentResult {
|
|
10
|
+
beef: string;
|
|
11
|
+
txid: string;
|
|
12
|
+
satoshis: number;
|
|
13
|
+
derivationPrefix: string;
|
|
14
|
+
derivationSuffix: string;
|
|
15
|
+
senderIdentityKey: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PaymentParams {
|
|
18
|
+
to: string;
|
|
19
|
+
satoshis: number;
|
|
20
|
+
description?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ServiceAdvertisement {
|
|
23
|
+
serviceId: string;
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
priceSats: number;
|
|
27
|
+
txid?: string;
|
|
28
|
+
registeredAt?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface Message {
|
|
31
|
+
id: string;
|
|
32
|
+
from: string;
|
|
33
|
+
to: string;
|
|
34
|
+
type: string;
|
|
35
|
+
payload: unknown;
|
|
36
|
+
signature?: string;
|
|
37
|
+
timestamp?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface CommandResult<T = unknown> {
|
|
40
|
+
success: boolean;
|
|
41
|
+
data?: T;
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface Registration {
|
|
45
|
+
identityKey: string;
|
|
46
|
+
agentName: string;
|
|
47
|
+
agentDescription: string;
|
|
48
|
+
overlayUrl: string;
|
|
49
|
+
identityTxid: string;
|
|
50
|
+
serviceTxid: string | null;
|
|
51
|
+
funded: string;
|
|
52
|
+
registeredAt: string;
|
|
53
|
+
}
|
|
54
|
+
export interface OverlayPayload {
|
|
55
|
+
protocol: string;
|
|
56
|
+
type: string;
|
|
57
|
+
identityKey?: string;
|
|
58
|
+
[key: string]: unknown;
|
|
59
|
+
}
|
|
60
|
+
export interface VerifyAndAcceptResult {
|
|
61
|
+
accepted: boolean;
|
|
62
|
+
txid: string | null;
|
|
63
|
+
satoshis: number;
|
|
64
|
+
outputIndex: number;
|
|
65
|
+
walletAccepted: boolean;
|
|
66
|
+
error: string | null;
|
|
67
|
+
}
|
|
68
|
+
export interface ProcessMessageResult {
|
|
69
|
+
id: string;
|
|
70
|
+
type: string;
|
|
71
|
+
action: string;
|
|
72
|
+
from: string;
|
|
73
|
+
ack: boolean;
|
|
74
|
+
[key: string]: unknown;
|
|
75
|
+
}
|
|
76
|
+
export interface RelayMessage {
|
|
77
|
+
id: string;
|
|
78
|
+
from: string;
|
|
79
|
+
to: string;
|
|
80
|
+
type: string;
|
|
81
|
+
payload: Record<string, unknown>;
|
|
82
|
+
signature?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface XVerification {
|
|
85
|
+
identityKey: string;
|
|
86
|
+
xHandle: string;
|
|
87
|
+
xUserId: string;
|
|
88
|
+
tweetId: string;
|
|
89
|
+
tweetUrl: string;
|
|
90
|
+
signature: string;
|
|
91
|
+
verifiedAt: string;
|
|
92
|
+
txid?: string | null;
|
|
93
|
+
}
|
|
94
|
+
export interface StoredChange {
|
|
95
|
+
txHex: string;
|
|
96
|
+
txid: string;
|
|
97
|
+
vout: number;
|
|
98
|
+
satoshis: number;
|
|
99
|
+
sourceChain?: SourceChainEntry[];
|
|
100
|
+
savedAt: string;
|
|
101
|
+
}
|
|
102
|
+
export interface SourceChainEntry {
|
|
103
|
+
txHex: string;
|
|
104
|
+
txid: string;
|
|
105
|
+
merklePathHex?: string;
|
|
106
|
+
blockHeight?: number;
|
|
107
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merkle path utilities for SPV proofs.
|
|
3
|
+
*/
|
|
4
|
+
import type { MerklePath as MerklePathType } from '@bsv/sdk';
|
|
5
|
+
/**
|
|
6
|
+
* Build a MerklePath from TSC (Transaction Status Check) proof data.
|
|
7
|
+
* @param txid - Transaction ID
|
|
8
|
+
* @param txIndex - Transaction's index in the block
|
|
9
|
+
* @param nodes - Array of sibling hashes (or '*' for duplicate)
|
|
10
|
+
* @param blockHeight - Block height
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildMerklePathFromTSC(txid: string, txIndex: number, nodes: string[], blockHeight: number): Promise<MerklePathType>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merkle path utilities for SPV proofs.
|
|
3
|
+
*/
|
|
4
|
+
// We'll import MerklePath dynamically to avoid issues with ESM resolution
|
|
5
|
+
let _MerklePath = null;
|
|
6
|
+
async function getMerklePath() {
|
|
7
|
+
if (_MerklePath)
|
|
8
|
+
return _MerklePath;
|
|
9
|
+
const sdk = await import('@bsv/sdk');
|
|
10
|
+
_MerklePath = sdk.MerklePath;
|
|
11
|
+
return _MerklePath;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Build a MerklePath from TSC (Transaction Status Check) proof data.
|
|
15
|
+
* @param txid - Transaction ID
|
|
16
|
+
* @param txIndex - Transaction's index in the block
|
|
17
|
+
* @param nodes - Array of sibling hashes (or '*' for duplicate)
|
|
18
|
+
* @param blockHeight - Block height
|
|
19
|
+
*/
|
|
20
|
+
export async function buildMerklePathFromTSC(txid, txIndex, nodes, blockHeight) {
|
|
21
|
+
const MerklePath = await getMerklePath();
|
|
22
|
+
const treeHeight = nodes.length;
|
|
23
|
+
const mpPath = [];
|
|
24
|
+
// Level 0
|
|
25
|
+
const level0 = [
|
|
26
|
+
{ offset: txIndex, hash: txid, txid: true }
|
|
27
|
+
];
|
|
28
|
+
if (nodes[0] === '*') {
|
|
29
|
+
level0.push({ offset: txIndex ^ 1, duplicate: true });
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
level0.push({ offset: txIndex ^ 1, hash: nodes[0] });
|
|
33
|
+
}
|
|
34
|
+
level0.sort((a, b) => a.offset - b.offset);
|
|
35
|
+
mpPath.push(level0);
|
|
36
|
+
// Higher levels
|
|
37
|
+
for (let i = 1; i < treeHeight; i++) {
|
|
38
|
+
const siblingOffset = (txIndex >> i) ^ 1;
|
|
39
|
+
if (nodes[i] === '*') {
|
|
40
|
+
mpPath.push([{ offset: siblingOffset, duplicate: true }]);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
mpPath.push([{ offset: siblingOffset, hash: nodes[i] }]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return new MerklePath(blockHeight, mpPath);
|
|
47
|
+
}
|