@rookdaemon/agora 0.2.7 → 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.
- package/README.md +33 -0
- package/dist/chunk-JUOGKXFN.js +1645 -0
- package/dist/chunk-JUOGKXFN.js.map +1 -0
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +1163 -1137
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1613 -25
- package/dist/index.js +1135 -24
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/config.d.ts +0 -59
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -115
- package/dist/config.js.map +0 -1
- package/dist/discovery/bootstrap.d.ts +0 -32
- package/dist/discovery/bootstrap.d.ts.map +0 -1
- package/dist/discovery/bootstrap.js +0 -36
- package/dist/discovery/bootstrap.js.map +0 -1
- package/dist/discovery/peer-discovery.d.ts +0 -59
- package/dist/discovery/peer-discovery.d.ts.map +0 -1
- package/dist/discovery/peer-discovery.js +0 -108
- package/dist/discovery/peer-discovery.js.map +0 -1
- package/dist/identity/keypair.d.ts +0 -42
- package/dist/identity/keypair.d.ts.map +0 -1
- package/dist/identity/keypair.js +0 -83
- package/dist/identity/keypair.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/message/envelope.d.ts +0 -59
- package/dist/message/envelope.d.ts.map +0 -1
- package/dist/message/envelope.js +0 -83
- package/dist/message/envelope.js.map +0 -1
- package/dist/message/types/paper-discovery.d.ts +0 -28
- package/dist/message/types/paper-discovery.d.ts.map +0 -1
- package/dist/message/types/paper-discovery.js +0 -2
- package/dist/message/types/paper-discovery.js.map +0 -1
- package/dist/message/types/peer-discovery.d.ts +0 -78
- package/dist/message/types/peer-discovery.d.ts.map +0 -1
- package/dist/message/types/peer-discovery.js +0 -90
- package/dist/message/types/peer-discovery.js.map +0 -1
- package/dist/peer/client.d.ts +0 -50
- package/dist/peer/client.d.ts.map +0 -1
- package/dist/peer/client.js +0 -138
- package/dist/peer/client.js.map +0 -1
- package/dist/peer/manager.d.ts +0 -65
- package/dist/peer/manager.d.ts.map +0 -1
- package/dist/peer/manager.js +0 -153
- package/dist/peer/manager.js.map +0 -1
- package/dist/peer/server.d.ts +0 -65
- package/dist/peer/server.d.ts.map +0 -1
- package/dist/peer/server.js +0 -154
- package/dist/peer/server.js.map +0 -1
- package/dist/registry/capability.d.ts +0 -44
- package/dist/registry/capability.d.ts.map +0 -1
- package/dist/registry/capability.js +0 -94
- package/dist/registry/capability.js.map +0 -1
- package/dist/registry/discovery-service.d.ts +0 -64
- package/dist/registry/discovery-service.d.ts.map +0 -1
- package/dist/registry/discovery-service.js +0 -129
- package/dist/registry/discovery-service.js.map +0 -1
- package/dist/registry/messages.d.ts +0 -105
- package/dist/registry/messages.d.ts.map +0 -1
- package/dist/registry/messages.js +0 -2
- package/dist/registry/messages.js.map +0 -1
- package/dist/registry/peer-store.d.ts +0 -57
- package/dist/registry/peer-store.d.ts.map +0 -1
- package/dist/registry/peer-store.js +0 -92
- package/dist/registry/peer-store.js.map +0 -1
- package/dist/registry/peer.d.ts +0 -20
- package/dist/registry/peer.d.ts.map +0 -1
- package/dist/registry/peer.js +0 -2
- package/dist/registry/peer.js.map +0 -1
- package/dist/relay/client.d.ts +0 -112
- package/dist/relay/client.d.ts.map +0 -1
- package/dist/relay/client.js +0 -281
- package/dist/relay/client.js.map +0 -1
- package/dist/relay/server.d.ts +0 -76
- package/dist/relay/server.d.ts.map +0 -1
- package/dist/relay/server.js +0 -338
- package/dist/relay/server.js.map +0 -1
- package/dist/relay/types.d.ts +0 -35
- package/dist/relay/types.d.ts.map +0 -1
- package/dist/relay/types.js +0 -2
- package/dist/relay/types.js.map +0 -1
- package/dist/reputation/commit-reveal.d.ts +0 -45
- package/dist/reputation/commit-reveal.d.ts.map +0 -1
- package/dist/reputation/commit-reveal.js +0 -125
- package/dist/reputation/commit-reveal.js.map +0 -1
- package/dist/reputation/scoring.d.ts +0 -31
- package/dist/reputation/scoring.d.ts.map +0 -1
- package/dist/reputation/scoring.js +0 -105
- package/dist/reputation/scoring.js.map +0 -1
- package/dist/reputation/store.d.ts +0 -83
- package/dist/reputation/store.d.ts.map +0 -1
- package/dist/reputation/store.js +0 -202
- package/dist/reputation/store.js.map +0 -1
- package/dist/reputation/types.d.ts +0 -150
- package/dist/reputation/types.d.ts.map +0 -1
- package/dist/reputation/types.js +0 -113
- package/dist/reputation/types.js.map +0 -1
- package/dist/reputation/verification.d.ts +0 -28
- package/dist/reputation/verification.d.ts.map +0 -1
- package/dist/reputation/verification.js +0 -91
- package/dist/reputation/verification.js.map +0 -1
- package/dist/service.d.ts +0 -90
- package/dist/service.d.ts.map +0 -1
- package/dist/service.js +0 -176
- package/dist/service.js.map +0 -1
- package/dist/transport/http.d.ts +0 -41
- package/dist/transport/http.d.ts.map +0 -1
- package/dist/transport/http.js +0 -103
- package/dist/transport/http.js.map +0 -1
- package/dist/transport/peer-config.d.ts +0 -38
- package/dist/transport/peer-config.d.ts.map +0 -1
- package/dist/transport/peer-config.js +0 -41
- package/dist/transport/peer-config.js.map +0 -1
- package/dist/transport/relay.d.ts +0 -30
- package/dist/transport/relay.d.ts.map +0 -1
- package/dist/transport/relay.js +0 -85
- package/dist/transport/relay.js.map +0 -1
- package/dist/utils.d.ts +0 -40
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -59
- package/dist/utils.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,1208 +1,1234 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
if (!
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
|
|
510
|
-
|
|
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
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
528
|
-
|
|
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
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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
|
-
|
|
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
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
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
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
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
|
-
|
|
789
|
-
|
|
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
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
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
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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
|
-
|
|
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
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
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
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
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
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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
|
-
|
|
1200
|
-
|
|
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
|
-
|
|
1206
|
-
|
|
1231
|
+
console.error("Fatal error:", e instanceof Error ? e.message : String(e));
|
|
1232
|
+
process.exit(1);
|
|
1207
1233
|
});
|
|
1208
1234
|
//# sourceMappingURL=cli.js.map
|