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,59 @@
1
+ /**
2
+ * Poll command: fetch and process all pending messages.
3
+ */
4
+
5
+ import { OVERLAY_URL } from '../config.js';
6
+ import { ok, fail } from '../output.js';
7
+ import { loadIdentity } from '../wallet/identity.js';
8
+ import { processMessage } from './handlers.js';
9
+
10
+ /**
11
+ * Poll command: fetch all pending messages and process them.
12
+ */
13
+ export async function cmdPoll(): Promise<never> {
14
+ const { identityKey, privKey } = await loadIdentity();
15
+
16
+ // Fetch inbox
17
+ const inboxResp = await fetch(`${OVERLAY_URL}/relay/inbox?identity=${identityKey}`);
18
+ if (!inboxResp.ok) {
19
+ const body = await inboxResp.text();
20
+ return fail(`Relay inbox failed (${inboxResp.status}): ${body}`);
21
+ }
22
+ const inbox = await inboxResp.json();
23
+
24
+ if (inbox.count === 0) {
25
+ return ok({ processed: 0, messages: [], summary: 'No pending messages.' });
26
+ }
27
+
28
+ const processed: any[] = [];
29
+ const ackedIds: string[] = [];
30
+ const unhandled: any[] = [];
31
+
32
+ for (const msg of inbox.messages) {
33
+ const result = await processMessage(msg, identityKey, privKey);
34
+ if (result.ack) {
35
+ ackedIds.push(result.id);
36
+ processed.push(result);
37
+ } else {
38
+ unhandled.push(result);
39
+ }
40
+ }
41
+
42
+ // ACK processed messages
43
+ if (ackedIds.length > 0) {
44
+ await fetch(`${OVERLAY_URL}/relay/ack`, {
45
+ method: 'POST',
46
+ headers: { 'Content-Type': 'application/json' },
47
+ body: JSON.stringify({ identity: identityKey, messageIds: ackedIds }),
48
+ });
49
+ }
50
+
51
+ return ok({
52
+ processed: processed.length,
53
+ unhandled: unhandled.length,
54
+ total: inbox.count,
55
+ messages: processed,
56
+ unhandledMessages: unhandled,
57
+ ackedIds,
58
+ });
59
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Send relay message command.
3
+ */
4
+
5
+ import { OVERLAY_URL } from '../config.js';
6
+ import { ok, fail } from '../output.js';
7
+ import { loadIdentity, signRelayMessage } from '../wallet/identity.js';
8
+
9
+ /**
10
+ * Send command: send a typed message to another agent.
11
+ */
12
+ export async function cmdSend(
13
+ targetKey: string | undefined,
14
+ type: string | undefined,
15
+ payloadStr: string | undefined
16
+ ): Promise<never> {
17
+ if (!targetKey || !type || !payloadStr) {
18
+ return fail('Usage: send <identityKey> <type> <json_payload>');
19
+ }
20
+
21
+ if (!/^0[23][0-9a-fA-F]{64}$/.test(targetKey)) {
22
+ return fail('Target must be a compressed public key (66 hex chars, 02/03 prefix)');
23
+ }
24
+
25
+ let payload: unknown;
26
+ try {
27
+ payload = JSON.parse(payloadStr);
28
+ } catch {
29
+ return fail('payload must be valid JSON');
30
+ }
31
+
32
+ const { identityKey, privKey } = await loadIdentity();
33
+ const signature = await signRelayMessage(privKey, targetKey, type, payload);
34
+
35
+ const resp = await fetch(`${OVERLAY_URL}/relay/send`, {
36
+ method: 'POST',
37
+ headers: { 'Content-Type': 'application/json' },
38
+ body: JSON.stringify({
39
+ from: identityKey,
40
+ to: targetKey,
41
+ type,
42
+ payload,
43
+ signature,
44
+ }),
45
+ });
46
+
47
+ if (!resp.ok) {
48
+ const body = await resp.text();
49
+ return fail(`Relay send failed (${resp.status}): ${body}`);
50
+ }
51
+
52
+ const result = await resp.json();
53
+ return ok({ sent: true, messageId: result.id, to: targetKey, type, signed: true });
54
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * JSON output helpers for CLI commands.
3
+ * All CLI output follows the { success, data/error } wrapper format.
4
+ */
5
+
6
+ /**
7
+ * Output a successful result and exit.
8
+ */
9
+ export function ok<T>(data: T): never {
10
+ console.log(JSON.stringify({ success: true, data }));
11
+ process.exit(0);
12
+ }
13
+
14
+ /**
15
+ * Output an error and exit.
16
+ */
17
+ export function fail(error: string | Error): never {
18
+ const message = error instanceof Error ? error.message : String(error);
19
+ console.log(JSON.stringify({ success: false, error: message }));
20
+ process.exit(1);
21
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Overlay discovery commands.
3
+ */
4
+
5
+ import { OVERLAY_URL, LOOKUP_SERVICES } from '../config.js';
6
+ import { ok } from '../output.js';
7
+ import { lookupOverlay, parseOverlayOutput } from './transaction.js';
8
+
9
+ /**
10
+ * Discover command: query the overlay for agents and services.
11
+ */
12
+ export async function cmdDiscover(args: string[]): Promise<never> {
13
+
14
+ // Parse flags
15
+ let serviceFilter: string | null = null;
16
+ let agentFilter: string | null = null;
17
+
18
+ for (let i = 0; i < args.length; i++) {
19
+ if (args[i] === '--service' && args[i + 1]) serviceFilter = args[++i];
20
+ else if (args[i] === '--agent' && args[i + 1]) agentFilter = args[++i];
21
+ }
22
+
23
+ const results: {
24
+ agents: any[];
25
+ services: any[];
26
+ agentError?: string;
27
+ serviceError?: string;
28
+ } = { agents: [], services: [] };
29
+
30
+ // Query agents
31
+ if (!serviceFilter) {
32
+ try {
33
+ const agentQuery = agentFilter ? { name: agentFilter } : { type: 'list' };
34
+ const agentResult = await lookupOverlay(LOOKUP_SERVICES.AGENTS, agentQuery);
35
+
36
+ if (agentResult.outputs) {
37
+ for (const output of agentResult.outputs) {
38
+ try {
39
+ const { data, txid } = await parseOverlayOutput(output.beef, output.outputIndex);
40
+ if (data?.type === 'identity') {
41
+ results.agents.push({ ...data, txid });
42
+ }
43
+ } catch { /* ignore */ }
44
+ }
45
+ }
46
+ } catch (err: any) {
47
+ results.agentError = String(err);
48
+ }
49
+ }
50
+
51
+ // Query services
52
+ if (!agentFilter) {
53
+ try {
54
+ const serviceQuery = serviceFilter ? { serviceType: serviceFilter } : {};
55
+ const serviceResult = await lookupOverlay(LOOKUP_SERVICES.SERVICES, serviceQuery);
56
+
57
+ if (serviceResult.outputs) {
58
+ for (const output of serviceResult.outputs) {
59
+ try {
60
+ const { data, txid } = await parseOverlayOutput(output.beef, output.outputIndex);
61
+ if (data?.type === 'service') {
62
+ results.services.push({ ...data, txid });
63
+ }
64
+ } catch { /* ignore */ }
65
+ }
66
+ }
67
+ } catch (err: any) {
68
+ results.serviceError = String(err);
69
+ }
70
+ }
71
+
72
+ return ok({
73
+ overlayUrl: OVERLAY_URL,
74
+ agentCount: results.agents.length,
75
+ serviceCount: results.services.length,
76
+ agents: results.agents,
77
+ services: results.services,
78
+ ...(results.agentError && { agentError: results.agentError }),
79
+ ...(results.serviceError && { serviceError: results.serviceError }),
80
+ });
81
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Overlay module exports.
3
+ */
4
+
5
+ export * from './transaction.js';
6
+ export * from './registration.js';
7
+ export * from './services.js';
8
+ export * from './discover.js';
@@ -0,0 +1,199 @@
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
+ import fs from 'node:fs';
14
+ import { NETWORK, WALLET_DIR, OVERLAY_URL, PROTOCOL_ID, TOPICS, PATHS, AGENT_NAME, AGENT_DESCRIPTION } from '../config.js';
15
+ import { ok, fail } from '../output.js';
16
+ import { loadRegistration, saveRegistration, deleteRegistration, loadServices } from '../utils/storage.js';
17
+ import { buildRealOverlayTransaction } from './transaction.js';
18
+ import { Transaction, Beef, Script, PushDrop, WalletOutput } from '@bsv/sdk'
19
+
20
+ import { BSVAgentWallet } from '../../core/index.js';
21
+
22
+ async function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {
23
+ return BSVAgentWallet;
24
+ }
25
+
26
+ /**
27
+ * Register command: register this agent on the overlay network.
28
+ */
29
+ export async function cmdRegister(): Promise<never> {
30
+ if (!fs.existsSync(PATHS.walletIdentity)) {
31
+ return fail('Wallet not initialized. Run: setup');
32
+ }
33
+
34
+ const BSVAgentWallet = await getBSVAgentWallet();
35
+ const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
36
+ const identityKey = await wallet.getIdentityKey();
37
+ await wallet.destroy();
38
+
39
+ const existingReg = loadRegistration();
40
+ if (existingReg && existingReg.identityKey === identityKey) {
41
+ return ok({
42
+ alreadyRegistered: true,
43
+ identityKey,
44
+ identityTxid: existingReg.identityTxid,
45
+ overlayUrl: OVERLAY_URL,
46
+ });
47
+ }
48
+
49
+ // Agent metadata from environment/config
50
+ const agentName = AGENT_NAME;
51
+ const agentDescription = AGENT_DESCRIPTION;
52
+
53
+ // Build capabilities list based on what services we might offer
54
+ const capabilities: string[] = ['services'];
55
+ const services = loadServices();
56
+ if (services.some(s => s.serviceId === 'tell-joke')) {
57
+ capabilities.push('jokes');
58
+ }
59
+
60
+ // Create identity record on-chain
61
+ // This payload format matches the clawdbot-overlay server's expected schema
62
+ const identityPayload = {
63
+ protocol: PROTOCOL_ID,
64
+ type: 'identity' as const,
65
+ identityKey,
66
+ name: agentName,
67
+ description: agentDescription,
68
+ channels: {
69
+ overlay: OVERLAY_URL,
70
+ },
71
+ capabilities,
72
+ timestamp: new Date().toISOString(),
73
+ };
74
+
75
+ let identityResult: { txid: string; funded: string };
76
+ try {
77
+ identityResult = await buildRealOverlayTransaction(identityPayload, TOPICS.IDENTITY);
78
+ } catch (err: any) {
79
+ return fail(`Registration failed: ${err.message}`);
80
+ }
81
+
82
+ // Optionally register services if pre-configured
83
+ let serviceTxid: string | null = null;
84
+
85
+ if (services.length > 0) {
86
+ // Register each service individually (server expects 'service' type, not 'service-bundle')
87
+ for (const service of services) {
88
+ const servicePayload = {
89
+ protocol: PROTOCOL_ID,
90
+ type: 'service' as const,
91
+ identityKey,
92
+ serviceId: service.serviceId,
93
+ name: service.name,
94
+ description: service.description,
95
+ pricing: {
96
+ model: 'per-task',
97
+ amountSats: service.priceSats,
98
+ },
99
+ timestamp: new Date().toISOString(),
100
+ };
101
+
102
+ try {
103
+ const serviceResult = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);
104
+ serviceTxid = serviceResult.txid; // Keep last one for backward compat
105
+ } catch {
106
+ // Non-fatal — identity registered but this service failed
107
+ }
108
+ }
109
+ }
110
+
111
+ // Save registration
112
+ const registration = {
113
+ identityKey,
114
+ agentName,
115
+ agentDescription,
116
+ overlayUrl: OVERLAY_URL,
117
+ identityTxid: identityResult.txid,
118
+ serviceTxid,
119
+ funded: identityResult.funded,
120
+ registeredAt: new Date().toISOString(),
121
+ };
122
+ saveRegistration(registration);
123
+
124
+ return ok({
125
+ registered: true,
126
+ identityKey,
127
+ identityTxid: identityResult.txid,
128
+ serviceTxid,
129
+ overlayUrl: OVERLAY_URL,
130
+ funded: identityResult.funded,
131
+ stateFile: PATHS.registration,
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Unregister command: submit revocation tx to remove agent from overlay network.
137
+ */
138
+ export async function cmdUnregister(): Promise<never> {
139
+
140
+ const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
141
+ const { outputs, BEEF } = await wallet._setup.wallet.listOutputs({ basket: TOPICS.IDENTITY, include: 'entire transactions' });
142
+
143
+ const token = new PushDrop(wallet._setup.wallet);
144
+ const unlockingScriptTemplate = await token.unlock([0, PROTOCOL_ID], '1', 'self', 'none', true)
145
+ const tempTx = new Transaction()
146
+ const beef = Beef.fromBinary(BEEF as number[])
147
+ outputs.forEach((o: WalletOutput) => {
148
+ const [txid, v] = o.outpoint.split('.')
149
+ const sourceOutputIndex = Number(v)
150
+ const sourceTransaction = beef.findTransactionForSigning(txid)
151
+ tempTx.addInput({
152
+ unlockingScriptTemplate,
153
+ sourceOutputIndex,
154
+ sourceTransaction
155
+ })
156
+ })
157
+ tempTx.addOutput({
158
+ lockingScript: Script.fromASM('OP_FALSE OP_RETURN 330123'),
159
+ satoshis: 0
160
+ })
161
+
162
+ await tempTx.sign()
163
+
164
+ const response = await wallet._setup.wallet.createAction({
165
+ inputBEEF: BEEF,
166
+ description: 'revoke registration token',
167
+ inputs: tempTx.inputs.map(o => ({
168
+ inputDescription: 'previous registration',
169
+ outpoint: o.sourceTXID + '.' + String(o.sourceOutputIndex),
170
+ unlockingScript: o.unlockingScript?.toHex() as string
171
+ }))
172
+ })
173
+
174
+ const txid = response.txid as string;
175
+
176
+ // --- Submit to overlay ---
177
+ // Use binary BEEF with X-Topics header (matches clawdbot-overlay server API)
178
+ const submitResp = await fetch(`${OVERLAY_URL}/submit`, {
179
+ method: 'POST',
180
+ headers: {
181
+ 'Content-Type': 'application/octet-stream',
182
+ 'X-Topics': JSON.stringify([TOPICS.IDENTITY]),
183
+ },
184
+ body: new Uint8Array(response.tx as number[]),
185
+ });
186
+
187
+ if (!submitResp.ok) {
188
+ const errText = await submitResp.text();
189
+ throw new Error(`Overlay submission failed: ${submitResp.status} — ${errText}`);
190
+ }
191
+
192
+ // Delete local registration
193
+ deleteRegistration();
194
+
195
+ return ok({
196
+ unregistered: true,
197
+ txid
198
+ });
199
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Overlay service commands: services, advertise, remove, readvertise.
3
+ *
4
+ * Service payloads match the clawdbot-overlay server schema:
5
+ * - protocol: "clawdbot-overlay-v1"
6
+ * - type: "service"
7
+ * - identityKey: provider's compressed public key
8
+ * - serviceId: unique service identifier
9
+ * - name: human-readable name
10
+ * - description: what the service does
11
+ * - pricing: { model: "per-task", amountSats: number }
12
+ * - timestamp: ISO 8601 time
13
+ */
14
+
15
+ import { NETWORK, WALLET_DIR, PROTOCOL_ID, TOPICS } from '../config.js';
16
+ import { ok, fail } from '../output.js';
17
+ import { loadServices, saveServices } from '../utils/storage.js';
18
+ import { buildRealOverlayTransaction } from './transaction.js';
19
+ import type { ServiceAdvertisement } from '../types.js';
20
+
21
+ import { BSVAgentWallet } from '../../core/index.js';
22
+
23
+ async function getBSVAgentWallet(): Promise<typeof BSVAgentWallet> {
24
+ return BSVAgentWallet;
25
+ }
26
+
27
+ /**
28
+ * Services command: list currently advertised services.
29
+ */
30
+ export async function cmdServices(): Promise<never> {
31
+ const services = loadServices();
32
+ return ok({ services, count: services.length });
33
+ }
34
+
35
+ /**
36
+ * Advertise command: add a new service advertisement.
37
+ */
38
+ export async function cmdAdvertise(
39
+ serviceId: string | undefined,
40
+ name: string | undefined,
41
+ priceSatsStr: string | undefined,
42
+ description?: string
43
+ ): Promise<never> {
44
+ if (!serviceId || !name || !priceSatsStr) {
45
+ return fail('Usage: advertise <serviceId> <name> <priceSats> [description]');
46
+ }
47
+
48
+ const priceSats = parseInt(priceSatsStr, 10);
49
+ if (isNaN(priceSats) || priceSats < 0) {
50
+ return fail('priceSats must be a non-negative integer');
51
+ }
52
+
53
+ const BSVAgentWallet = await getBSVAgentWallet();
54
+ const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
55
+ const identityKey = await wallet.getIdentityKey();
56
+ await wallet.destroy();
57
+
58
+ // Load existing services
59
+ const services = loadServices();
60
+ const existing = services.find(s => s.serviceId === serviceId);
61
+ if (existing) {
62
+ return fail(`Service '${serviceId}' already exists. Use 'readvertise' to update.`);
63
+ }
64
+
65
+ // Create service record (local storage format)
66
+ const newService: ServiceAdvertisement = {
67
+ serviceId,
68
+ name,
69
+ description: description || `${name} service`,
70
+ priceSats,
71
+ registeredAt: new Date().toISOString(),
72
+ };
73
+
74
+ // Publish on-chain (matches clawdbot-overlay server schema)
75
+ const servicePayload = {
76
+ protocol: PROTOCOL_ID,
77
+ type: 'service' as const,
78
+ identityKey,
79
+ serviceId,
80
+ name,
81
+ description: newService.description,
82
+ pricing: {
83
+ model: 'per-task',
84
+ amountSats: priceSats,
85
+ },
86
+ timestamp: new Date().toISOString(),
87
+ };
88
+
89
+ try {
90
+ const result = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);
91
+ newService.txid = result.txid;
92
+
93
+ // Save locally
94
+ services.push(newService);
95
+ saveServices(services);
96
+
97
+ return ok({
98
+ advertised: true,
99
+ service: newService,
100
+ txid: result.txid,
101
+ funded: result.funded,
102
+ });
103
+ } catch (err: any) {
104
+ return fail(`Failed to advertise service: ${err.message}`);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Remove command: remove a service from local registry.
110
+ */
111
+ export async function cmdRemove(serviceId: string | undefined): Promise<never> {
112
+ if (!serviceId) {
113
+ return fail('Usage: remove <serviceId>');
114
+ }
115
+
116
+ const services = loadServices();
117
+ const idx = services.findIndex(s => s.serviceId === serviceId);
118
+ if (idx === -1) {
119
+ return fail(`Service '${serviceId}' not found`);
120
+ }
121
+
122
+ const removed = services.splice(idx, 1)[0];
123
+ saveServices(services);
124
+
125
+ return ok({
126
+ removed: true,
127
+ service: removed,
128
+ note: 'Removed from local registry. On-chain record remains (blockchain is immutable).',
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Readvertise command: update an existing service advertisement.
134
+ */
135
+ export async function cmdReadvertise(
136
+ serviceId: string | undefined,
137
+ name?: string,
138
+ priceSatsStr?: string,
139
+ description?: string
140
+ ): Promise<never> {
141
+ if (!serviceId) {
142
+ return fail('Usage: readvertise <serviceId> [name] [priceSats] [description]');
143
+ }
144
+
145
+ const services = loadServices();
146
+ const existing = services.find(s => s.serviceId === serviceId);
147
+ if (!existing) {
148
+ return fail(`Service '${serviceId}' not found. Use 'advertise' to create.`);
149
+ }
150
+
151
+ const BSVAgentWallet = await getBSVAgentWallet();
152
+ const wallet = await BSVAgentWallet.load({ network: NETWORK, storageDir: WALLET_DIR });
153
+ const identityKey = await wallet.getIdentityKey();
154
+ await wallet.destroy();
155
+
156
+ // Update fields if provided
157
+ if (name) existing.name = name;
158
+ if (priceSatsStr) {
159
+ const priceSats = parseInt(priceSatsStr, 10);
160
+ if (isNaN(priceSats) || priceSats < 0) {
161
+ return fail('priceSats must be a non-negative integer');
162
+ }
163
+ existing.priceSats = priceSats;
164
+ }
165
+ if (description) existing.description = description;
166
+ existing.registeredAt = new Date().toISOString();
167
+
168
+ // Publish update on-chain (matches clawdbot-overlay server schema)
169
+ const servicePayload = {
170
+ protocol: PROTOCOL_ID,
171
+ type: 'service' as const,
172
+ identityKey,
173
+ serviceId,
174
+ name: existing.name,
175
+ description: existing.description,
176
+ pricing: {
177
+ model: 'per-task',
178
+ amountSats: existing.priceSats,
179
+ },
180
+ timestamp: existing.registeredAt,
181
+ };
182
+
183
+ try {
184
+ const result = await buildRealOverlayTransaction(servicePayload, TOPICS.SERVICES);
185
+ existing.txid = result.txid;
186
+
187
+ // Save locally
188
+ saveServices(services);
189
+
190
+ return ok({
191
+ readvertised: true,
192
+ service: existing,
193
+ txid: result.txid,
194
+ funded: result.funded,
195
+ });
196
+ } catch (err: any) {
197
+ return fail(`Failed to readvertise service: ${err.message}`);
198
+ }
199
+ }