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.
Files changed (221) hide show
  1. package/README.md +406 -0
  2. package/SKILL.md +78 -0
  3. package/clawdbot.plugin.json +106 -0
  4. package/dist/cli-main.d.ts +7 -0
  5. package/dist/cli-main.js +192 -0
  6. package/dist/cli.d.ts +8 -0
  7. package/dist/cli.js +14 -0
  8. package/dist/core/config.d.ts +11 -0
  9. package/dist/core/config.js +13 -0
  10. package/dist/core/index.d.ts +25 -0
  11. package/dist/core/index.js +26 -0
  12. package/dist/core/payment.d.ts +16 -0
  13. package/dist/core/payment.js +94 -0
  14. package/dist/core/types.d.ts +94 -0
  15. package/dist/core/types.js +4 -0
  16. package/dist/core/verify.d.ts +28 -0
  17. package/dist/core/verify.js +104 -0
  18. package/dist/core/wallet.d.ts +99 -0
  19. package/dist/core/wallet.js +219 -0
  20. package/dist/scripts/baemail/commands.d.ts +64 -0
  21. package/dist/scripts/baemail/commands.js +258 -0
  22. package/dist/scripts/baemail/handler.d.ts +36 -0
  23. package/dist/scripts/baemail/handler.js +284 -0
  24. package/dist/scripts/baemail/index.d.ts +5 -0
  25. package/dist/scripts/baemail/index.js +5 -0
  26. package/dist/scripts/config.d.ts +48 -0
  27. package/dist/scripts/config.js +68 -0
  28. package/dist/scripts/index.d.ts +7 -0
  29. package/dist/scripts/index.js +7 -0
  30. package/dist/scripts/messaging/connect.d.ts +8 -0
  31. package/dist/scripts/messaging/connect.js +114 -0
  32. package/dist/scripts/messaging/handlers.d.ts +21 -0
  33. package/dist/scripts/messaging/handlers.js +334 -0
  34. package/dist/scripts/messaging/inbox.d.ts +11 -0
  35. package/dist/scripts/messaging/inbox.js +51 -0
  36. package/dist/scripts/messaging/index.d.ts +8 -0
  37. package/dist/scripts/messaging/index.js +8 -0
  38. package/dist/scripts/messaging/poll.d.ts +7 -0
  39. package/dist/scripts/messaging/poll.js +52 -0
  40. package/dist/scripts/messaging/send.d.ts +7 -0
  41. package/dist/scripts/messaging/send.js +43 -0
  42. package/dist/scripts/output.d.ts +12 -0
  43. package/dist/scripts/output.js +19 -0
  44. package/dist/scripts/overlay/discover.d.ts +7 -0
  45. package/dist/scripts/overlay/discover.js +72 -0
  46. package/dist/scripts/overlay/index.d.ts +7 -0
  47. package/dist/scripts/overlay/index.js +7 -0
  48. package/dist/scripts/overlay/registration.d.ts +19 -0
  49. package/dist/scripts/overlay/registration.js +176 -0
  50. package/dist/scripts/overlay/services.d.ts +29 -0
  51. package/dist/scripts/overlay/services.js +167 -0
  52. package/dist/scripts/overlay/transaction.d.ts +42 -0
  53. package/dist/scripts/overlay/transaction.js +103 -0
  54. package/dist/scripts/payment/build.d.ts +24 -0
  55. package/dist/scripts/payment/build.js +54 -0
  56. package/dist/scripts/payment/commands.d.ts +15 -0
  57. package/dist/scripts/payment/commands.js +73 -0
  58. package/dist/scripts/payment/index.d.ts +6 -0
  59. package/dist/scripts/payment/index.js +6 -0
  60. package/dist/scripts/payment/types.d.ts +56 -0
  61. package/dist/scripts/payment/types.js +4 -0
  62. package/dist/scripts/services/index.d.ts +6 -0
  63. package/dist/scripts/services/index.js +6 -0
  64. package/dist/scripts/services/queue.d.ts +11 -0
  65. package/dist/scripts/services/queue.js +28 -0
  66. package/dist/scripts/services/request.d.ts +7 -0
  67. package/dist/scripts/services/request.js +82 -0
  68. package/dist/scripts/services/respond.d.ts +11 -0
  69. package/dist/scripts/services/respond.js +132 -0
  70. package/dist/scripts/types.d.ts +107 -0
  71. package/dist/scripts/types.js +4 -0
  72. package/dist/scripts/utils/index.d.ts +6 -0
  73. package/dist/scripts/utils/index.js +6 -0
  74. package/dist/scripts/utils/merkle.d.ts +12 -0
  75. package/dist/scripts/utils/merkle.js +47 -0
  76. package/dist/scripts/utils/storage.d.ts +66 -0
  77. package/dist/scripts/utils/storage.js +211 -0
  78. package/dist/scripts/utils/woc.d.ts +26 -0
  79. package/dist/scripts/utils/woc.js +91 -0
  80. package/dist/scripts/wallet/balance.d.ts +22 -0
  81. package/dist/scripts/wallet/balance.js +240 -0
  82. package/dist/scripts/wallet/identity.d.ts +70 -0
  83. package/dist/scripts/wallet/identity.js +151 -0
  84. package/dist/scripts/wallet/index.d.ts +6 -0
  85. package/dist/scripts/wallet/index.js +6 -0
  86. package/dist/scripts/wallet/setup.d.ts +15 -0
  87. package/dist/scripts/wallet/setup.js +105 -0
  88. package/dist/scripts/x-verification/commands.d.ts +27 -0
  89. package/dist/scripts/x-verification/commands.js +222 -0
  90. package/dist/scripts/x-verification/index.d.ts +4 -0
  91. package/dist/scripts/x-verification/index.js +4 -0
  92. package/dist/services/built-in/api-proxy/index.d.ts +6 -0
  93. package/dist/services/built-in/api-proxy/index.js +23 -0
  94. package/dist/services/built-in/code-develop/index.d.ts +6 -0
  95. package/dist/services/built-in/code-develop/index.js +23 -0
  96. package/dist/services/built-in/code-review/index.d.ts +10 -0
  97. package/dist/services/built-in/code-review/index.js +51 -0
  98. package/dist/services/built-in/image-analysis/index.d.ts +6 -0
  99. package/dist/services/built-in/image-analysis/index.js +33 -0
  100. package/dist/services/built-in/memory-store/index.d.ts +6 -0
  101. package/dist/services/built-in/memory-store/index.js +22 -0
  102. package/dist/services/built-in/roulette/index.d.ts +6 -0
  103. package/dist/services/built-in/roulette/index.js +27 -0
  104. package/dist/services/built-in/summarize/index.d.ts +6 -0
  105. package/dist/services/built-in/summarize/index.js +21 -0
  106. package/dist/services/built-in/tell-joke/handler.d.ts +7 -0
  107. package/dist/services/built-in/tell-joke/handler.js +122 -0
  108. package/dist/services/built-in/tell-joke/index.d.ts +9 -0
  109. package/dist/services/built-in/tell-joke/index.js +31 -0
  110. package/dist/services/built-in/translate/index.d.ts +6 -0
  111. package/dist/services/built-in/translate/index.js +21 -0
  112. package/dist/services/built-in/web-research/index.d.ts +9 -0
  113. package/dist/services/built-in/web-research/index.js +51 -0
  114. package/dist/services/index.d.ts +13 -0
  115. package/dist/services/index.js +14 -0
  116. package/dist/services/loader.d.ts +77 -0
  117. package/dist/services/loader.js +292 -0
  118. package/dist/services/manager.d.ts +86 -0
  119. package/dist/services/manager.js +255 -0
  120. package/dist/services/registry.d.ts +98 -0
  121. package/dist/services/registry.js +204 -0
  122. package/dist/services/types.d.ts +230 -0
  123. package/dist/services/types.js +30 -0
  124. package/dist/test/cli.test.d.ts +7 -0
  125. package/dist/test/cli.test.js +329 -0
  126. package/dist/test/comprehensive-overlay.test.d.ts +13 -0
  127. package/dist/test/comprehensive-overlay.test.js +593 -0
  128. package/dist/test/key-derivation.test.d.ts +12 -0
  129. package/dist/test/key-derivation.test.js +86 -0
  130. package/dist/test/overlay-submit.test.d.ts +10 -0
  131. package/dist/test/overlay-submit.test.js +460 -0
  132. package/dist/test/request-response-flow.test.d.ts +5 -0
  133. package/dist/test/request-response-flow.test.js +209 -0
  134. package/dist/test/service-system.test.d.ts +5 -0
  135. package/dist/test/service-system.test.js +190 -0
  136. package/dist/test/utils/server-logic.d.ts +98 -0
  137. package/dist/test/utils/server-logic.js +286 -0
  138. package/dist/test/wallet.test.d.ts +7 -0
  139. package/dist/test/wallet.test.js +146 -0
  140. package/index.ts +1965 -0
  141. package/openclaw.plugin.json +106 -0
  142. package/package.json +73 -0
  143. package/src/cli-main.ts +230 -0
  144. package/src/cli.ts +16 -0
  145. package/src/core/README.md +246 -0
  146. package/src/core/config.ts +21 -0
  147. package/src/core/index.ts +42 -0
  148. package/src/core/payment.ts +111 -0
  149. package/src/core/types.ts +102 -0
  150. package/src/core/verify.ts +119 -0
  151. package/src/core/wallet.ts +282 -0
  152. package/src/scripts/baemail/commands.ts +326 -0
  153. package/src/scripts/baemail/handler.ts +338 -0
  154. package/src/scripts/baemail/index.ts +6 -0
  155. package/src/scripts/config.ts +81 -0
  156. package/src/scripts/index.ts +8 -0
  157. package/src/scripts/messaging/connect.ts +121 -0
  158. package/src/scripts/messaging/handlers.ts +394 -0
  159. package/src/scripts/messaging/inbox.ts +64 -0
  160. package/src/scripts/messaging/index.ts +9 -0
  161. package/src/scripts/messaging/poll.ts +59 -0
  162. package/src/scripts/messaging/send.ts +54 -0
  163. package/src/scripts/output.ts +21 -0
  164. package/src/scripts/overlay/discover.ts +81 -0
  165. package/src/scripts/overlay/index.ts +8 -0
  166. package/src/scripts/overlay/registration.ts +199 -0
  167. package/src/scripts/overlay/services.ts +199 -0
  168. package/src/scripts/overlay/transaction.ts +124 -0
  169. package/src/scripts/payment/build.ts +65 -0
  170. package/src/scripts/payment/commands.ts +92 -0
  171. package/src/scripts/payment/index.ts +7 -0
  172. package/src/scripts/payment/types.ts +62 -0
  173. package/src/scripts/services/index.ts +7 -0
  174. package/src/scripts/services/queue.ts +35 -0
  175. package/src/scripts/services/request.ts +98 -0
  176. package/src/scripts/services/respond.ts +149 -0
  177. package/src/scripts/types.ts +121 -0
  178. package/src/scripts/utils/index.ts +7 -0
  179. package/src/scripts/utils/merkle.ts +57 -0
  180. package/src/scripts/utils/storage.ts +231 -0
  181. package/src/scripts/utils/woc.ts +106 -0
  182. package/src/scripts/wallet/balance.ts +277 -0
  183. package/src/scripts/wallet/identity.ts +203 -0
  184. package/src/scripts/wallet/index.ts +7 -0
  185. package/src/scripts/wallet/setup.ts +121 -0
  186. package/src/scripts/x-verification/commands.ts +256 -0
  187. package/src/scripts/x-verification/index.ts +5 -0
  188. package/src/services/built-in/api-proxy/index.ts +26 -0
  189. package/src/services/built-in/api-proxy/prompt.md +26 -0
  190. package/src/services/built-in/code-develop/index.ts +26 -0
  191. package/src/services/built-in/code-develop/prompt.md +35 -0
  192. package/src/services/built-in/code-review/index.ts +54 -0
  193. package/src/services/built-in/code-review/prompt.md +105 -0
  194. package/src/services/built-in/image-analysis/index.ts +36 -0
  195. package/src/services/built-in/image-analysis/prompt.md +42 -0
  196. package/src/services/built-in/memory-store/index.ts +25 -0
  197. package/src/services/built-in/memory-store/prompt.md +45 -0
  198. package/src/services/built-in/roulette/index.ts +30 -0
  199. package/src/services/built-in/roulette/prompt.md +35 -0
  200. package/src/services/built-in/summarize/index.ts +24 -0
  201. package/src/services/built-in/summarize/prompt.md +27 -0
  202. package/src/services/built-in/tell-joke/handler.ts +134 -0
  203. package/src/services/built-in/tell-joke/index.ts +34 -0
  204. package/src/services/built-in/tell-joke/prompt.md +59 -0
  205. package/src/services/built-in/translate/index.ts +24 -0
  206. package/src/services/built-in/translate/prompt.md +23 -0
  207. package/src/services/built-in/web-research/index.ts +54 -0
  208. package/src/services/built-in/web-research/prompt.md +110 -0
  209. package/src/services/index.ts +16 -0
  210. package/src/services/loader.ts +344 -0
  211. package/src/services/manager.ts +304 -0
  212. package/src/services/registry.ts +246 -0
  213. package/src/services/types.ts +259 -0
  214. package/src/test/cli.test.ts +352 -0
  215. package/src/test/comprehensive-overlay.test.ts +729 -0
  216. package/src/test/key-derivation.test.ts +102 -0
  217. package/src/test/overlay-submit.test.ts +570 -0
  218. package/src/test/request-response-flow.test.ts +252 -0
  219. package/src/test/service-system.test.ts +241 -0
  220. package/src/test/utils/server-logic.ts +368 -0
  221. package/src/test/wallet.test.ts +166 -0
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Message type handlers and processMessage function.
3
+ */
4
+ import fs from 'node:fs';
5
+ import { OVERLAY_URL, WALLET_DIR, PATHS } from '../config.js';
6
+ import { signRelayMessage, verifyRelaySignature, loadWalletIdentity } from '../wallet/identity.js';
7
+ import { loadServices, appendToJsonl } from '../utils/storage.js';
8
+ import { fetchWithTimeout } from '../utils/woc.js';
9
+ import { serviceManager } from '../../services/index.js';
10
+ // Dynamic import for @bsv/sdk (needed for hash160 computation)
11
+ let _sdk = null;
12
+ async function getSdk() {
13
+ if (_sdk)
14
+ return _sdk;
15
+ try {
16
+ _sdk = await import('@bsv/sdk');
17
+ return _sdk;
18
+ }
19
+ catch {
20
+ const { fileURLToPath } = await import('node:url');
21
+ const path = await import('node:path');
22
+ const os = await import('node:os');
23
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
+ const candidates = [
25
+ path.resolve(__dirname, '..', '..', '..', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),
26
+ path.resolve(__dirname, '..', '..', '..', '..', '..', 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),
27
+ path.resolve(os.homedir(), 'a2a-bsv', 'packages', 'core', 'node_modules', '@bsv', 'sdk', 'dist', 'esm', 'mod.js'),
28
+ ];
29
+ for (const p of candidates) {
30
+ try {
31
+ _sdk = await import(p);
32
+ return _sdk;
33
+ }
34
+ catch {
35
+ // Try next
36
+ }
37
+ }
38
+ throw new Error('Cannot find @bsv/sdk. Run setup.sh first.');
39
+ }
40
+ }
41
+ import { BSVAgentWallet } from '../../core/index.js';
42
+ async function getBSVAgentWallet() {
43
+ return BSVAgentWallet;
44
+ }
45
+ // Import NETWORK lazily to avoid circular dependencies
46
+ async function getNetwork() {
47
+ const config = await import('../config.js');
48
+ return config.NETWORK;
49
+ }
50
+ /**
51
+ * Verify and accept a payment from a service request.
52
+ * Uses a2a-bsv wallet.acceptPayment() for proper BRC-29 handling.
53
+ */
54
+ export async function verifyAndAcceptPayment(payment, minSats, senderKey, serviceId, ourHash160) {
55
+ if (!payment) {
56
+ return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: 'no payment' };
57
+ }
58
+ if (payment.error) {
59
+ return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: payment.error };
60
+ }
61
+ if (!payment.beef || !payment.satoshis) {
62
+ return { accepted: false, txid: null, satoshis: 0, outputIndex: 0, walletAccepted: false, error: 'missing beef or satoshis' };
63
+ }
64
+ if (payment.satoshis < minSats) {
65
+ return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: `insufficient payment: ${payment.satoshis} < ${minSats}` };
66
+ }
67
+ // Accept the payment using a2a-bsv wallet
68
+ const BSVAgentWallet = await getBSVAgentWallet();
69
+ const network = await getNetwork();
70
+ const wallet = await BSVAgentWallet.load({ network, storageDir: WALLET_DIR });
71
+ try {
72
+ // First verify the payment structure
73
+ const verifyResult = await wallet.verifyPayment({ beef: payment.beef });
74
+ if (!verifyResult.valid) {
75
+ await wallet.destroy();
76
+ return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: `verification failed: ${verifyResult.errors.join(', ')}` };
77
+ }
78
+ // Accept the payment (this broadcasts the transaction)
79
+ const acceptResult = await wallet.acceptPayment({
80
+ beef: payment.beef,
81
+ derivationPrefix: payment.derivationPrefix,
82
+ derivationSuffix: payment.derivationSuffix,
83
+ senderIdentityKey: payment.senderIdentityKey,
84
+ description: `Payment for ${serviceId}`,
85
+ });
86
+ await wallet.destroy();
87
+ if (!acceptResult.accepted) {
88
+ return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: 'wallet rejected payment' };
89
+ }
90
+ return {
91
+ accepted: true,
92
+ txid: payment.txid,
93
+ satoshis: payment.satoshis,
94
+ outputIndex: 0,
95
+ walletAccepted: true,
96
+ error: null,
97
+ };
98
+ }
99
+ catch (err) {
100
+ await wallet.destroy();
101
+ return { accepted: false, txid: payment.txid || null, satoshis: payment.satoshis, outputIndex: 0, walletAccepted: false, error: err.message };
102
+ }
103
+ }
104
+ /**
105
+ * Queue a service request for agent processing.
106
+ */
107
+ async function queueForAgent(msg, identityKey, privKey, serviceId) {
108
+ // Check if this request has already been processed to prevent duplicates
109
+ if (fs.existsSync(PATHS.serviceQueue)) {
110
+ const lines = fs.readFileSync(PATHS.serviceQueue, 'utf-8').trim().split('\n').filter(Boolean);
111
+ for (const line of lines) {
112
+ try {
113
+ const entry = JSON.parse(line);
114
+ if (entry.requestId === msg.id) {
115
+ // Request already exists in queue - return existing status
116
+ return {
117
+ id: msg.id,
118
+ type: 'service-request',
119
+ serviceId,
120
+ action: entry.status === 'pending' ? 'already-queued' : `already-${entry.status}`,
121
+ paymentAccepted: true,
122
+ paymentTxid: entry.paymentTxid,
123
+ satoshisReceived: entry.satoshisReceived,
124
+ from: msg.from,
125
+ ack: true,
126
+ };
127
+ }
128
+ }
129
+ catch { }
130
+ }
131
+ }
132
+ const sdk = await getSdk();
133
+ const payment = msg.payload?.payment;
134
+ const input = msg.payload?.input || msg.payload;
135
+ // Verify and accept payment
136
+ const walletIdentity = loadWalletIdentity();
137
+ const ourHash160 = sdk.Hash.hash160(sdk.PrivateKey.fromHex(walletIdentity.rootKeyHex).toPublicKey().encode(true));
138
+ // Find the service price using the service registry
139
+ const serviceDefinition = serviceManager.registry.get(serviceId);
140
+ let minPrice = 5; // default fallback
141
+ if (serviceDefinition) {
142
+ minPrice = serviceDefinition.defaultPrice;
143
+ // Validate service input if possible
144
+ const validation = serviceManager.validate(serviceId, input);
145
+ if (!validation.valid) {
146
+ // Send validation rejection
147
+ const rejectPayload = {
148
+ requestId: msg.id,
149
+ serviceId,
150
+ status: 'rejected',
151
+ reason: `Input validation failed: ${validation.error}`
152
+ };
153
+ const sig = await signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
154
+ await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
155
+ method: 'POST',
156
+ headers: { 'Content-Type': 'application/json' },
157
+ body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
158
+ });
159
+ // Also add the rejected entry to the queue for tracking
160
+ const rejectedEntry = {
161
+ status: 'rejected',
162
+ requestId: msg.id,
163
+ serviceId,
164
+ from: msg.from,
165
+ identityKey,
166
+ input: input,
167
+ paymentTxid: null,
168
+ satoshisReceived: 0,
169
+ walletAccepted: false,
170
+ error: validation.error,
171
+ _ts: Date.now(),
172
+ };
173
+ appendToJsonl(PATHS.serviceQueue, rejectedEntry);
174
+ return {
175
+ id: msg.id,
176
+ type: 'service-request',
177
+ serviceId,
178
+ action: 'rejected',
179
+ reason: validation.error || 'input validation failed',
180
+ from: msg.from,
181
+ ack: true
182
+ };
183
+ }
184
+ }
185
+ else {
186
+ // Fall back to legacy service loading for backward compatibility
187
+ const services = loadServices();
188
+ const svc = services.find(s => s.serviceId === serviceId);
189
+ minPrice = svc?.priceSats || 5;
190
+ }
191
+ const payResult = await verifyAndAcceptPayment(payment, minPrice, msg.from, serviceId, ourHash160);
192
+ if (!payResult.accepted) {
193
+ // Send rejection
194
+ const rejectPayload = { requestId: msg.id, serviceId, status: 'rejected', reason: `Payment rejected: ${payResult.error}` };
195
+ const sig = await signRelayMessage(privKey, msg.from, 'service-response', rejectPayload);
196
+ await fetchWithTimeout(`${OVERLAY_URL}/relay/send`, {
197
+ method: 'POST',
198
+ headers: { 'Content-Type': 'application/json' },
199
+ body: JSON.stringify({ from: identityKey, to: msg.from, type: 'service-response', payload: rejectPayload, signature: sig }),
200
+ });
201
+ // Also add the rejected entry to the queue for tracking
202
+ const rejectedEntry = {
203
+ status: 'rejected',
204
+ requestId: msg.id,
205
+ serviceId,
206
+ from: msg.from,
207
+ identityKey,
208
+ input: input,
209
+ paymentTxid: null,
210
+ satoshisReceived: 0,
211
+ walletAccepted: false,
212
+ error: payResult.error,
213
+ _ts: Date.now(),
214
+ };
215
+ appendToJsonl(PATHS.serviceQueue, rejectedEntry);
216
+ return { id: msg.id, type: 'service-request', serviceId, action: 'rejected', reason: payResult.error || 'payment rejected', from: msg.from, ack: true };
217
+ }
218
+ // Queue for agent processing
219
+ const queueEntry = {
220
+ status: 'pending',
221
+ requestId: msg.id,
222
+ serviceId,
223
+ from: msg.from,
224
+ identityKey,
225
+ input: input,
226
+ paymentTxid: payResult.txid,
227
+ satoshisReceived: payResult.satoshis,
228
+ walletAccepted: payResult.walletAccepted,
229
+ _ts: Date.now(),
230
+ };
231
+ appendToJsonl(PATHS.serviceQueue, queueEntry);
232
+ return {
233
+ id: msg.id,
234
+ type: 'service-request',
235
+ serviceId,
236
+ action: 'queued-for-agent',
237
+ paymentAccepted: true,
238
+ paymentTxid: payResult.txid,
239
+ satoshisReceived: payResult.satoshis,
240
+ from: msg.from,
241
+ ack: true,
242
+ };
243
+ }
244
+ /**
245
+ * Process a single relay message.
246
+ * Handles pings, service requests, pongs, and service responses.
247
+ */
248
+ export async function processMessage(msg, identityKey, privKey) {
249
+ // Verify signature if present
250
+ const sigCheck = msg.signature
251
+ ? await verifyRelaySignature(msg.from, msg.to, msg.type, msg.payload, msg.signature)
252
+ : { valid: null };
253
+ // Reject unsigned/forged service-requests
254
+ if (msg.type === 'service-request' && sigCheck.valid !== true) {
255
+ console.error(JSON.stringify({ event: 'signature-rejected', type: msg.type, from: msg.from, reason: sigCheck.reason || 'missing signature' }));
256
+ return {
257
+ id: msg.id,
258
+ type: msg.type,
259
+ from: msg.from,
260
+ action: 'rejected',
261
+ reason: 'invalid-signature',
262
+ signatureValid: sigCheck.valid,
263
+ ack: true,
264
+ };
265
+ }
266
+ if (msg.type === 'ping') {
267
+ // Auto-respond with pong
268
+ const pongPayload = {
269
+ text: 'pong',
270
+ inReplyTo: msg.id,
271
+ originalText: msg.payload?.text || null,
272
+ };
273
+ const pongSig = await signRelayMessage(privKey, msg.from, 'pong', pongPayload);
274
+ await fetch(`${OVERLAY_URL}/relay/send`, {
275
+ method: 'POST',
276
+ headers: { 'Content-Type': 'application/json' },
277
+ body: JSON.stringify({
278
+ from: identityKey,
279
+ to: msg.from,
280
+ type: 'pong',
281
+ payload: pongPayload,
282
+ signature: pongSig,
283
+ }),
284
+ });
285
+ return { id: msg.id, type: 'ping', action: 'replied-pong', from: msg.from, ack: true };
286
+ }
287
+ if (msg.type === 'service-request') {
288
+ const serviceId = msg.payload?.serviceId;
289
+ // Agent-routed mode: queue for the agent
290
+ if (process.env.AGENT_ROUTED === 'true') {
291
+ return await queueForAgent(msg, identityKey, privKey, serviceId);
292
+ }
293
+ // No hardcoded handlers in TypeScript version — always queue
294
+ return await queueForAgent(msg, identityKey, privKey, serviceId);
295
+ }
296
+ if (msg.type === 'pong') {
297
+ return {
298
+ id: msg.id,
299
+ type: 'pong',
300
+ action: 'received',
301
+ from: msg.from,
302
+ text: msg.payload?.text,
303
+ inReplyTo: msg.payload?.inReplyTo,
304
+ ack: true,
305
+ };
306
+ }
307
+ if (msg.type === 'service-response') {
308
+ const serviceId = msg.payload?.serviceId;
309
+ const status = msg.payload?.status;
310
+ const result = msg.payload?.result;
311
+ return {
312
+ id: msg.id,
313
+ type: 'service-response',
314
+ action: 'received',
315
+ from: msg.from,
316
+ serviceId,
317
+ status,
318
+ result,
319
+ requestId: msg.payload?.requestId,
320
+ direction: 'incoming-response',
321
+ ack: true,
322
+ };
323
+ }
324
+ // Unknown type
325
+ return {
326
+ id: msg.id,
327
+ type: msg.type,
328
+ from: msg.from,
329
+ payload: msg.payload,
330
+ signatureValid: sigCheck.valid,
331
+ action: 'unhandled',
332
+ ack: false,
333
+ };
334
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Inbox and ack commands.
3
+ */
4
+ /**
5
+ * Inbox command: fetch pending messages.
6
+ */
7
+ export declare function cmdInbox(args: string[]): Promise<never>;
8
+ /**
9
+ * Ack command: acknowledge processed messages.
10
+ */
11
+ export declare function cmdAck(messageIds: string[]): Promise<never>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Inbox and ack commands.
3
+ */
4
+ import { OVERLAY_URL } from '../config.js';
5
+ import { ok, fail } from '../output.js';
6
+ import { loadIdentity, verifyRelaySignature } from '../wallet/identity.js';
7
+ /**
8
+ * Inbox command: fetch pending messages.
9
+ */
10
+ export async function cmdInbox(args) {
11
+ const { identityKey } = await loadIdentity();
12
+ let since = '';
13
+ for (let i = 0; i < args.length; i++) {
14
+ if (args[i] === '--since' && args[i + 1])
15
+ since = `&since=${args[++i]}`;
16
+ }
17
+ const resp = await fetch(`${OVERLAY_URL}/relay/inbox?identity=${identityKey}${since}`);
18
+ if (!resp.ok) {
19
+ const body = await resp.text();
20
+ return fail(`Relay inbox failed (${resp.status}): ${body}`);
21
+ }
22
+ const result = await resp.json();
23
+ // Verify signatures on received messages
24
+ const messages = await Promise.all(result.messages.map(async (msg) => ({
25
+ ...msg,
26
+ signatureValid: msg.signature
27
+ ? (await verifyRelaySignature(msg.from, msg.to, msg.type, msg.payload, msg.signature)).valid
28
+ : null,
29
+ })));
30
+ return ok({ messages, count: messages.length, identityKey });
31
+ }
32
+ /**
33
+ * Ack command: acknowledge processed messages.
34
+ */
35
+ export async function cmdAck(messageIds) {
36
+ if (!messageIds || messageIds.length === 0) {
37
+ return fail('Usage: ack <messageId> [messageId2 ...]');
38
+ }
39
+ const { identityKey } = await loadIdentity();
40
+ const resp = await fetch(`${OVERLAY_URL}/relay/ack`, {
41
+ method: 'POST',
42
+ headers: { 'Content-Type': 'application/json' },
43
+ body: JSON.stringify({ identity: identityKey, messageIds }),
44
+ });
45
+ if (!resp.ok) {
46
+ const body = await resp.text();
47
+ return fail(`Relay ack failed (${resp.status}): ${body}`);
48
+ }
49
+ const result = await resp.json();
50
+ return ok({ acked: result.acked, messageIds });
51
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Messaging module exports.
3
+ */
4
+ export * from './send.js';
5
+ export * from './inbox.js';
6
+ export * from './handlers.js';
7
+ export * from './poll.js';
8
+ export * from './connect.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Messaging module exports.
3
+ */
4
+ export * from './send.js';
5
+ export * from './inbox.js';
6
+ export * from './handlers.js';
7
+ export * from './poll.js';
8
+ export * from './connect.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Poll command: fetch and process all pending messages.
3
+ */
4
+ /**
5
+ * Poll command: fetch all pending messages and process them.
6
+ */
7
+ export declare function cmdPoll(): Promise<never>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Poll command: fetch and process all pending messages.
3
+ */
4
+ import { OVERLAY_URL } from '../config.js';
5
+ import { ok, fail } from '../output.js';
6
+ import { loadIdentity } from '../wallet/identity.js';
7
+ import { processMessage } from './handlers.js';
8
+ /**
9
+ * Poll command: fetch all pending messages and process them.
10
+ */
11
+ export async function cmdPoll() {
12
+ const { identityKey, privKey } = await loadIdentity();
13
+ // Fetch inbox
14
+ const inboxResp = await fetch(`${OVERLAY_URL}/relay/inbox?identity=${identityKey}`);
15
+ if (!inboxResp.ok) {
16
+ const body = await inboxResp.text();
17
+ return fail(`Relay inbox failed (${inboxResp.status}): ${body}`);
18
+ }
19
+ const inbox = await inboxResp.json();
20
+ if (inbox.count === 0) {
21
+ return ok({ processed: 0, messages: [], summary: 'No pending messages.' });
22
+ }
23
+ const processed = [];
24
+ const ackedIds = [];
25
+ const unhandled = [];
26
+ for (const msg of inbox.messages) {
27
+ const result = await processMessage(msg, identityKey, privKey);
28
+ if (result.ack) {
29
+ ackedIds.push(result.id);
30
+ processed.push(result);
31
+ }
32
+ else {
33
+ unhandled.push(result);
34
+ }
35
+ }
36
+ // ACK processed messages
37
+ if (ackedIds.length > 0) {
38
+ await fetch(`${OVERLAY_URL}/relay/ack`, {
39
+ method: 'POST',
40
+ headers: { 'Content-Type': 'application/json' },
41
+ body: JSON.stringify({ identity: identityKey, messageIds: ackedIds }),
42
+ });
43
+ }
44
+ return ok({
45
+ processed: processed.length,
46
+ unhandled: unhandled.length,
47
+ total: inbox.count,
48
+ messages: processed,
49
+ unhandledMessages: unhandled,
50
+ ackedIds,
51
+ });
52
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Send relay message command.
3
+ */
4
+ /**
5
+ * Send command: send a typed message to another agent.
6
+ */
7
+ export declare function cmdSend(targetKey: string | undefined, type: string | undefined, payloadStr: string | undefined): Promise<never>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Send relay message 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
+ /**
8
+ * Send command: send a typed message to another agent.
9
+ */
10
+ export async function cmdSend(targetKey, type, payloadStr) {
11
+ if (!targetKey || !type || !payloadStr) {
12
+ return fail('Usage: send <identityKey> <type> <json_payload>');
13
+ }
14
+ if (!/^0[23][0-9a-fA-F]{64}$/.test(targetKey)) {
15
+ return fail('Target must be a compressed public key (66 hex chars, 02/03 prefix)');
16
+ }
17
+ let payload;
18
+ try {
19
+ payload = JSON.parse(payloadStr);
20
+ }
21
+ catch {
22
+ return fail('payload must be valid JSON');
23
+ }
24
+ const { identityKey, privKey } = await loadIdentity();
25
+ const signature = await signRelayMessage(privKey, targetKey, type, payload);
26
+ const resp = await fetch(`${OVERLAY_URL}/relay/send`, {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify({
30
+ from: identityKey,
31
+ to: targetKey,
32
+ type,
33
+ payload,
34
+ signature,
35
+ }),
36
+ });
37
+ if (!resp.ok) {
38
+ const body = await resp.text();
39
+ return fail(`Relay send failed (${resp.status}): ${body}`);
40
+ }
41
+ const result = await resp.json();
42
+ return ok({ sent: true, messageId: result.id, to: targetKey, type, signed: true });
43
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * JSON output helpers for CLI commands.
3
+ * All CLI output follows the { success, data/error } wrapper format.
4
+ */
5
+ /**
6
+ * Output a successful result and exit.
7
+ */
8
+ export declare function ok<T>(data: T): never;
9
+ /**
10
+ * Output an error and exit.
11
+ */
12
+ export declare function fail(error: string | Error): never;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * JSON output helpers for CLI commands.
3
+ * All CLI output follows the { success, data/error } wrapper format.
4
+ */
5
+ /**
6
+ * Output a successful result and exit.
7
+ */
8
+ export function ok(data) {
9
+ console.log(JSON.stringify({ success: true, data }));
10
+ process.exit(0);
11
+ }
12
+ /**
13
+ * Output an error and exit.
14
+ */
15
+ export function fail(error) {
16
+ const message = error instanceof Error ? error.message : String(error);
17
+ console.log(JSON.stringify({ success: false, error: message }));
18
+ process.exit(1);
19
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Overlay discovery commands.
3
+ */
4
+ /**
5
+ * Discover command: query the overlay for agents and services.
6
+ */
7
+ export declare function cmdDiscover(args: string[]): Promise<never>;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Overlay discovery commands.
3
+ */
4
+ import { OVERLAY_URL, LOOKUP_SERVICES } from '../config.js';
5
+ import { ok } from '../output.js';
6
+ import { lookupOverlay, parseOverlayOutput } from './transaction.js';
7
+ /**
8
+ * Discover command: query the overlay for agents and services.
9
+ */
10
+ export async function cmdDiscover(args) {
11
+ // Parse flags
12
+ let serviceFilter = null;
13
+ let agentFilter = null;
14
+ for (let i = 0; i < args.length; i++) {
15
+ if (args[i] === '--service' && args[i + 1])
16
+ serviceFilter = args[++i];
17
+ else if (args[i] === '--agent' && args[i + 1])
18
+ agentFilter = args[++i];
19
+ }
20
+ const results = { agents: [], services: [] };
21
+ // Query agents
22
+ if (!serviceFilter) {
23
+ try {
24
+ const agentQuery = agentFilter ? { name: agentFilter } : { type: 'list' };
25
+ const agentResult = await lookupOverlay(LOOKUP_SERVICES.AGENTS, agentQuery);
26
+ if (agentResult.outputs) {
27
+ for (const output of agentResult.outputs) {
28
+ try {
29
+ const { data, txid } = await parseOverlayOutput(output.beef, output.outputIndex);
30
+ if (data?.type === 'identity') {
31
+ results.agents.push({ ...data, txid });
32
+ }
33
+ }
34
+ catch { /* ignore */ }
35
+ }
36
+ }
37
+ }
38
+ catch (err) {
39
+ results.agentError = String(err);
40
+ }
41
+ }
42
+ // Query services
43
+ if (!agentFilter) {
44
+ try {
45
+ const serviceQuery = serviceFilter ? { serviceType: serviceFilter } : {};
46
+ const serviceResult = await lookupOverlay(LOOKUP_SERVICES.SERVICES, serviceQuery);
47
+ if (serviceResult.outputs) {
48
+ for (const output of serviceResult.outputs) {
49
+ try {
50
+ const { data, txid } = await parseOverlayOutput(output.beef, output.outputIndex);
51
+ if (data?.type === 'service') {
52
+ results.services.push({ ...data, txid });
53
+ }
54
+ }
55
+ catch { /* ignore */ }
56
+ }
57
+ }
58
+ }
59
+ catch (err) {
60
+ results.serviceError = String(err);
61
+ }
62
+ }
63
+ return ok({
64
+ overlayUrl: OVERLAY_URL,
65
+ agentCount: results.agents.length,
66
+ serviceCount: results.services.length,
67
+ agents: results.agents,
68
+ services: results.services,
69
+ ...(results.agentError && { agentError: results.agentError }),
70
+ ...(results.serviceError && { serviceError: results.serviceError }),
71
+ });
72
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Overlay module exports.
3
+ */
4
+ export * from './transaction.js';
5
+ export * from './registration.js';
6
+ export * from './services.js';
7
+ export * from './discover.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Overlay module exports.
3
+ */
4
+ export * from './transaction.js';
5
+ export * from './registration.js';
6
+ export * from './services.js';
7
+ export * from './discover.js';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Overlay registration commands: register, unregister.
3
+ *
4
+ * Registration creates an identity record on the overlay network with:
5
+ * - identityKey: compressed public key (66 hex chars)
6
+ * - name: agent display name
7
+ * - description: what the agent does
8
+ * - channels: contact methods (e.g., { overlay: "https://..." })
9
+ * - capabilities: what the agent can do (e.g., ["services", "jokes"])
10
+ * - timestamp: ISO 8601 registration time
11
+ */
12
+ /**
13
+ * Register command: register this agent on the overlay network.
14
+ */
15
+ export declare function cmdRegister(): Promise<never>;
16
+ /**
17
+ * Unregister command: submit revocation tx to remove agent from overlay network.
18
+ */
19
+ export declare function cmdUnregister(): Promise<never>;