@rookdaemon/agora 0.2.8 → 0.2.9

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 (143) hide show
  1. package/dist/chunk-JUOGKXFN.js +1645 -0
  2. package/dist/chunk-JUOGKXFN.js.map +1 -0
  3. package/dist/cli.d.ts +0 -2
  4. package/dist/cli.js +1163 -1137
  5. package/dist/cli.js.map +1 -1
  6. package/dist/index.d.ts +1613 -30
  7. package/dist/index.js +1135 -29
  8. package/dist/index.js.map +1 -1
  9. package/package.json +4 -2
  10. package/dist/cli.d.ts.map +0 -1
  11. package/dist/config.d.ts +0 -59
  12. package/dist/config.d.ts.map +0 -1
  13. package/dist/config.js +0 -115
  14. package/dist/config.js.map +0 -1
  15. package/dist/discovery/bootstrap.d.ts +0 -32
  16. package/dist/discovery/bootstrap.d.ts.map +0 -1
  17. package/dist/discovery/bootstrap.js +0 -36
  18. package/dist/discovery/bootstrap.js.map +0 -1
  19. package/dist/discovery/peer-discovery.d.ts +0 -59
  20. package/dist/discovery/peer-discovery.d.ts.map +0 -1
  21. package/dist/discovery/peer-discovery.js +0 -108
  22. package/dist/discovery/peer-discovery.js.map +0 -1
  23. package/dist/identity/keypair.d.ts +0 -42
  24. package/dist/identity/keypair.d.ts.map +0 -1
  25. package/dist/identity/keypair.js +0 -83
  26. package/dist/identity/keypair.js.map +0 -1
  27. package/dist/index.d.ts.map +0 -1
  28. package/dist/message/envelope.d.ts +0 -59
  29. package/dist/message/envelope.d.ts.map +0 -1
  30. package/dist/message/envelope.js +0 -83
  31. package/dist/message/envelope.js.map +0 -1
  32. package/dist/message/types/paper-discovery.d.ts +0 -28
  33. package/dist/message/types/paper-discovery.d.ts.map +0 -1
  34. package/dist/message/types/paper-discovery.js +0 -2
  35. package/dist/message/types/paper-discovery.js.map +0 -1
  36. package/dist/message/types/peer-discovery.d.ts +0 -78
  37. package/dist/message/types/peer-discovery.d.ts.map +0 -1
  38. package/dist/message/types/peer-discovery.js +0 -90
  39. package/dist/message/types/peer-discovery.js.map +0 -1
  40. package/dist/peer/client.d.ts +0 -50
  41. package/dist/peer/client.d.ts.map +0 -1
  42. package/dist/peer/client.js +0 -138
  43. package/dist/peer/client.js.map +0 -1
  44. package/dist/peer/manager.d.ts +0 -65
  45. package/dist/peer/manager.d.ts.map +0 -1
  46. package/dist/peer/manager.js +0 -153
  47. package/dist/peer/manager.js.map +0 -1
  48. package/dist/peer/server.d.ts +0 -65
  49. package/dist/peer/server.d.ts.map +0 -1
  50. package/dist/peer/server.js +0 -154
  51. package/dist/peer/server.js.map +0 -1
  52. package/dist/registry/capability.d.ts +0 -44
  53. package/dist/registry/capability.d.ts.map +0 -1
  54. package/dist/registry/capability.js +0 -94
  55. package/dist/registry/capability.js.map +0 -1
  56. package/dist/registry/discovery-service.d.ts +0 -64
  57. package/dist/registry/discovery-service.d.ts.map +0 -1
  58. package/dist/registry/discovery-service.js +0 -129
  59. package/dist/registry/discovery-service.js.map +0 -1
  60. package/dist/registry/messages.d.ts +0 -105
  61. package/dist/registry/messages.d.ts.map +0 -1
  62. package/dist/registry/messages.js +0 -2
  63. package/dist/registry/messages.js.map +0 -1
  64. package/dist/registry/peer-store.d.ts +0 -57
  65. package/dist/registry/peer-store.d.ts.map +0 -1
  66. package/dist/registry/peer-store.js +0 -92
  67. package/dist/registry/peer-store.js.map +0 -1
  68. package/dist/registry/peer.d.ts +0 -20
  69. package/dist/registry/peer.d.ts.map +0 -1
  70. package/dist/registry/peer.js +0 -2
  71. package/dist/registry/peer.js.map +0 -1
  72. package/dist/relay/client.d.ts +0 -112
  73. package/dist/relay/client.d.ts.map +0 -1
  74. package/dist/relay/client.js +0 -281
  75. package/dist/relay/client.js.map +0 -1
  76. package/dist/relay/jwt-auth.d.ts +0 -40
  77. package/dist/relay/jwt-auth.d.ts.map +0 -1
  78. package/dist/relay/jwt-auth.js +0 -109
  79. package/dist/relay/jwt-auth.js.map +0 -1
  80. package/dist/relay/message-buffer.d.ts +0 -41
  81. package/dist/relay/message-buffer.d.ts.map +0 -1
  82. package/dist/relay/message-buffer.js +0 -53
  83. package/dist/relay/message-buffer.js.map +0 -1
  84. package/dist/relay/rest-api.d.ts +0 -68
  85. package/dist/relay/rest-api.d.ts.map +0 -1
  86. package/dist/relay/rest-api.js +0 -225
  87. package/dist/relay/rest-api.js.map +0 -1
  88. package/dist/relay/run-relay.d.ts +0 -33
  89. package/dist/relay/run-relay.d.ts.map +0 -1
  90. package/dist/relay/run-relay.js +0 -57
  91. package/dist/relay/run-relay.js.map +0 -1
  92. package/dist/relay/server.d.ts +0 -91
  93. package/dist/relay/server.d.ts.map +0 -1
  94. package/dist/relay/server.js +0 -385
  95. package/dist/relay/server.js.map +0 -1
  96. package/dist/relay/store.d.ts +0 -19
  97. package/dist/relay/store.d.ts.map +0 -1
  98. package/dist/relay/store.js +0 -55
  99. package/dist/relay/store.js.map +0 -1
  100. package/dist/relay/types.d.ts +0 -35
  101. package/dist/relay/types.d.ts.map +0 -1
  102. package/dist/relay/types.js +0 -2
  103. package/dist/relay/types.js.map +0 -1
  104. package/dist/reputation/commit-reveal.d.ts +0 -45
  105. package/dist/reputation/commit-reveal.d.ts.map +0 -1
  106. package/dist/reputation/commit-reveal.js +0 -125
  107. package/dist/reputation/commit-reveal.js.map +0 -1
  108. package/dist/reputation/scoring.d.ts +0 -31
  109. package/dist/reputation/scoring.d.ts.map +0 -1
  110. package/dist/reputation/scoring.js +0 -105
  111. package/dist/reputation/scoring.js.map +0 -1
  112. package/dist/reputation/store.d.ts +0 -83
  113. package/dist/reputation/store.d.ts.map +0 -1
  114. package/dist/reputation/store.js +0 -202
  115. package/dist/reputation/store.js.map +0 -1
  116. package/dist/reputation/types.d.ts +0 -150
  117. package/dist/reputation/types.d.ts.map +0 -1
  118. package/dist/reputation/types.js +0 -113
  119. package/dist/reputation/types.js.map +0 -1
  120. package/dist/reputation/verification.d.ts +0 -28
  121. package/dist/reputation/verification.d.ts.map +0 -1
  122. package/dist/reputation/verification.js +0 -91
  123. package/dist/reputation/verification.js.map +0 -1
  124. package/dist/service.d.ts +0 -90
  125. package/dist/service.d.ts.map +0 -1
  126. package/dist/service.js +0 -176
  127. package/dist/service.js.map +0 -1
  128. package/dist/transport/http.d.ts +0 -41
  129. package/dist/transport/http.d.ts.map +0 -1
  130. package/dist/transport/http.js +0 -103
  131. package/dist/transport/http.js.map +0 -1
  132. package/dist/transport/peer-config.d.ts +0 -38
  133. package/dist/transport/peer-config.d.ts.map +0 -1
  134. package/dist/transport/peer-config.js +0 -41
  135. package/dist/transport/peer-config.js.map +0 -1
  136. package/dist/transport/relay.d.ts +0 -30
  137. package/dist/transport/relay.d.ts.map +0 -1
  138. package/dist/transport/relay.js +0 -85
  139. package/dist/transport/relay.js.map +0 -1
  140. package/dist/utils.d.ts +0 -40
  141. package/dist/utils.d.ts.map +0 -1
  142. package/dist/utils.js +0 -59
  143. package/dist/utils.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,1208 +1,1234 @@
1
1
  #!/usr/bin/env node
2
- import { parseArgs } from 'node:util';
3
- import { existsSync, mkdirSync } from 'node:fs';
4
- import { dirname, resolve } from 'node:path';
5
- import { homedir } from 'node:os';
6
- import { loadPeerConfig, savePeerConfig, initPeerConfig } from './transport/peer-config.js';
7
- import { sendToPeer, decodeInboundEnvelope } from './transport/http.js';
8
- import { sendViaRelay } from './transport/relay.js';
9
- import { PeerServer } from './peer/server.js';
10
- import { RelayServer } from './relay/server.js';
11
- import { RelayClient } from './relay/client.js';
12
- import { PeerDiscoveryService } from './discovery/peer-discovery.js';
13
- import { getDefaultBootstrapRelay } from './discovery/bootstrap.js';
14
- import { resolveBroadcastName } from './utils.js';
15
- import { ReputationStore } from './reputation/store.js';
16
- import { createVerification } from './reputation/verification.js';
17
- import { createCommit, createReveal, verifyReveal } from './reputation/commit-reveal.js';
18
- import { computeTrustScore } from './reputation/scoring.js';
19
- /**
20
- * Get the config file path from CLI options, environment, or default.
21
- */
22
- function getConfigPath(options) {
23
- if (options.config) {
24
- return resolve(options.config);
25
- }
26
- if (process.env.AGORA_CONFIG) {
27
- return resolve(process.env.AGORA_CONFIG);
2
+ import {
3
+ PeerDiscoveryService,
4
+ RelayClient,
5
+ RelayServer,
6
+ ReputationStore,
7
+ computeTrustScore,
8
+ createCommit,
9
+ createEnvelope,
10
+ createReveal,
11
+ createVerification,
12
+ decodeInboundEnvelope,
13
+ getDefaultBootstrapRelay,
14
+ initPeerConfig,
15
+ loadPeerConfig,
16
+ resolveBroadcastName,
17
+ savePeerConfig,
18
+ sendToPeer,
19
+ sendViaRelay,
20
+ verifyEnvelope,
21
+ verifyReveal
22
+ } from "./chunk-JUOGKXFN.js";
23
+
24
+ // src/cli.ts
25
+ import { parseArgs } from "util";
26
+ import { existsSync, mkdirSync } from "fs";
27
+ import { dirname, resolve } from "path";
28
+ import { homedir } from "os";
29
+
30
+ // src/peer/server.ts
31
+ import { EventEmitter } from "events";
32
+ import { WebSocketServer, WebSocket } from "ws";
33
+ var PeerServer = class extends EventEmitter {
34
+ wss = null;
35
+ peers = /* @__PURE__ */ new Map();
36
+ identity;
37
+ announcePayload;
38
+ constructor(identity, announcePayload) {
39
+ super();
40
+ this.identity = identity;
41
+ this.announcePayload = announcePayload;
42
+ }
43
+ /**
44
+ * Start the WebSocket server
45
+ */
46
+ start(port) {
47
+ return new Promise((resolve2, reject) => {
48
+ try {
49
+ this.wss = new WebSocketServer({ port });
50
+ this.wss.on("error", (error) => {
51
+ this.emit("error", error);
52
+ reject(error);
53
+ });
54
+ this.wss.on("listening", () => {
55
+ resolve2();
56
+ });
57
+ this.wss.on("connection", (socket) => {
58
+ this.handleConnection(socket);
59
+ });
60
+ } catch (error) {
61
+ reject(error);
62
+ }
63
+ });
64
+ }
65
+ /**
66
+ * Stop the WebSocket server
67
+ */
68
+ async stop() {
69
+ return new Promise((resolve2, reject) => {
70
+ if (!this.wss) {
71
+ resolve2();
72
+ return;
73
+ }
74
+ for (const peer of this.peers.values()) {
75
+ peer.socket.close();
76
+ }
77
+ this.peers.clear();
78
+ this.wss.close((err) => {
79
+ if (err) {
80
+ reject(err);
81
+ } else {
82
+ this.wss = null;
83
+ resolve2();
84
+ }
85
+ });
86
+ });
87
+ }
88
+ /**
89
+ * Get all connected peers
90
+ */
91
+ getPeers() {
92
+ return new Map(this.peers);
93
+ }
94
+ /**
95
+ * Send a message to a specific peer
96
+ */
97
+ send(publicKey, envelope) {
98
+ const peer = this.peers.get(publicKey);
99
+ if (!peer || peer.socket.readyState !== WebSocket.OPEN) {
100
+ return false;
28
101
  }
29
- return resolve(homedir(), '.config', 'agora', 'config.json');
102
+ try {
103
+ peer.socket.send(JSON.stringify(envelope));
104
+ return true;
105
+ } catch (error) {
106
+ this.emit("error", error);
107
+ return false;
108
+ }
109
+ }
110
+ /**
111
+ * Broadcast a message to all connected peers
112
+ */
113
+ broadcast(envelope) {
114
+ for (const [publicKey, peer] of this.peers) {
115
+ if (peer.socket.readyState === WebSocket.OPEN) {
116
+ this.send(publicKey, envelope);
117
+ }
118
+ }
119
+ }
120
+ /**
121
+ * Handle incoming connection
122
+ */
123
+ handleConnection(socket) {
124
+ let peerPublicKey = null;
125
+ const announceEnvelope = createEnvelope(
126
+ "announce",
127
+ this.identity.publicKey,
128
+ this.identity.privateKey,
129
+ this.announcePayload
130
+ );
131
+ socket.send(JSON.stringify(announceEnvelope));
132
+ socket.on("message", (data) => {
133
+ try {
134
+ const envelope = JSON.parse(data.toString());
135
+ const verification = verifyEnvelope(envelope);
136
+ if (!verification.valid) {
137
+ return;
138
+ }
139
+ if (!peerPublicKey) {
140
+ if (envelope.type === "announce") {
141
+ peerPublicKey = envelope.sender;
142
+ const payload = envelope.payload;
143
+ const peer = {
144
+ publicKey: peerPublicKey,
145
+ socket,
146
+ announced: true,
147
+ metadata: payload.metadata
148
+ };
149
+ this.peers.set(peerPublicKey, peer);
150
+ this.emit("peer-connected", peerPublicKey, peer);
151
+ }
152
+ return;
153
+ }
154
+ if (envelope.sender !== peerPublicKey) {
155
+ return;
156
+ }
157
+ this.emit("message-received", envelope, peerPublicKey);
158
+ } catch {
159
+ }
160
+ });
161
+ socket.on("close", () => {
162
+ if (peerPublicKey) {
163
+ this.peers.delete(peerPublicKey);
164
+ this.emit("peer-disconnected", peerPublicKey);
165
+ }
166
+ });
167
+ socket.on("error", (error) => {
168
+ this.emit("error", error);
169
+ });
170
+ }
171
+ };
172
+
173
+ // src/cli.ts
174
+ function getConfigPath(options) {
175
+ if (options.config) {
176
+ return resolve(options.config);
177
+ }
178
+ if (process.env.AGORA_CONFIG) {
179
+ return resolve(process.env.AGORA_CONFIG);
180
+ }
181
+ return resolve(homedir(), ".config", "agora", "config.json");
30
182
  }
31
- /**
32
- * Ensure the config directory exists.
33
- */
34
183
  function ensureConfigDir(configPath) {
35
- const dir = dirname(configPath);
36
- if (!existsSync(dir)) {
37
- mkdirSync(dir, { recursive: true });
38
- }
184
+ const dir = dirname(configPath);
185
+ if (!existsSync(dir)) {
186
+ mkdirSync(dir, { recursive: true });
187
+ }
39
188
  }
40
- /**
41
- * Output data as JSON or pretty format.
42
- */
43
189
  function output(data, pretty) {
44
- if (pretty) {
45
- // Pretty output for humans
46
- if (typeof data === 'object' && data !== null) {
47
- for (const [key, value] of Object.entries(data)) {
48
- if (Array.isArray(value)) {
49
- console.log(`${key}:`);
50
- for (const item of value) {
51
- if (typeof item === 'object' && item !== null) {
52
- const entries = Object.entries(item);
53
- console.log(` - ${entries.map(([k, v]) => `${k}: ${v}`).join(', ')}`);
54
- }
55
- else {
56
- console.log(` - ${item}`);
57
- }
58
- }
59
- }
60
- else if (typeof value === 'object' && value !== null) {
61
- console.log(`${key}:`);
62
- for (const [k, v] of Object.entries(value)) {
63
- console.log(` ${k}: ${v}`);
64
- }
65
- }
66
- else {
67
- console.log(`${key}: ${value}`);
68
- }
190
+ if (pretty) {
191
+ if (typeof data === "object" && data !== null) {
192
+ for (const [key, value] of Object.entries(data)) {
193
+ if (Array.isArray(value)) {
194
+ console.log(`${key}:`);
195
+ for (const item of value) {
196
+ if (typeof item === "object" && item !== null) {
197
+ const entries = Object.entries(item);
198
+ console.log(` - ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")}`);
199
+ } else {
200
+ console.log(` - ${item}`);
69
201
  }
202
+ }
203
+ } else if (typeof value === "object" && value !== null) {
204
+ console.log(`${key}:`);
205
+ for (const [k, v] of Object.entries(value)) {
206
+ console.log(` ${k}: ${v}`);
207
+ }
208
+ } else {
209
+ console.log(`${key}: ${value}`);
70
210
  }
71
- else {
72
- console.log(data);
73
- }
74
- }
75
- else {
76
- // JSON output for programmatic use
77
- console.log(JSON.stringify(data, null, 2));
211
+ }
212
+ } else {
213
+ console.log(data);
78
214
  }
215
+ } else {
216
+ console.log(JSON.stringify(data, null, 2));
217
+ }
79
218
  }
80
- /**
81
- * Handle the `agora init` command.
82
- */
83
219
  function handleInit(options) {
84
- const configPath = getConfigPath(options);
85
- ensureConfigDir(configPath);
86
- if (existsSync(configPath)) {
87
- const config = loadPeerConfig(configPath);
88
- output({
89
- status: 'already_initialized',
90
- publicKey: config.identity.publicKey,
91
- configPath
92
- }, options.pretty || false);
93
- process.exit(0);
94
- }
95
- const config = initPeerConfig(configPath);
220
+ const configPath = getConfigPath(options);
221
+ ensureConfigDir(configPath);
222
+ if (existsSync(configPath)) {
223
+ const config2 = loadPeerConfig(configPath);
96
224
  output({
97
- status: 'initialized',
98
- publicKey: config.identity.publicKey,
99
- configPath
225
+ status: "already_initialized",
226
+ publicKey: config2.identity.publicKey,
227
+ configPath
100
228
  }, options.pretty || false);
229
+ process.exit(0);
230
+ }
231
+ const config = initPeerConfig(configPath);
232
+ output({
233
+ status: "initialized",
234
+ publicKey: config.identity.publicKey,
235
+ configPath
236
+ }, options.pretty || false);
101
237
  }
102
- /**
103
- * Handle the `agora whoami` command.
104
- */
105
238
  function handleWhoami(options) {
106
- const configPath = getConfigPath(options);
107
- if (!existsSync(configPath)) {
108
- console.error('Error: Config file not found. Run `agora init` first.');
109
- process.exit(1);
110
- }
111
- const config = loadPeerConfig(configPath);
112
- output({
113
- publicKey: config.identity.publicKey,
114
- configPath
115
- }, options.pretty || false);
239
+ const configPath = getConfigPath(options);
240
+ if (!existsSync(configPath)) {
241
+ console.error("Error: Config file not found. Run `agora init` first.");
242
+ process.exit(1);
243
+ }
244
+ const config = loadPeerConfig(configPath);
245
+ output({
246
+ publicKey: config.identity.publicKey,
247
+ configPath
248
+ }, options.pretty || false);
116
249
  }
117
- /**
118
- * Handle the `agora peers add` command.
119
- */
120
250
  function handlePeersAdd(args, options) {
121
- if (args.length < 1) {
122
- console.error('Error: Missing peer name. Usage: agora peers add <name> --pubkey <pubkey> [--url <url> --token <token>]');
123
- process.exit(1);
124
- }
125
- const name = args[0];
126
- const configPath = getConfigPath(options);
127
- if (!existsSync(configPath)) {
128
- console.error('Error: Config file not found. Run `agora init` first.');
129
- process.exit(1);
130
- }
131
- const url = options.url;
132
- const token = options.token;
133
- const pubkey = options.pubkey;
134
- if (!pubkey) {
135
- console.error('Error: Missing required --pubkey option.');
136
- process.exit(1);
137
- }
138
- // Validate that if one of url/token is provided, both must be
139
- if ((url && !token) || (!url && token)) {
140
- console.error('Error: Both --url and --token must be provided together.');
141
- process.exit(1);
142
- }
143
- // Check if we have HTTP transport or relay
144
- const config = loadPeerConfig(configPath);
145
- const hasHttpConfig = url && token;
146
- const hasRelay = config.relay;
147
- if (!hasHttpConfig && !hasRelay) {
148
- console.error('Error: Either (--url and --token) must be provided, or relay must be configured in config file.');
149
- process.exit(1);
150
- }
151
- // Add the peer
152
- config.peers[name] = {
153
- publicKey: pubkey,
154
- name, // Set name to match the key for consistency
155
- };
156
- if (url && token) {
157
- config.peers[name].url = url;
158
- config.peers[name].token = token;
159
- }
160
- savePeerConfig(configPath, config);
161
- const outputData = {
162
- status: 'added',
163
- name,
164
- publicKey: pubkey
165
- };
166
- if (url) {
167
- outputData.url = url;
168
- }
169
- output(outputData, options.pretty || false);
251
+ if (args.length < 1) {
252
+ console.error("Error: Missing peer name. Usage: agora peers add <name> --pubkey <pubkey> [--url <url> --token <token>]");
253
+ process.exit(1);
254
+ }
255
+ const name = args[0];
256
+ const configPath = getConfigPath(options);
257
+ if (!existsSync(configPath)) {
258
+ console.error("Error: Config file not found. Run `agora init` first.");
259
+ process.exit(1);
260
+ }
261
+ const url = options.url;
262
+ const token = options.token;
263
+ const pubkey = options.pubkey;
264
+ if (!pubkey) {
265
+ console.error("Error: Missing required --pubkey option.");
266
+ process.exit(1);
267
+ }
268
+ if (url && !token || !url && token) {
269
+ console.error("Error: Both --url and --token must be provided together.");
270
+ process.exit(1);
271
+ }
272
+ const config = loadPeerConfig(configPath);
273
+ const hasHttpConfig = url && token;
274
+ const hasRelay = config.relay;
275
+ if (!hasHttpConfig && !hasRelay) {
276
+ console.error("Error: Either (--url and --token) must be provided, or relay must be configured in config file.");
277
+ process.exit(1);
278
+ }
279
+ config.peers[name] = {
280
+ publicKey: pubkey,
281
+ name
282
+ // Set name to match the key for consistency
283
+ };
284
+ if (url && token) {
285
+ config.peers[name].url = url;
286
+ config.peers[name].token = token;
287
+ }
288
+ savePeerConfig(configPath, config);
289
+ const outputData = {
290
+ status: "added",
291
+ name,
292
+ publicKey: pubkey
293
+ };
294
+ if (url) {
295
+ outputData.url = url;
296
+ }
297
+ output(outputData, options.pretty || false);
170
298
  }
171
- /**
172
- * Handle the `agora peers list` command.
173
- */
174
299
  function handlePeersList(options) {
175
- const configPath = getConfigPath(options);
176
- if (!existsSync(configPath)) {
177
- console.error('Error: Config file not found. Run `agora init` first.');
178
- process.exit(1);
179
- }
180
- const config = loadPeerConfig(configPath);
181
- const peers = Object.entries(config.peers).map(([key, peer]) => ({
182
- name: peer.name || key,
183
- url: peer.url,
184
- publicKey: peer.publicKey,
185
- }));
186
- output({ peers }, options.pretty || false);
300
+ const configPath = getConfigPath(options);
301
+ if (!existsSync(configPath)) {
302
+ console.error("Error: Config file not found. Run `agora init` first.");
303
+ process.exit(1);
304
+ }
305
+ const config = loadPeerConfig(configPath);
306
+ const peers = Object.entries(config.peers).map(([key, peer]) => ({
307
+ name: peer.name || key,
308
+ url: peer.url,
309
+ publicKey: peer.publicKey
310
+ }));
311
+ output({ peers }, options.pretty || false);
187
312
  }
188
- /**
189
- * Handle the `agora peers remove` command.
190
- */
191
313
  function handlePeersRemove(args, options) {
192
- if (args.length < 1) {
193
- console.error('Error: Missing peer name. Usage: agora peers remove <name>');
194
- process.exit(1);
195
- }
196
- const name = args[0];
197
- const configPath = getConfigPath(options);
198
- if (!existsSync(configPath)) {
199
- console.error('Error: Config file not found. Run `agora init` first.');
200
- process.exit(1);
201
- }
202
- const config = loadPeerConfig(configPath);
203
- if (!config.peers[name]) {
204
- console.error(`Error: Peer '${name}' not found.`);
205
- process.exit(1);
206
- }
207
- delete config.peers[name];
208
- savePeerConfig(configPath, config);
209
- output({
210
- status: 'removed',
211
- name
212
- }, options.pretty || false);
314
+ if (args.length < 1) {
315
+ console.error("Error: Missing peer name. Usage: agora peers remove <name>");
316
+ process.exit(1);
317
+ }
318
+ const name = args[0];
319
+ const configPath = getConfigPath(options);
320
+ if (!existsSync(configPath)) {
321
+ console.error("Error: Config file not found. Run `agora init` first.");
322
+ process.exit(1);
323
+ }
324
+ const config = loadPeerConfig(configPath);
325
+ if (!config.peers[name]) {
326
+ console.error(`Error: Peer '${name}' not found.`);
327
+ process.exit(1);
328
+ }
329
+ delete config.peers[name];
330
+ savePeerConfig(configPath, config);
331
+ output({
332
+ status: "removed",
333
+ name
334
+ }, options.pretty || false);
213
335
  }
214
- /**
215
- * Handle the `agora peers discover` command.
216
- */
217
336
  async function handlePeersDiscover(options) {
218
- const configPath = getConfigPath(options);
219
- if (!existsSync(configPath)) {
220
- console.error('Error: Config file not found. Run `agora init` first.');
221
- process.exit(1);
222
- }
223
- const config = loadPeerConfig(configPath);
224
- // Determine relay configuration
225
- let relayUrl;
226
- let relayPublicKey;
227
- if (options.relay) {
228
- // Use custom relay from command line
229
- relayUrl = options.relay;
230
- relayPublicKey = options['relay-pubkey'];
231
- }
232
- else if (config.relay) {
233
- // Use relay from config
234
- relayUrl = typeof config.relay === 'string' ? config.relay : config.relay.url;
235
- // TODO: Add relayPublicKey to config schema in future
236
- relayPublicKey = undefined;
237
- }
238
- else {
239
- // Use default bootstrap relay
240
- const bootstrap = getDefaultBootstrapRelay();
241
- relayUrl = bootstrap.relayUrl;
242
- relayPublicKey = bootstrap.relayPublicKey;
243
- }
244
- // Parse filters
245
- const filters = {};
246
- if (options['active-within']) {
247
- const ms = parseInt(options['active-within'], 10);
248
- if (isNaN(ms) || ms <= 0) {
249
- console.error('Error: --active-within must be a positive number (milliseconds)');
250
- process.exit(1);
251
- }
252
- filters.activeWithin = ms;
253
- }
254
- if (options.limit) {
255
- const limit = parseInt(options.limit, 10);
256
- if (isNaN(limit) || limit <= 0) {
257
- console.error('Error: --limit must be a positive number');
258
- process.exit(1);
259
- }
260
- filters.limit = limit;
261
- }
262
- // Resolve broadcast name
263
- const broadcastName = resolveBroadcastName(config, undefined);
264
- // Connect to relay
265
- const relayClient = new RelayClient({
266
- relayUrl,
267
- publicKey: config.identity.publicKey,
268
- privateKey: config.identity.privateKey,
269
- name: broadcastName,
337
+ const configPath = getConfigPath(options);
338
+ if (!existsSync(configPath)) {
339
+ console.error("Error: Config file not found. Run `agora init` first.");
340
+ process.exit(1);
341
+ }
342
+ const config = loadPeerConfig(configPath);
343
+ let relayUrl;
344
+ let relayPublicKey;
345
+ if (options.relay) {
346
+ relayUrl = options.relay;
347
+ relayPublicKey = options["relay-pubkey"];
348
+ } else if (config.relay) {
349
+ relayUrl = typeof config.relay === "string" ? config.relay : config.relay.url;
350
+ relayPublicKey = void 0;
351
+ } else {
352
+ const bootstrap = getDefaultBootstrapRelay();
353
+ relayUrl = bootstrap.relayUrl;
354
+ relayPublicKey = bootstrap.relayPublicKey;
355
+ }
356
+ const filters = {};
357
+ if (options["active-within"]) {
358
+ const ms = parseInt(options["active-within"], 10);
359
+ if (isNaN(ms) || ms <= 0) {
360
+ console.error("Error: --active-within must be a positive number (milliseconds)");
361
+ process.exit(1);
362
+ }
363
+ filters.activeWithin = ms;
364
+ }
365
+ if (options.limit) {
366
+ const limit = parseInt(options.limit, 10);
367
+ if (isNaN(limit) || limit <= 0) {
368
+ console.error("Error: --limit must be a positive number");
369
+ process.exit(1);
370
+ }
371
+ filters.limit = limit;
372
+ }
373
+ const broadcastName = resolveBroadcastName(config, void 0);
374
+ const relayClient = new RelayClient({
375
+ relayUrl,
376
+ publicKey: config.identity.publicKey,
377
+ privateKey: config.identity.privateKey,
378
+ name: broadcastName
379
+ });
380
+ try {
381
+ await relayClient.connect();
382
+ const discoveryService = new PeerDiscoveryService({
383
+ publicKey: config.identity.publicKey,
384
+ privateKey: config.identity.privateKey,
385
+ relayClient,
386
+ relayPublicKey
270
387
  });
271
- try {
272
- // Connect
273
- await relayClient.connect();
274
- // Create discovery service
275
- const discoveryService = new PeerDiscoveryService({
276
- publicKey: config.identity.publicKey,
277
- privateKey: config.identity.privateKey,
278
- relayClient,
279
- relayPublicKey,
280
- });
281
- // Discover peers
282
- const peerList = await discoveryService.discoverViaRelay(Object.keys(filters).length > 0 ? filters : undefined);
283
- if (!peerList) {
284
- output({
285
- status: 'no_response',
286
- message: 'No response from relay',
287
- }, options.pretty || false);
288
- process.exit(1);
289
- }
290
- // Save to config if requested
291
- if (options.save) {
292
- let savedCount = 0;
293
- for (const peer of peerList.peers) {
294
- // Only save if not already in config
295
- const existing = Object.values(config.peers).find(p => p.publicKey === peer.publicKey);
296
- if (!existing) {
297
- const peerName = peer.metadata?.name || `peer-${peer.publicKey.substring(0, 8)}`;
298
- config.peers[peerName] = {
299
- publicKey: peer.publicKey,
300
- name: peerName,
301
- };
302
- savedCount++;
303
- }
304
- }
305
- if (savedCount > 0) {
306
- savePeerConfig(configPath, config);
307
- }
308
- output({
309
- status: 'discovered',
310
- totalPeers: peerList.totalPeers,
311
- peersReturned: peerList.peers.length,
312
- peersSaved: savedCount,
313
- relayPublicKey: peerList.relayPublicKey,
314
- peers: peerList.peers.map(p => ({
315
- publicKey: p.publicKey,
316
- name: p.metadata?.name,
317
- version: p.metadata?.version,
318
- lastSeen: p.lastSeen,
319
- })),
320
- }, options.pretty || false);
388
+ const peerList = await discoveryService.discoverViaRelay(Object.keys(filters).length > 0 ? filters : void 0);
389
+ if (!peerList) {
390
+ output({
391
+ status: "no_response",
392
+ message: "No response from relay"
393
+ }, options.pretty || false);
394
+ process.exit(1);
395
+ }
396
+ if (options.save) {
397
+ let savedCount = 0;
398
+ for (const peer of peerList.peers) {
399
+ const existing = Object.values(config.peers).find((p) => p.publicKey === peer.publicKey);
400
+ if (!existing) {
401
+ const peerName = peer.metadata?.name || `peer-${peer.publicKey.substring(0, 8)}`;
402
+ config.peers[peerName] = {
403
+ publicKey: peer.publicKey,
404
+ name: peerName
405
+ };
406
+ savedCount++;
321
407
  }
322
- else {
323
- output({
324
- status: 'discovered',
325
- totalPeers: peerList.totalPeers,
326
- peersReturned: peerList.peers.length,
327
- relayPublicKey: peerList.relayPublicKey,
328
- peers: peerList.peers.map(p => ({
329
- publicKey: p.publicKey,
330
- name: p.metadata?.name,
331
- version: p.metadata?.version,
332
- lastSeen: p.lastSeen,
333
- })),
334
- }, options.pretty || false);
335
- }
336
- }
337
- catch (e) {
338
- console.error('Error discovering peers:', e instanceof Error ? e.message : String(e));
339
- process.exit(1);
340
- }
341
- finally {
342
- relayClient.disconnect();
343
- }
408
+ }
409
+ if (savedCount > 0) {
410
+ savePeerConfig(configPath, config);
411
+ }
412
+ output({
413
+ status: "discovered",
414
+ totalPeers: peerList.totalPeers,
415
+ peersReturned: peerList.peers.length,
416
+ peersSaved: savedCount,
417
+ relayPublicKey: peerList.relayPublicKey,
418
+ peers: peerList.peers.map((p) => ({
419
+ publicKey: p.publicKey,
420
+ name: p.metadata?.name,
421
+ version: p.metadata?.version,
422
+ lastSeen: p.lastSeen
423
+ }))
424
+ }, options.pretty || false);
425
+ } else {
426
+ output({
427
+ status: "discovered",
428
+ totalPeers: peerList.totalPeers,
429
+ peersReturned: peerList.peers.length,
430
+ relayPublicKey: peerList.relayPublicKey,
431
+ peers: peerList.peers.map((p) => ({
432
+ publicKey: p.publicKey,
433
+ name: p.metadata?.name,
434
+ version: p.metadata?.version,
435
+ lastSeen: p.lastSeen
436
+ }))
437
+ }, options.pretty || false);
438
+ }
439
+ } catch (e) {
440
+ console.error("Error discovering peers:", e instanceof Error ? e.message : String(e));
441
+ process.exit(1);
442
+ } finally {
443
+ relayClient.disconnect();
444
+ }
344
445
  }
345
- /**
346
- * Handle the `agora send` command.
347
- */
348
446
  async function handleSend(args, options) {
349
- if (args.length < 1) {
350
- console.error('Error: Missing peer name. Usage: agora send <name> <message> OR agora send <name> --type <type> --payload <json>');
351
- process.exit(1);
352
- }
353
- const name = args[0];
354
- const configPath = getConfigPath(options);
355
- if (!existsSync(configPath)) {
356
- console.error('Error: Config file not found. Run `agora init` first.');
357
- process.exit(1);
358
- }
359
- const config = loadPeerConfig(configPath);
360
- if (!config.peers[name]) {
361
- console.error(`Error: Peer '${name}' not found.`);
362
- process.exit(1);
363
- }
364
- const peer = config.peers[name];
365
- let messageType;
366
- let messagePayload;
367
- if (options.type && options.payload) {
368
- // Typed message - validate it's a valid MessageType
369
- const validTypes = ['announce', 'discover', 'request', 'response', 'publish', 'subscribe', 'verify', 'ack', 'error'];
370
- if (!validTypes.includes(options.type)) {
371
- console.error(`Error: Invalid message type '${options.type}'. Valid types: ${validTypes.join(', ')}`);
372
- process.exit(1);
373
- }
374
- messageType = options.type;
375
- try {
376
- messagePayload = JSON.parse(options.payload);
377
- }
378
- catch {
379
- console.error('Error: Invalid JSON payload.');
380
- process.exit(1);
381
- }
382
- }
383
- else {
384
- // Text message - use 'publish' type with text payload
385
- if (args.length < 2) {
386
- console.error('Error: Missing message text. Usage: agora send <name> <message>');
387
- process.exit(1);
388
- }
389
- messageType = 'publish';
390
- messagePayload = { text: args.slice(1).join(' ') };
391
- }
392
- // Determine transport method: HTTP or relay
393
- const hasHttpTransport = peer.url && peer.token;
394
- const hasRelay = config.relay;
395
- // Send the message
447
+ if (args.length < 1) {
448
+ console.error("Error: Missing peer name. Usage: agora send <name> <message> OR agora send <name> --type <type> --payload <json>");
449
+ process.exit(1);
450
+ }
451
+ const name = args[0];
452
+ const configPath = getConfigPath(options);
453
+ if (!existsSync(configPath)) {
454
+ console.error("Error: Config file not found. Run `agora init` first.");
455
+ process.exit(1);
456
+ }
457
+ const config = loadPeerConfig(configPath);
458
+ if (!config.peers[name]) {
459
+ console.error(`Error: Peer '${name}' not found.`);
460
+ process.exit(1);
461
+ }
462
+ const peer = config.peers[name];
463
+ let messageType;
464
+ let messagePayload;
465
+ if (options.type && options.payload) {
466
+ const validTypes = ["announce", "discover", "request", "response", "publish", "subscribe", "verify", "ack", "error"];
467
+ if (!validTypes.includes(options.type)) {
468
+ console.error(`Error: Invalid message type '${options.type}'. Valid types: ${validTypes.join(", ")}`);
469
+ process.exit(1);
470
+ }
471
+ messageType = options.type;
396
472
  try {
397
- if (hasHttpTransport) {
398
- // Use HTTP transport (existing behavior)
399
- // Non-null assertion: we know url and token are strings here
400
- const transportConfig = {
401
- identity: config.identity,
402
- peers: new Map([[peer.publicKey, {
403
- url: peer.url,
404
- token: peer.token,
405
- publicKey: peer.publicKey,
406
- }]]),
407
- };
408
- const result = await sendToPeer(transportConfig, peer.publicKey, messageType, messagePayload);
409
- if (result.ok) {
410
- output({
411
- status: 'sent',
412
- peer: name,
413
- type: messageType,
414
- transport: 'http',
415
- httpStatus: result.status
416
- }, options.pretty || false);
417
- }
418
- else {
419
- output({
420
- status: 'failed',
421
- peer: name,
422
- type: messageType,
423
- transport: 'http',
424
- httpStatus: result.status,
425
- error: result.error
426
- }, options.pretty || false);
427
- process.exit(1);
428
- }
429
- }
430
- else if (hasRelay && config.relay) {
431
- // Use relay transport
432
- // Extract URL from relay (string or object)
433
- const relayUrl = typeof config.relay === 'string' ? config.relay : config.relay.url;
434
- const relayConfig = {
435
- identity: config.identity,
436
- relayUrl,
437
- };
438
- const result = await sendViaRelay(relayConfig, peer.publicKey, messageType, messagePayload);
439
- if (result.ok) {
440
- output({
441
- status: 'sent',
442
- peer: name,
443
- type: messageType,
444
- transport: 'relay'
445
- }, options.pretty || false);
446
- }
447
- else {
448
- output({
449
- status: 'failed',
450
- peer: name,
451
- type: messageType,
452
- transport: 'relay',
453
- error: result.error
454
- }, options.pretty || false);
455
- process.exit(1);
456
- }
457
- }
458
- else {
459
- // Neither HTTP nor relay available
460
- console.error(`Error: Peer '${name}' unreachable. No HTTP endpoint and no relay configured.`);
461
- process.exit(1);
462
- }
463
- }
464
- catch (e) {
465
- console.error('Error sending message:', e instanceof Error ? e.message : String(e));
466
- process.exit(1);
467
- }
468
- }
469
- /**
470
- * Handle the `agora decode` command.
471
- */
472
- function handleDecode(args, options) {
473
- if (args.length < 1) {
474
- console.error('Error: Missing message. Usage: agora decode <message>');
475
- process.exit(1);
476
- }
477
- const configPath = getConfigPath(options);
478
- if (!existsSync(configPath)) {
479
- console.error('Error: Config file not found. Run `agora init` first.');
473
+ messagePayload = JSON.parse(options.payload);
474
+ } catch {
475
+ console.error("Error: Invalid JSON payload.");
476
+ process.exit(1);
477
+ }
478
+ } else {
479
+ if (args.length < 2) {
480
+ console.error("Error: Missing message text. Usage: agora send <name> <message>");
481
+ process.exit(1);
482
+ }
483
+ messageType = "publish";
484
+ messagePayload = { text: args.slice(1).join(" ") };
485
+ }
486
+ const hasHttpTransport = peer.url && peer.token;
487
+ const hasRelay = config.relay;
488
+ try {
489
+ if (hasHttpTransport) {
490
+ const transportConfig = {
491
+ identity: config.identity,
492
+ peers: /* @__PURE__ */ new Map([[peer.publicKey, {
493
+ url: peer.url,
494
+ token: peer.token,
495
+ publicKey: peer.publicKey
496
+ }]])
497
+ };
498
+ const result = await sendToPeer(
499
+ transportConfig,
500
+ peer.publicKey,
501
+ messageType,
502
+ messagePayload
503
+ );
504
+ if (result.ok) {
505
+ output({
506
+ status: "sent",
507
+ peer: name,
508
+ type: messageType,
509
+ transport: "http",
510
+ httpStatus: result.status
511
+ }, options.pretty || false);
512
+ } else {
513
+ output({
514
+ status: "failed",
515
+ peer: name,
516
+ type: messageType,
517
+ transport: "http",
518
+ httpStatus: result.status,
519
+ error: result.error
520
+ }, options.pretty || false);
480
521
  process.exit(1);
481
- }
482
- const config = loadPeerConfig(configPath);
483
- const peers = new Map();
484
- for (const [, val] of Object.entries(config.peers)) {
485
- // Only add peers with HTTP config to the map for decoding
486
- if (val.url && val.token) {
487
- peers.set(val.publicKey, {
488
- url: val.url,
489
- token: val.token,
490
- publicKey: val.publicKey,
491
- });
492
- }
493
- }
494
- const message = args.join(' ');
495
- const result = decodeInboundEnvelope(message, peers);
496
- if (result.ok) {
522
+ }
523
+ } else if (hasRelay && config.relay) {
524
+ const relayUrl = typeof config.relay === "string" ? config.relay : config.relay.url;
525
+ const relayConfig = {
526
+ identity: config.identity,
527
+ relayUrl
528
+ };
529
+ const result = await sendViaRelay(
530
+ relayConfig,
531
+ peer.publicKey,
532
+ messageType,
533
+ messagePayload
534
+ );
535
+ if (result.ok) {
497
536
  output({
498
- status: 'verified',
499
- sender: result.envelope.sender,
500
- type: result.envelope.type,
501
- payload: result.envelope.payload,
502
- id: result.envelope.id,
503
- timestamp: result.envelope.timestamp,
504
- inReplyTo: result.envelope.inReplyTo || null,
537
+ status: "sent",
538
+ peer: name,
539
+ type: messageType,
540
+ transport: "relay"
505
541
  }, options.pretty || false);
506
- }
507
- else {
542
+ } else {
508
543
  output({
509
- status: 'failed',
510
- reason: result.reason,
544
+ status: "failed",
545
+ peer: name,
546
+ type: messageType,
547
+ transport: "relay",
548
+ error: result.error
511
549
  }, options.pretty || false);
512
550
  process.exit(1);
551
+ }
552
+ } else {
553
+ console.error(`Error: Peer '${name}' unreachable. No HTTP endpoint and no relay configured.`);
554
+ process.exit(1);
513
555
  }
556
+ } catch (e) {
557
+ console.error("Error sending message:", e instanceof Error ? e.message : String(e));
558
+ process.exit(1);
559
+ }
514
560
  }
515
- /**
516
- * Handle the `agora status` command.
517
- */
518
- function handleStatus(options) {
519
- const configPath = getConfigPath(options);
520
- if (!existsSync(configPath)) {
521
- console.error('Error: Config file not found. Run `agora init` first.');
522
- process.exit(1);
523
- }
524
- const config = loadPeerConfig(configPath);
525
- const peerCount = Object.keys(config.peers).length;
561
+ function handleDecode(args, options) {
562
+ if (args.length < 1) {
563
+ console.error("Error: Missing message. Usage: agora decode <message>");
564
+ process.exit(1);
565
+ }
566
+ const configPath = getConfigPath(options);
567
+ if (!existsSync(configPath)) {
568
+ console.error("Error: Config file not found. Run `agora init` first.");
569
+ process.exit(1);
570
+ }
571
+ const config = loadPeerConfig(configPath);
572
+ const peers = /* @__PURE__ */ new Map();
573
+ for (const [, val] of Object.entries(config.peers)) {
574
+ if (val.url && val.token) {
575
+ peers.set(val.publicKey, {
576
+ url: val.url,
577
+ token: val.token,
578
+ publicKey: val.publicKey
579
+ });
580
+ }
581
+ }
582
+ const message = args.join(" ");
583
+ const result = decodeInboundEnvelope(message, peers);
584
+ if (result.ok) {
585
+ output({
586
+ status: "verified",
587
+ sender: result.envelope.sender,
588
+ type: result.envelope.type,
589
+ payload: result.envelope.payload,
590
+ id: result.envelope.id,
591
+ timestamp: result.envelope.timestamp,
592
+ inReplyTo: result.envelope.inReplyTo || null
593
+ }, options.pretty || false);
594
+ } else {
526
595
  output({
527
- identity: config.identity.publicKey,
528
- configPath,
529
- relay: config.relay || 'not configured',
530
- peerCount,
531
- peers: Object.keys(config.peers),
596
+ status: "failed",
597
+ reason: result.reason
532
598
  }, options.pretty || false);
599
+ process.exit(1);
600
+ }
601
+ }
602
+ function handleStatus(options) {
603
+ const configPath = getConfigPath(options);
604
+ if (!existsSync(configPath)) {
605
+ console.error("Error: Config file not found. Run `agora init` first.");
606
+ process.exit(1);
607
+ }
608
+ const config = loadPeerConfig(configPath);
609
+ const peerCount = Object.keys(config.peers).length;
610
+ output({
611
+ identity: config.identity.publicKey,
612
+ configPath,
613
+ relay: config.relay || "not configured",
614
+ peerCount,
615
+ peers: Object.keys(config.peers)
616
+ }, options.pretty || false);
533
617
  }
534
- /**
535
- * Handle the `agora announce` command.
536
- * Broadcasts an announce message to all configured peers.
537
- */
538
618
  async function handleAnnounce(options) {
539
- const configPath = getConfigPath(options);
540
- if (!existsSync(configPath)) {
541
- console.error('Error: Config file not found. Run `agora init` first.');
542
- process.exit(1);
543
- }
544
- const config = loadPeerConfig(configPath);
545
- const peerCount = Object.keys(config.peers).length;
546
- if (peerCount === 0) {
547
- console.error('Error: No peers configured. Use `agora peers add` to add peers first.');
548
- process.exit(1);
549
- }
550
- // Create announce payload
551
- const announcePayload = {
552
- capabilities: [],
553
- metadata: {
554
- name: options.name || 'agora-node',
555
- version: options.version || '0.1.0',
556
- },
557
- };
558
- // Send announce to all peers
559
- const results = [];
560
- for (const [name, peer] of Object.entries(config.peers)) {
561
- const hasHttpTransport = peer.url && peer.token;
562
- const hasRelay = config.relay;
563
- try {
564
- if (hasHttpTransport) {
565
- // Use HTTP transport
566
- const peers = new Map();
567
- peers.set(peer.publicKey, {
568
- url: peer.url,
569
- token: peer.token,
570
- publicKey: peer.publicKey,
571
- });
572
- const transportConfig = {
573
- identity: config.identity,
574
- peers,
575
- };
576
- const result = await sendToPeer(transportConfig, peer.publicKey, 'announce', announcePayload);
577
- if (result.ok) {
578
- results.push({
579
- peer: name,
580
- status: 'sent',
581
- transport: 'http',
582
- httpStatus: result.status,
583
- });
584
- }
585
- else {
586
- results.push({
587
- peer: name,
588
- status: 'failed',
589
- transport: 'http',
590
- httpStatus: result.status,
591
- error: result.error,
592
- });
593
- }
594
- }
595
- else if (hasRelay && config.relay) {
596
- // Use relay transport
597
- // Extract URL from relay (string or object)
598
- const relayUrl = typeof config.relay === 'string' ? config.relay : config.relay.url;
599
- const relayConfig = {
600
- identity: config.identity,
601
- relayUrl,
602
- };
603
- const result = await sendViaRelay(relayConfig, peer.publicKey, 'announce', announcePayload);
604
- if (result.ok) {
605
- results.push({
606
- peer: name,
607
- status: 'sent',
608
- transport: 'relay',
609
- });
610
- }
611
- else {
612
- results.push({
613
- peer: name,
614
- status: 'failed',
615
- transport: 'relay',
616
- error: result.error,
617
- });
618
- }
619
- }
620
- else {
621
- results.push({
622
- peer: name,
623
- status: 'unreachable',
624
- error: 'No HTTP endpoint and no relay configured',
625
- });
626
- }
619
+ const configPath = getConfigPath(options);
620
+ if (!existsSync(configPath)) {
621
+ console.error("Error: Config file not found. Run `agora init` first.");
622
+ process.exit(1);
623
+ }
624
+ const config = loadPeerConfig(configPath);
625
+ const peerCount = Object.keys(config.peers).length;
626
+ if (peerCount === 0) {
627
+ console.error("Error: No peers configured. Use `agora peers add` to add peers first.");
628
+ process.exit(1);
629
+ }
630
+ const announcePayload = {
631
+ capabilities: [],
632
+ metadata: {
633
+ name: options.name || "agora-node",
634
+ version: options.version || "0.1.0"
635
+ }
636
+ };
637
+ const results = [];
638
+ for (const [name, peer] of Object.entries(config.peers)) {
639
+ const hasHttpTransport = peer.url && peer.token;
640
+ const hasRelay = config.relay;
641
+ try {
642
+ if (hasHttpTransport) {
643
+ const peers = /* @__PURE__ */ new Map();
644
+ peers.set(peer.publicKey, {
645
+ url: peer.url,
646
+ token: peer.token,
647
+ publicKey: peer.publicKey
648
+ });
649
+ const transportConfig = {
650
+ identity: config.identity,
651
+ peers
652
+ };
653
+ const result = await sendToPeer(
654
+ transportConfig,
655
+ peer.publicKey,
656
+ "announce",
657
+ announcePayload
658
+ );
659
+ if (result.ok) {
660
+ results.push({
661
+ peer: name,
662
+ status: "sent",
663
+ transport: "http",
664
+ httpStatus: result.status
665
+ });
666
+ } else {
667
+ results.push({
668
+ peer: name,
669
+ status: "failed",
670
+ transport: "http",
671
+ httpStatus: result.status,
672
+ error: result.error
673
+ });
627
674
  }
628
- catch (e) {
629
- results.push({
630
- peer: name,
631
- status: 'error',
632
- error: e instanceof Error ? e.message : String(e),
633
- });
675
+ } else if (hasRelay && config.relay) {
676
+ const relayUrl = typeof config.relay === "string" ? config.relay : config.relay.url;
677
+ const relayConfig = {
678
+ identity: config.identity,
679
+ relayUrl
680
+ };
681
+ const result = await sendViaRelay(
682
+ relayConfig,
683
+ peer.publicKey,
684
+ "announce",
685
+ announcePayload
686
+ );
687
+ if (result.ok) {
688
+ results.push({
689
+ peer: name,
690
+ status: "sent",
691
+ transport: "relay"
692
+ });
693
+ } else {
694
+ results.push({
695
+ peer: name,
696
+ status: "failed",
697
+ transport: "relay",
698
+ error: result.error
699
+ });
634
700
  }
701
+ } else {
702
+ results.push({
703
+ peer: name,
704
+ status: "unreachable",
705
+ error: "No HTTP endpoint and no relay configured"
706
+ });
707
+ }
708
+ } catch (e) {
709
+ results.push({
710
+ peer: name,
711
+ status: "error",
712
+ error: e instanceof Error ? e.message : String(e)
713
+ });
635
714
  }
636
- output({ results }, options.pretty || false);
715
+ }
716
+ output({ results }, options.pretty || false);
637
717
  }
638
- /**
639
- * Handle the `agora diagnose` command.
640
- * Run diagnostic checks on a peer (ping, workspace, tools).
641
- */
642
718
  async function handleDiagnose(args, options) {
643
- if (args.length < 1) {
644
- console.error('Error: Missing peer name. Usage: agora diagnose <name> [--checks <comma-separated-list>]');
645
- process.exit(1);
646
- }
647
- const name = args[0];
648
- const configPath = getConfigPath(options);
649
- if (!existsSync(configPath)) {
650
- console.error('Error: Config file not found. Run `agora init` first.');
651
- process.exit(1);
652
- }
653
- const config = loadPeerConfig(configPath);
654
- if (!config.peers[name]) {
655
- console.error(`Error: Peer '${name}' not found.`);
656
- process.exit(1);
657
- }
658
- const peer = config.peers[name];
659
- if (!peer.url) {
660
- console.error(`Error: Peer '${name}' has no URL configured. Cannot diagnose.`);
661
- process.exit(1);
662
- }
663
- // Parse checks parameter
664
- const checksParam = options.checks || 'ping';
665
- const requestedChecks = checksParam.split(',').map(c => c.trim());
666
- // Validate check types
667
- const validChecks = ['ping', 'workspace', 'tools'];
668
- for (const check of requestedChecks) {
669
- if (!validChecks.includes(check)) {
670
- console.error(`Error: Invalid check type '${check}'. Valid checks: ${validChecks.join(', ')}`);
671
- process.exit(1);
672
- }
673
- }
674
- const result = {
675
- peer: name,
676
- status: 'unknown',
677
- checks: {},
678
- timestamp: new Date().toISOString(),
719
+ if (args.length < 1) {
720
+ console.error("Error: Missing peer name. Usage: agora diagnose <name> [--checks <comma-separated-list>]");
721
+ process.exit(1);
722
+ }
723
+ const name = args[0];
724
+ const configPath = getConfigPath(options);
725
+ if (!existsSync(configPath)) {
726
+ console.error("Error: Config file not found. Run `agora init` first.");
727
+ process.exit(1);
728
+ }
729
+ const config = loadPeerConfig(configPath);
730
+ if (!config.peers[name]) {
731
+ console.error(`Error: Peer '${name}' not found.`);
732
+ process.exit(1);
733
+ }
734
+ const peer = config.peers[name];
735
+ if (!peer.url) {
736
+ console.error(`Error: Peer '${name}' has no URL configured. Cannot diagnose.`);
737
+ process.exit(1);
738
+ }
739
+ const checksParam = options.checks || "ping";
740
+ const requestedChecks = checksParam.split(",").map((c) => c.trim());
741
+ const validChecks = ["ping", "workspace", "tools"];
742
+ for (const check of requestedChecks) {
743
+ if (!validChecks.includes(check)) {
744
+ console.error(`Error: Invalid check type '${check}'. Valid checks: ${validChecks.join(", ")}`);
745
+ process.exit(1);
746
+ }
747
+ }
748
+ const result = {
749
+ peer: name,
750
+ status: "unknown",
751
+ checks: {},
752
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
753
+ };
754
+ if (requestedChecks.includes("ping")) {
755
+ const startTime = Date.now();
756
+ try {
757
+ const controller = new AbortController();
758
+ const timeout = setTimeout(() => controller.abort(), 1e4);
759
+ const response = await fetch(peer.url, {
760
+ method: "GET",
761
+ headers: peer.token ? { "Authorization": `Bearer ${peer.token}` } : {},
762
+ signal: controller.signal
763
+ });
764
+ clearTimeout(timeout);
765
+ const latency = Date.now() - startTime;
766
+ if (response.ok || response.status === 404 || response.status === 405) {
767
+ result.checks.ping = { ok: true, latency_ms: latency };
768
+ } else {
769
+ result.checks.ping = { ok: false, latency_ms: latency, error: `HTTP ${response.status}` };
770
+ }
771
+ } catch (err) {
772
+ const latency = Date.now() - startTime;
773
+ result.checks.ping = {
774
+ ok: false,
775
+ latency_ms: latency,
776
+ error: err instanceof Error ? err.message : String(err)
777
+ };
778
+ }
779
+ }
780
+ if (requestedChecks.includes("workspace")) {
781
+ result.checks.workspace = {
782
+ ok: false,
783
+ implemented: false,
784
+ error: "Workspace check requires peer diagnostic protocol support"
679
785
  };
680
- // Run ping check
681
- if (requestedChecks.includes('ping')) {
682
- const startTime = Date.now();
683
- try {
684
- // Add timeout to prevent hanging on unreachable peers
685
- const controller = new AbortController();
686
- const timeout = setTimeout(() => controller.abort(), 10000);
687
- const response = await fetch(peer.url, {
688
- method: 'GET',
689
- headers: peer.token ? { 'Authorization': `Bearer ${peer.token}` } : {},
690
- signal: controller.signal,
691
- });
692
- clearTimeout(timeout);
693
- const latency = Date.now() - startTime;
694
- if (response.ok || response.status === 404 || response.status === 405) {
695
- // 404 or 405 means the endpoint exists but GET isn't supported - that's OK for a ping
696
- result.checks.ping = { ok: true, latency_ms: latency };
697
- }
698
- else {
699
- result.checks.ping = { ok: false, latency_ms: latency, error: `HTTP ${response.status}` };
700
- }
701
- }
702
- catch (err) {
703
- const latency = Date.now() - startTime;
704
- result.checks.ping = {
705
- ok: false,
706
- latency_ms: latency,
707
- error: err instanceof Error ? err.message : String(err)
708
- };
709
- }
710
- }
711
- // Run workspace check
712
- if (requestedChecks.includes('workspace')) {
713
- // This is a placeholder - actual implementation would depend on peer's diagnostic protocol
714
- result.checks.workspace = {
715
- ok: false,
716
- implemented: false,
717
- error: 'Workspace check requires peer diagnostic protocol support'
718
- };
719
- }
720
- // Run tools check
721
- if (requestedChecks.includes('tools')) {
722
- // This is a placeholder - actual implementation would depend on peer's diagnostic protocol
723
- result.checks.tools = {
724
- ok: false,
725
- implemented: false,
726
- error: 'Tools check requires peer diagnostic protocol support'
727
- };
728
- }
729
- // Determine overall status - only consider implemented checks
730
- const implementedChecks = Object.values(result.checks).filter(check => check.implemented !== false);
731
- if (implementedChecks.length === 0) {
732
- result.status = 'unknown';
733
- }
734
- else {
735
- const allOk = implementedChecks.every(check => check.ok);
736
- const anyOk = implementedChecks.some(check => check.ok);
737
- result.status = allOk ? 'healthy' : anyOk ? 'degraded' : 'unhealthy';
738
- }
739
- output(result, options.pretty || false);
786
+ }
787
+ if (requestedChecks.includes("tools")) {
788
+ result.checks.tools = {
789
+ ok: false,
790
+ implemented: false,
791
+ error: "Tools check requires peer diagnostic protocol support"
792
+ };
793
+ }
794
+ const implementedChecks = Object.values(result.checks).filter(
795
+ (check) => check.implemented !== false
796
+ );
797
+ if (implementedChecks.length === 0) {
798
+ result.status = "unknown";
799
+ } else {
800
+ const allOk = implementedChecks.every((check) => check.ok);
801
+ const anyOk = implementedChecks.some((check) => check.ok);
802
+ result.status = allOk ? "healthy" : anyOk ? "degraded" : "unhealthy";
803
+ }
804
+ output(result, options.pretty || false);
740
805
  }
741
- /**
742
- * Handle the `agora serve` command.
743
- * Starts a persistent WebSocket server for incoming peer connections.
744
- */
745
806
  async function handleServe(options) {
746
- const configPath = getConfigPath(options);
747
- if (!existsSync(configPath)) {
748
- console.error('Error: Config file not found. Run `agora init` first.');
749
- process.exit(1);
750
- }
751
- const config = loadPeerConfig(configPath);
752
- const port = parseInt(options.port || '9473', 10);
753
- // Validate port
754
- if (isNaN(port) || port < 1 || port > 65535) {
755
- console.error(`Error: Invalid port number '${options.port}'. Port must be between 1 and 65535.`);
756
- process.exit(1);
757
- }
758
- // Resolve server name using priority: CLI --name, config.relay.name, config.identity.name, default
759
- const serverName = resolveBroadcastName(config, options.name) || 'agora-server';
760
- // Create announce payload
761
- const announcePayload = {
762
- capabilities: [],
763
- metadata: {
764
- name: serverName,
765
- version: '0.1.0',
766
- },
767
- };
768
- // Create and configure PeerServer
769
- const server = new PeerServer(config.identity, announcePayload);
770
- // Setup event listeners
771
- server.on('peer-connected', (publicKey, peer) => {
772
- const peerName = peer.metadata?.name || publicKey.substring(0, 16);
773
- console.log(`[${new Date().toISOString()}] Peer connected: ${peerName} (${publicKey})`);
774
- });
775
- server.on('peer-disconnected', (publicKey) => {
776
- console.log(`[${new Date().toISOString()}] Peer disconnected: ${publicKey}`);
777
- });
778
- server.on('message-received', (envelope, fromPublicKey) => {
779
- console.log(`[${new Date().toISOString()}] Message from ${fromPublicKey}:`);
780
- console.log(JSON.stringify({
781
- id: envelope.id,
782
- type: envelope.type,
783
- sender: envelope.sender,
784
- timestamp: envelope.timestamp,
785
- payload: envelope.payload,
786
- }, null, 2));
807
+ const configPath = getConfigPath(options);
808
+ if (!existsSync(configPath)) {
809
+ console.error("Error: Config file not found. Run `agora init` first.");
810
+ process.exit(1);
811
+ }
812
+ const config = loadPeerConfig(configPath);
813
+ const port = parseInt(options.port || "9473", 10);
814
+ if (isNaN(port) || port < 1 || port > 65535) {
815
+ console.error(`Error: Invalid port number '${options.port}'. Port must be between 1 and 65535.`);
816
+ process.exit(1);
817
+ }
818
+ const serverName = resolveBroadcastName(config, options.name) || "agora-server";
819
+ const announcePayload = {
820
+ capabilities: [],
821
+ metadata: {
822
+ name: serverName,
823
+ version: "0.1.0"
824
+ }
825
+ };
826
+ const server = new PeerServer(config.identity, announcePayload);
827
+ server.on("peer-connected", (publicKey, peer) => {
828
+ const peerName = peer.metadata?.name || publicKey.substring(0, 16);
829
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Peer connected: ${peerName} (${publicKey})`);
830
+ });
831
+ server.on("peer-disconnected", (publicKey) => {
832
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Peer disconnected: ${publicKey}`);
833
+ });
834
+ server.on("message-received", (envelope, fromPublicKey) => {
835
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Message from ${fromPublicKey}:`);
836
+ console.log(JSON.stringify({
837
+ id: envelope.id,
838
+ type: envelope.type,
839
+ sender: envelope.sender,
840
+ timestamp: envelope.timestamp,
841
+ payload: envelope.payload
842
+ }, null, 2));
843
+ });
844
+ server.on("error", (error) => {
845
+ console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] Error:`, error.message);
846
+ });
847
+ try {
848
+ await server.start(port);
849
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Agora server started`);
850
+ console.log(` Name: ${serverName}`);
851
+ console.log(` Public Key: ${config.identity.publicKey}`);
852
+ console.log(` WebSocket Port: ${port}`);
853
+ console.log(` Listening for peer connections...`);
854
+ console.log("");
855
+ console.log("Press Ctrl+C to stop the server");
856
+ process.on("SIGINT", async () => {
857
+ console.log(`
858
+ [${(/* @__PURE__ */ new Date()).toISOString()}] Shutting down server...`);
859
+ await server.stop();
860
+ console.log("Server stopped");
861
+ process.exit(0);
787
862
  });
788
- server.on('error', (error) => {
789
- console.error(`[${new Date().toISOString()}] Error:`, error.message);
863
+ process.on("SIGTERM", async () => {
864
+ console.log(`
865
+ [${(/* @__PURE__ */ new Date()).toISOString()}] Shutting down server...`);
866
+ await server.stop();
867
+ console.log("Server stopped");
868
+ process.exit(0);
790
869
  });
791
- // Start the server
792
- try {
793
- await server.start(port);
794
- console.log(`[${new Date().toISOString()}] Agora server started`);
795
- console.log(` Name: ${serverName}`);
796
- console.log(` Public Key: ${config.identity.publicKey}`);
797
- console.log(` WebSocket Port: ${port}`);
798
- console.log(` Listening for peer connections...`);
799
- console.log('');
800
- console.log('Press Ctrl+C to stop the server');
801
- // Keep the process alive
802
- process.on('SIGINT', async () => {
803
- console.log(`\n[${new Date().toISOString()}] Shutting down server...`);
804
- await server.stop();
805
- console.log('Server stopped');
806
- process.exit(0);
807
- });
808
- process.on('SIGTERM', async () => {
809
- console.log(`\n[${new Date().toISOString()}] Shutting down server...`);
810
- await server.stop();
811
- console.log('Server stopped');
812
- process.exit(0);
813
- });
814
- }
815
- catch (error) {
816
- console.error('Failed to start server:', error instanceof Error ? error.message : String(error));
817
- process.exit(1);
818
- }
870
+ } catch (error) {
871
+ console.error("Failed to start server:", error instanceof Error ? error.message : String(error));
872
+ process.exit(1);
873
+ }
819
874
  }
820
- /**
821
- * Handle the `agora relay` command.
822
- * Starts a WebSocket relay server for routing messages between agents.
823
- */
824
875
  async function handleRelay(options) {
825
- const port = parseInt(options.port || '9474', 10);
826
- // Validate port
827
- if (isNaN(port) || port < 1 || port > 65535) {
828
- console.error(`Error: Invalid port number '${options.port}'. Port must be between 1 and 65535.`);
829
- process.exit(1);
830
- }
831
- // Create and configure RelayServer
832
- const server = new RelayServer();
833
- // Setup event listeners
834
- server.on('agent-registered', (publicKey) => {
835
- console.log(`[${new Date().toISOString()}] Agent registered: ${publicKey}`);
836
- });
837
- server.on('agent-disconnected', (publicKey) => {
838
- console.log(`[${new Date().toISOString()}] Agent disconnected: ${publicKey}`);
839
- });
840
- server.on('message-relayed', (from, to, envelope) => {
841
- console.log(`[${new Date().toISOString()}] Message relayed: ${from.substring(0, 16)}... → ${to.substring(0, 16)}... (type: ${envelope.type})`);
842
- });
843
- server.on('error', (error) => {
844
- console.error(`[${new Date().toISOString()}] Error:`, error.message);
845
- });
846
- // Start the server
847
- try {
848
- await server.start(port);
849
- console.log(`[${new Date().toISOString()}] Agora relay server started`);
850
- console.log(` WebSocket Port: ${port}`);
851
- console.log(` Connected agents: 0`);
852
- console.log(` Listening for agent connections...`);
853
- console.log('');
854
- console.log('Press Ctrl+C to stop the relay');
855
- // Shared shutdown handler
856
- const shutdown = async () => {
857
- console.log(`\n[${new Date().toISOString()}] Shutting down relay...`);
858
- await server.stop();
859
- console.log('Relay stopped');
860
- process.exit(0);
861
- };
862
- // Keep the process alive
863
- process.on('SIGINT', shutdown);
864
- process.on('SIGTERM', shutdown);
865
- }
866
- catch (error) {
867
- console.error('Failed to start relay:', error instanceof Error ? error.message : String(error));
868
- process.exit(1);
869
- }
876
+ const port = parseInt(options.port || "9474", 10);
877
+ if (isNaN(port) || port < 1 || port > 65535) {
878
+ console.error(`Error: Invalid port number '${options.port}'. Port must be between 1 and 65535.`);
879
+ process.exit(1);
880
+ }
881
+ const server = new RelayServer();
882
+ server.on("agent-registered", (publicKey) => {
883
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Agent registered: ${publicKey}`);
884
+ });
885
+ server.on("agent-disconnected", (publicKey) => {
886
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Agent disconnected: ${publicKey}`);
887
+ });
888
+ server.on("message-relayed", (from, to, envelope) => {
889
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Message relayed: ${from.substring(0, 16)}... \u2192 ${to.substring(0, 16)}... (type: ${envelope.type})`);
890
+ });
891
+ server.on("error", (error) => {
892
+ console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] Error:`, error.message);
893
+ });
894
+ try {
895
+ await server.start(port);
896
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Agora relay server started`);
897
+ console.log(` WebSocket Port: ${port}`);
898
+ console.log(` Connected agents: 0`);
899
+ console.log(` Listening for agent connections...`);
900
+ console.log("");
901
+ console.log("Press Ctrl+C to stop the relay");
902
+ const shutdown = async () => {
903
+ console.log(`
904
+ [${(/* @__PURE__ */ new Date()).toISOString()}] Shutting down relay...`);
905
+ await server.stop();
906
+ console.log("Relay stopped");
907
+ process.exit(0);
908
+ };
909
+ process.on("SIGINT", shutdown);
910
+ process.on("SIGTERM", shutdown);
911
+ } catch (error) {
912
+ console.error("Failed to start relay:", error instanceof Error ? error.message : String(error));
913
+ process.exit(1);
914
+ }
870
915
  }
871
- /**
872
- * Get the reputation store file path.
873
- */
874
916
  function getReputationStorePath() {
875
- return resolve(homedir(), '.local', 'share', 'agora', 'reputation.jsonl');
917
+ return resolve(homedir(), ".local", "share", "agora", "reputation.jsonl");
876
918
  }
877
- /**
878
- * Handle the `agora reputation verify` command.
879
- * Creates a verification record for another agent's output.
880
- */
881
919
  async function handleReputationVerify(args, options) {
882
- if (!options.target || !options.domain || !options.verdict) {
883
- console.error('Error: Missing required options');
884
- console.error('Usage: agora reputation verify --target <id> --domain <domain> --verdict <correct|incorrect|disputed> --confidence <0-1> [--evidence <url>]');
885
- process.exit(1);
886
- }
887
- // Validate verdict
888
- if (!['correct', 'incorrect', 'disputed'].includes(options.verdict)) {
889
- console.error('Error: verdict must be one of: correct, incorrect, disputed');
890
- process.exit(1);
891
- }
892
- // Parse confidence
893
- const confidence = options.confidence ? parseFloat(options.confidence) : 1.0;
894
- if (isNaN(confidence) || confidence < 0 || confidence > 1) {
895
- console.error('Error: confidence must be a number between 0 and 1');
896
- process.exit(1);
897
- }
898
- // Load config
899
- const configPath = getConfigPath(options);
900
- if (!existsSync(configPath)) {
901
- console.error(`Error: Config file not found at ${configPath}. Run 'agora init' first.`);
902
- process.exit(1);
903
- }
904
- const config = loadPeerConfig(configPath);
905
- // Create verification
906
- const verification = createVerification(config.identity.publicKey, config.identity.privateKey, options.target, options.domain, options.verdict, confidence, Date.now(), options.evidence);
907
- // Save to reputation store
908
- const storePath = getReputationStorePath();
909
- const store = new ReputationStore(storePath);
910
- await store.addVerification(verification);
911
- output({
912
- status: 'verification_created',
913
- id: verification.id,
914
- verifier: verification.verifier,
915
- target: verification.target,
916
- domain: verification.domain,
917
- verdict: verification.verdict,
918
- confidence: verification.confidence,
919
- timestamp: verification.timestamp,
920
- }, options.pretty || false);
920
+ if (!options.target || !options.domain || !options.verdict) {
921
+ console.error("Error: Missing required options");
922
+ console.error("Usage: agora reputation verify --target <id> --domain <domain> --verdict <correct|incorrect|disputed> --confidence <0-1> [--evidence <url>]");
923
+ process.exit(1);
924
+ }
925
+ if (!["correct", "incorrect", "disputed"].includes(options.verdict)) {
926
+ console.error("Error: verdict must be one of: correct, incorrect, disputed");
927
+ process.exit(1);
928
+ }
929
+ const confidence = options.confidence ? parseFloat(options.confidence) : 1;
930
+ if (isNaN(confidence) || confidence < 0 || confidence > 1) {
931
+ console.error("Error: confidence must be a number between 0 and 1");
932
+ process.exit(1);
933
+ }
934
+ const configPath = getConfigPath(options);
935
+ if (!existsSync(configPath)) {
936
+ console.error(`Error: Config file not found at ${configPath}. Run 'agora init' first.`);
937
+ process.exit(1);
938
+ }
939
+ const config = loadPeerConfig(configPath);
940
+ const verification = createVerification(
941
+ config.identity.publicKey,
942
+ config.identity.privateKey,
943
+ options.target,
944
+ options.domain,
945
+ options.verdict,
946
+ confidence,
947
+ Date.now(),
948
+ options.evidence
949
+ );
950
+ const storePath = getReputationStorePath();
951
+ const store = new ReputationStore(storePath);
952
+ await store.addVerification(verification);
953
+ output({
954
+ status: "verification_created",
955
+ id: verification.id,
956
+ verifier: verification.verifier,
957
+ target: verification.target,
958
+ domain: verification.domain,
959
+ verdict: verification.verdict,
960
+ confidence: verification.confidence,
961
+ timestamp: verification.timestamp
962
+ }, options.pretty || false);
921
963
  }
922
- /**
923
- * Handle the `agora reputation commit` command.
924
- * Creates a commitment to a prediction before outcome is known.
925
- */
926
964
  async function handleReputationCommit(args, options) {
927
- if (!options.domain || !options.prediction) {
928
- console.error('Error: Missing required options');
929
- console.error('Usage: agora reputation commit --domain <domain> --prediction <text> [--expiry <milliseconds>]');
930
- console.error('Example: agora reputation commit --domain weather_forecast --prediction "It will rain tomorrow" --expiry 86400000');
931
- process.exit(1);
932
- }
933
- // Parse expiry (default 24 hours)
934
- const expiryMs = options.expiry ? parseInt(options.expiry, 10) : 86400000;
935
- if (isNaN(expiryMs) || expiryMs <= 0) {
936
- console.error('Error: expiry must be a positive number (milliseconds)');
937
- process.exit(1);
938
- }
939
- // Load config
940
- const configPath = getConfigPath(options);
941
- if (!existsSync(configPath)) {
942
- console.error(`Error: Config file not found at ${configPath}. Run 'agora init' first.`);
943
- process.exit(1);
944
- }
945
- const config = loadPeerConfig(configPath);
946
- // Create commit
947
- const commit = createCommit(config.identity.publicKey, config.identity.privateKey, options.domain, options.prediction, Date.now(), expiryMs);
948
- // Save to reputation store
949
- const storePath = getReputationStorePath();
950
- const store = new ReputationStore(storePath);
951
- await store.addCommit(commit);
952
- output({
953
- status: 'commitment_created',
954
- id: commit.id,
955
- agent: commit.agent,
956
- domain: commit.domain,
957
- commitment: commit.commitment,
958
- timestamp: commit.timestamp,
959
- expiry: commit.expiry,
960
- note: 'Store this ID to reveal the prediction after expiry',
961
- }, options.pretty || false);
965
+ if (!options.domain || !options.prediction) {
966
+ console.error("Error: Missing required options");
967
+ console.error("Usage: agora reputation commit --domain <domain> --prediction <text> [--expiry <milliseconds>]");
968
+ console.error('Example: agora reputation commit --domain weather_forecast --prediction "It will rain tomorrow" --expiry 86400000');
969
+ process.exit(1);
970
+ }
971
+ const expiryMs = options.expiry ? parseInt(options.expiry, 10) : 864e5;
972
+ if (isNaN(expiryMs) || expiryMs <= 0) {
973
+ console.error("Error: expiry must be a positive number (milliseconds)");
974
+ process.exit(1);
975
+ }
976
+ const configPath = getConfigPath(options);
977
+ if (!existsSync(configPath)) {
978
+ console.error(`Error: Config file not found at ${configPath}. Run 'agora init' first.`);
979
+ process.exit(1);
980
+ }
981
+ const config = loadPeerConfig(configPath);
982
+ const commit = createCommit(
983
+ config.identity.publicKey,
984
+ config.identity.privateKey,
985
+ options.domain,
986
+ options.prediction,
987
+ Date.now(),
988
+ expiryMs
989
+ );
990
+ const storePath = getReputationStorePath();
991
+ const store = new ReputationStore(storePath);
992
+ await store.addCommit(commit);
993
+ output({
994
+ status: "commitment_created",
995
+ id: commit.id,
996
+ agent: commit.agent,
997
+ domain: commit.domain,
998
+ commitment: commit.commitment,
999
+ timestamp: commit.timestamp,
1000
+ expiry: commit.expiry,
1001
+ note: "Store this ID to reveal the prediction after expiry"
1002
+ }, options.pretty || false);
962
1003
  }
963
- /**
964
- * Handle the `agora reputation reveal` command.
965
- * Reveals a prediction and outcome after commitment expiry.
966
- */
967
1004
  async function handleReputationReveal(args, options) {
968
- if (!options['commit-id'] || !options.prediction || !options.outcome) {
969
- console.error('Error: Missing required options');
970
- console.error('Usage: agora reputation reveal --commit-id <id> --prediction <text> --outcome <text> [--evidence <url>]');
971
- process.exit(1);
972
- }
973
- // Load config
974
- const configPath = getConfigPath(options);
975
- if (!existsSync(configPath)) {
976
- console.error(`Error: Config file not found at ${configPath}. Run 'agora init' first.`);
977
- process.exit(1);
978
- }
979
- const config = loadPeerConfig(configPath);
980
- // Load commit from store
981
- const storePath = getReputationStorePath();
982
- const store = new ReputationStore(storePath);
983
- const commit = await store.getCommit(options['commit-id']);
984
- if (!commit) {
985
- console.error(`Error: Commitment ${options['commit-id']} not found in local store`);
986
- process.exit(1);
987
- }
988
- // Create reveal
989
- const reveal = createReveal(config.identity.publicKey, config.identity.privateKey, options['commit-id'], options.prediction, options.outcome, Date.now(), options.evidence);
990
- // Verify the reveal against the commit
991
- const verification = verifyReveal(commit, reveal);
992
- if (!verification.valid) {
993
- console.error(`Error: Reveal verification failed: ${verification.reason}`);
994
- process.exit(1);
995
- }
996
- // Save to reputation store
997
- await store.addReveal(reveal);
998
- output({
999
- status: 'prediction_revealed',
1000
- id: reveal.id,
1001
- agent: reveal.agent,
1002
- commitmentId: reveal.commitmentId,
1003
- prediction: reveal.prediction,
1004
- outcome: reveal.outcome,
1005
- timestamp: reveal.timestamp,
1006
- verified: true,
1007
- }, options.pretty || false);
1005
+ if (!options["commit-id"] || !options.prediction || !options.outcome) {
1006
+ console.error("Error: Missing required options");
1007
+ console.error("Usage: agora reputation reveal --commit-id <id> --prediction <text> --outcome <text> [--evidence <url>]");
1008
+ process.exit(1);
1009
+ }
1010
+ const configPath = getConfigPath(options);
1011
+ if (!existsSync(configPath)) {
1012
+ console.error(`Error: Config file not found at ${configPath}. Run 'agora init' first.`);
1013
+ process.exit(1);
1014
+ }
1015
+ const config = loadPeerConfig(configPath);
1016
+ const storePath = getReputationStorePath();
1017
+ const store = new ReputationStore(storePath);
1018
+ const commit = await store.getCommit(options["commit-id"]);
1019
+ if (!commit) {
1020
+ console.error(`Error: Commitment ${options["commit-id"]} not found in local store`);
1021
+ process.exit(1);
1022
+ }
1023
+ const reveal = createReveal(
1024
+ config.identity.publicKey,
1025
+ config.identity.privateKey,
1026
+ options["commit-id"],
1027
+ options.prediction,
1028
+ options.outcome,
1029
+ Date.now(),
1030
+ options.evidence
1031
+ );
1032
+ const verification = verifyReveal(commit, reveal);
1033
+ if (!verification.valid) {
1034
+ console.error(`Error: Reveal verification failed: ${verification.reason}`);
1035
+ process.exit(1);
1036
+ }
1037
+ await store.addReveal(reveal);
1038
+ output({
1039
+ status: "prediction_revealed",
1040
+ id: reveal.id,
1041
+ agent: reveal.agent,
1042
+ commitmentId: reveal.commitmentId,
1043
+ prediction: reveal.prediction,
1044
+ outcome: reveal.outcome,
1045
+ timestamp: reveal.timestamp,
1046
+ verified: true
1047
+ }, options.pretty || false);
1008
1048
  }
1009
- /**
1010
- * Handle the `agora reputation query` command.
1011
- * Queries reputation score for an agent in a domain.
1012
- */
1013
1049
  async function handleReputationQuery(args, options) {
1014
- if (!options.domain) {
1015
- console.error('Error: Missing required option: --domain');
1016
- console.error('Usage: agora reputation query --domain <domain> [--agent <pubkey>]');
1017
- console.error('If --agent is omitted, shows reputation for current agent');
1018
- process.exit(1);
1019
- }
1020
- // Load config
1021
- const configPath = getConfigPath(options);
1022
- if (!existsSync(configPath)) {
1023
- console.error(`Error: Config file not found at ${configPath}. Run 'agora init' first.`);
1024
- process.exit(1);
1025
- }
1026
- const config = loadPeerConfig(configPath);
1027
- const agent = options.agent || config.identity.publicKey;
1028
- // Load reputation store
1029
- const storePath = getReputationStorePath();
1030
- const store = new ReputationStore(storePath);
1031
- const verifications = await store.getVerificationsByDomain(options.domain);
1032
- // Filter verifications for this agent
1033
- const agentVerifications = verifications.filter(v => v.target === agent);
1034
- // Compute trust score
1035
- const score = computeTrustScore(agent, options.domain, agentVerifications, Date.now());
1036
- output({
1037
- agent: score.agent,
1038
- domain: score.domain,
1039
- score: score.score,
1040
- verificationCount: score.verificationCount,
1041
- lastVerified: score.lastVerified,
1042
- lastVerifiedDate: score.lastVerified > 0 ? new Date(score.lastVerified).toISOString() : 'never',
1043
- topVerifiers: score.topVerifiers,
1044
- }, options.pretty || false);
1050
+ if (!options.domain) {
1051
+ console.error("Error: Missing required option: --domain");
1052
+ console.error("Usage: agora reputation query --domain <domain> [--agent <pubkey>]");
1053
+ console.error("If --agent is omitted, shows reputation for current agent");
1054
+ process.exit(1);
1055
+ }
1056
+ const configPath = getConfigPath(options);
1057
+ if (!existsSync(configPath)) {
1058
+ console.error(`Error: Config file not found at ${configPath}. Run 'agora init' first.`);
1059
+ process.exit(1);
1060
+ }
1061
+ const config = loadPeerConfig(configPath);
1062
+ const agent = options.agent || config.identity.publicKey;
1063
+ const storePath = getReputationStorePath();
1064
+ const store = new ReputationStore(storePath);
1065
+ const verifications = await store.getVerificationsByDomain(options.domain);
1066
+ const agentVerifications = verifications.filter((v) => v.target === agent);
1067
+ const score = computeTrustScore(agent, options.domain, agentVerifications, Date.now());
1068
+ output({
1069
+ agent: score.agent,
1070
+ domain: score.domain,
1071
+ score: score.score,
1072
+ verificationCount: score.verificationCount,
1073
+ lastVerified: score.lastVerified,
1074
+ lastVerifiedDate: score.lastVerified > 0 ? new Date(score.lastVerified).toISOString() : "never",
1075
+ topVerifiers: score.topVerifiers
1076
+ }, options.pretty || false);
1045
1077
  }
1046
- /**
1047
- * Parse CLI arguments and route to appropriate handler.
1048
- */
1049
1078
  async function main() {
1050
- const args = process.argv.slice(2);
1051
- if (args.length === 0) {
1052
- console.error('Usage: agora <command> [options]');
1053
- console.error('Commands: init, whoami, status, peers, announce, send, decode, serve, diagnose, relay, reputation');
1054
- console.error(' peers subcommands: add, list, remove, discover');
1055
- console.error(' reputation subcommands: verify, commit, reveal, query');
1056
- process.exit(1);
1057
- }
1058
- // Parse global options
1059
- const parsed = parseArgs({
1060
- args,
1061
- options: {
1062
- config: { type: 'string' },
1063
- pretty: { type: 'boolean' },
1064
- url: { type: 'string' },
1065
- token: { type: 'string' },
1066
- pubkey: { type: 'string' },
1067
- type: { type: 'string' },
1068
- payload: { type: 'string' },
1069
- name: { type: 'string' },
1070
- version: { type: 'string' },
1071
- port: { type: 'string' },
1072
- checks: { type: 'string' },
1073
- relay: { type: 'string' },
1074
- 'relay-pubkey': { type: 'string' },
1075
- limit: { type: 'string' },
1076
- 'active-within': { type: 'string' },
1077
- save: { type: 'boolean' },
1078
- // Reputation options
1079
- target: { type: 'string' },
1080
- domain: { type: 'string' },
1081
- verdict: { type: 'string' },
1082
- confidence: { type: 'string' },
1083
- evidence: { type: 'string' },
1084
- prediction: { type: 'string' },
1085
- expiry: { type: 'string' },
1086
- 'commit-id': { type: 'string' },
1087
- outcome: { type: 'string' },
1088
- agent: { type: 'string' },
1089
- },
1090
- strict: false,
1091
- allowPositionals: true,
1092
- });
1093
- const command = parsed.positionals[0];
1094
- const subcommand = parsed.positionals[1];
1095
- const remainingArgs = parsed.positionals.slice(2);
1096
- const options = {
1097
- config: typeof parsed.values.config === 'string' ? parsed.values.config : undefined,
1098
- pretty: typeof parsed.values.pretty === 'boolean' ? parsed.values.pretty : undefined,
1099
- type: typeof parsed.values.type === 'string' ? parsed.values.type : undefined,
1100
- payload: typeof parsed.values.payload === 'string' ? parsed.values.payload : undefined,
1101
- url: typeof parsed.values.url === 'string' ? parsed.values.url : undefined,
1102
- token: typeof parsed.values.token === 'string' ? parsed.values.token : undefined,
1103
- pubkey: typeof parsed.values.pubkey === 'string' ? parsed.values.pubkey : undefined,
1104
- name: typeof parsed.values.name === 'string' ? parsed.values.name : undefined,
1105
- version: typeof parsed.values.version === 'string' ? parsed.values.version : undefined,
1106
- port: typeof parsed.values.port === 'string' ? parsed.values.port : undefined,
1107
- checks: typeof parsed.values.checks === 'string' ? parsed.values.checks : undefined,
1108
- relay: typeof parsed.values.relay === 'string' ? parsed.values.relay : undefined,
1109
- 'relay-pubkey': typeof parsed.values['relay-pubkey'] === 'string' ? parsed.values['relay-pubkey'] : undefined,
1110
- limit: typeof parsed.values.limit === 'string' ? parsed.values.limit : undefined,
1111
- 'active-within': typeof parsed.values['active-within'] === 'string' ? parsed.values['active-within'] : undefined,
1112
- save: typeof parsed.values.save === 'boolean' ? parsed.values.save : undefined,
1113
- // Reputation options
1114
- target: typeof parsed.values.target === 'string' ? parsed.values.target : undefined,
1115
- domain: typeof parsed.values.domain === 'string' ? parsed.values.domain : undefined,
1116
- verdict: typeof parsed.values.verdict === 'string' ? parsed.values.verdict : undefined,
1117
- confidence: typeof parsed.values.confidence === 'string' ? parsed.values.confidence : undefined,
1118
- evidence: typeof parsed.values.evidence === 'string' ? parsed.values.evidence : undefined,
1119
- prediction: typeof parsed.values.prediction === 'string' ? parsed.values.prediction : undefined,
1120
- expiry: typeof parsed.values.expiry === 'string' ? parsed.values.expiry : undefined,
1121
- 'commit-id': typeof parsed.values['commit-id'] === 'string' ? parsed.values['commit-id'] : undefined,
1122
- outcome: typeof parsed.values.outcome === 'string' ? parsed.values.outcome : undefined,
1123
- agent: typeof parsed.values.agent === 'string' ? parsed.values.agent : undefined,
1124
- };
1125
- try {
1126
- switch (command) {
1127
- case 'init':
1128
- handleInit(options);
1129
- break;
1130
- case 'whoami':
1131
- handleWhoami(options);
1132
- break;
1133
- case 'status':
1134
- handleStatus(options);
1135
- break;
1136
- case 'announce':
1137
- await handleAnnounce(options);
1138
- break;
1139
- case 'diagnose':
1140
- await handleDiagnose([subcommand, ...remainingArgs].filter(Boolean), options);
1141
- break;
1142
- case 'peers':
1143
- switch (subcommand) {
1144
- case 'add':
1145
- handlePeersAdd(remainingArgs, options);
1146
- break;
1147
- case 'list':
1148
- case undefined:
1149
- // Allow 'agora peers' to work like 'agora peers list'
1150
- handlePeersList(options);
1151
- break;
1152
- case 'remove':
1153
- handlePeersRemove(remainingArgs, options);
1154
- break;
1155
- case 'discover':
1156
- await handlePeersDiscover(options);
1157
- break;
1158
- default:
1159
- console.error('Error: Unknown peers subcommand. Use: add, list, remove, discover');
1160
- process.exit(1);
1161
- }
1162
- break;
1163
- case 'send':
1164
- await handleSend([subcommand, ...remainingArgs], options);
1165
- break;
1166
- case 'decode':
1167
- handleDecode([subcommand, ...remainingArgs].filter(Boolean), options);
1168
- break;
1169
- case 'serve':
1170
- await handleServe(options);
1171
- break;
1172
- case 'relay':
1173
- await handleRelay(options);
1174
- break;
1175
- case 'reputation':
1176
- switch (subcommand) {
1177
- case 'verify':
1178
- await handleReputationVerify(remainingArgs, options);
1179
- break;
1180
- case 'commit':
1181
- await handleReputationCommit(remainingArgs, options);
1182
- break;
1183
- case 'reveal':
1184
- await handleReputationReveal(remainingArgs, options);
1185
- break;
1186
- case 'query':
1187
- await handleReputationQuery(remainingArgs, options);
1188
- break;
1189
- default:
1190
- console.error('Error: Unknown reputation subcommand. Use: verify, commit, reveal, query');
1191
- process.exit(1);
1192
- }
1193
- break;
1194
- default:
1195
- console.error(`Error: Unknown command '${command}'. Use: init, whoami, status, peers, announce, send, decode, serve, diagnose, relay, reputation`);
1196
- process.exit(1);
1079
+ const args = process.argv.slice(2);
1080
+ if (args.length === 0) {
1081
+ console.error("Usage: agora <command> [options]");
1082
+ console.error("Commands: init, whoami, status, peers, announce, send, decode, serve, diagnose, relay, reputation");
1083
+ console.error(" peers subcommands: add, list, remove, discover");
1084
+ console.error(" reputation subcommands: verify, commit, reveal, query");
1085
+ process.exit(1);
1086
+ }
1087
+ const parsed = parseArgs({
1088
+ args,
1089
+ options: {
1090
+ config: { type: "string" },
1091
+ pretty: { type: "boolean" },
1092
+ url: { type: "string" },
1093
+ token: { type: "string" },
1094
+ pubkey: { type: "string" },
1095
+ type: { type: "string" },
1096
+ payload: { type: "string" },
1097
+ name: { type: "string" },
1098
+ version: { type: "string" },
1099
+ port: { type: "string" },
1100
+ checks: { type: "string" },
1101
+ relay: { type: "string" },
1102
+ "relay-pubkey": { type: "string" },
1103
+ limit: { type: "string" },
1104
+ "active-within": { type: "string" },
1105
+ save: { type: "boolean" },
1106
+ // Reputation options
1107
+ target: { type: "string" },
1108
+ domain: { type: "string" },
1109
+ verdict: { type: "string" },
1110
+ confidence: { type: "string" },
1111
+ evidence: { type: "string" },
1112
+ prediction: { type: "string" },
1113
+ expiry: { type: "string" },
1114
+ "commit-id": { type: "string" },
1115
+ outcome: { type: "string" },
1116
+ agent: { type: "string" }
1117
+ },
1118
+ strict: false,
1119
+ allowPositionals: true
1120
+ });
1121
+ const command = parsed.positionals[0];
1122
+ const subcommand = parsed.positionals[1];
1123
+ const remainingArgs = parsed.positionals.slice(2);
1124
+ const options = {
1125
+ config: typeof parsed.values.config === "string" ? parsed.values.config : void 0,
1126
+ pretty: typeof parsed.values.pretty === "boolean" ? parsed.values.pretty : void 0,
1127
+ type: typeof parsed.values.type === "string" ? parsed.values.type : void 0,
1128
+ payload: typeof parsed.values.payload === "string" ? parsed.values.payload : void 0,
1129
+ url: typeof parsed.values.url === "string" ? parsed.values.url : void 0,
1130
+ token: typeof parsed.values.token === "string" ? parsed.values.token : void 0,
1131
+ pubkey: typeof parsed.values.pubkey === "string" ? parsed.values.pubkey : void 0,
1132
+ name: typeof parsed.values.name === "string" ? parsed.values.name : void 0,
1133
+ version: typeof parsed.values.version === "string" ? parsed.values.version : void 0,
1134
+ port: typeof parsed.values.port === "string" ? parsed.values.port : void 0,
1135
+ checks: typeof parsed.values.checks === "string" ? parsed.values.checks : void 0,
1136
+ relay: typeof parsed.values.relay === "string" ? parsed.values.relay : void 0,
1137
+ "relay-pubkey": typeof parsed.values["relay-pubkey"] === "string" ? parsed.values["relay-pubkey"] : void 0,
1138
+ limit: typeof parsed.values.limit === "string" ? parsed.values.limit : void 0,
1139
+ "active-within": typeof parsed.values["active-within"] === "string" ? parsed.values["active-within"] : void 0,
1140
+ save: typeof parsed.values.save === "boolean" ? parsed.values.save : void 0,
1141
+ // Reputation options
1142
+ target: typeof parsed.values.target === "string" ? parsed.values.target : void 0,
1143
+ domain: typeof parsed.values.domain === "string" ? parsed.values.domain : void 0,
1144
+ verdict: typeof parsed.values.verdict === "string" ? parsed.values.verdict : void 0,
1145
+ confidence: typeof parsed.values.confidence === "string" ? parsed.values.confidence : void 0,
1146
+ evidence: typeof parsed.values.evidence === "string" ? parsed.values.evidence : void 0,
1147
+ prediction: typeof parsed.values.prediction === "string" ? parsed.values.prediction : void 0,
1148
+ expiry: typeof parsed.values.expiry === "string" ? parsed.values.expiry : void 0,
1149
+ "commit-id": typeof parsed.values["commit-id"] === "string" ? parsed.values["commit-id"] : void 0,
1150
+ outcome: typeof parsed.values.outcome === "string" ? parsed.values.outcome : void 0,
1151
+ agent: typeof parsed.values.agent === "string" ? parsed.values.agent : void 0
1152
+ };
1153
+ try {
1154
+ switch (command) {
1155
+ case "init":
1156
+ handleInit(options);
1157
+ break;
1158
+ case "whoami":
1159
+ handleWhoami(options);
1160
+ break;
1161
+ case "status":
1162
+ handleStatus(options);
1163
+ break;
1164
+ case "announce":
1165
+ await handleAnnounce(options);
1166
+ break;
1167
+ case "diagnose":
1168
+ await handleDiagnose([subcommand, ...remainingArgs].filter(Boolean), options);
1169
+ break;
1170
+ case "peers":
1171
+ switch (subcommand) {
1172
+ case "add":
1173
+ handlePeersAdd(remainingArgs, options);
1174
+ break;
1175
+ case "list":
1176
+ case void 0:
1177
+ handlePeersList(options);
1178
+ break;
1179
+ case "remove":
1180
+ handlePeersRemove(remainingArgs, options);
1181
+ break;
1182
+ case "discover":
1183
+ await handlePeersDiscover(options);
1184
+ break;
1185
+ default:
1186
+ console.error("Error: Unknown peers subcommand. Use: add, list, remove, discover");
1187
+ process.exit(1);
1197
1188
  }
1198
- }
1199
- catch (e) {
1200
- console.error('Error:', e instanceof Error ? e.message : String(e));
1189
+ break;
1190
+ case "send":
1191
+ await handleSend([subcommand, ...remainingArgs], options);
1192
+ break;
1193
+ case "decode":
1194
+ handleDecode([subcommand, ...remainingArgs].filter(Boolean), options);
1195
+ break;
1196
+ case "serve":
1197
+ await handleServe(options);
1198
+ break;
1199
+ case "relay":
1200
+ await handleRelay(options);
1201
+ break;
1202
+ case "reputation":
1203
+ switch (subcommand) {
1204
+ case "verify":
1205
+ await handleReputationVerify(remainingArgs, options);
1206
+ break;
1207
+ case "commit":
1208
+ await handleReputationCommit(remainingArgs, options);
1209
+ break;
1210
+ case "reveal":
1211
+ await handleReputationReveal(remainingArgs, options);
1212
+ break;
1213
+ case "query":
1214
+ await handleReputationQuery(remainingArgs, options);
1215
+ break;
1216
+ default:
1217
+ console.error("Error: Unknown reputation subcommand. Use: verify, commit, reveal, query");
1218
+ process.exit(1);
1219
+ }
1220
+ break;
1221
+ default:
1222
+ console.error(`Error: Unknown command '${command}'. Use: init, whoami, status, peers, announce, send, decode, serve, diagnose, relay, reputation`);
1201
1223
  process.exit(1);
1202
1224
  }
1225
+ } catch (e) {
1226
+ console.error("Error:", e instanceof Error ? e.message : String(e));
1227
+ process.exit(1);
1228
+ }
1203
1229
  }
1204
1230
  main().catch((e) => {
1205
- console.error('Fatal error:', e instanceof Error ? e.message : String(e));
1206
- process.exit(1);
1231
+ console.error("Fatal error:", e instanceof Error ? e.message : String(e));
1232
+ process.exit(1);
1207
1233
  });
1208
1234
  //# sourceMappingURL=cli.js.map