@jellylegsai/aether-cli 1.9.1 → 2.0.1
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/IMPLEMENTATION_REPORT.md +319 -0
- package/commands/blockheight.js +230 -0
- package/commands/call.js +981 -0
- package/commands/claim.js +98 -72
- package/commands/deploy.js +959 -0
- package/commands/index.js +88 -86
- package/commands/init.js +33 -49
- package/commands/network-diagnostics.js +706 -0
- package/commands/network.js +412 -429
- package/commands/rewards.js +311 -266
- package/commands/sdk.js +791 -656
- package/commands/slot.js +3 -11
- package/commands/stake.js +581 -516
- package/commands/supply.js +483 -391
- package/commands/token-accounts.js +275 -0
- package/commands/transfer.js +3 -11
- package/commands/unstake.js +3 -11
- package/commands/validator-start.js +681 -323
- package/commands/validator.js +959 -0
- package/commands/validators.js +623 -626
- package/commands/version.js +240 -0
- package/commands/wallet.js +17 -24
- package/cycle-report-issue-116.txt +165 -0
- package/index.js +501 -595
- package/lib/ui.js +623 -0
- package/package.json +83 -76
- package/sdk/index.d.ts +546 -0
- package/sdk/index.js +130 -0
- package/sdk/package.json +2 -1
package/commands/network.js
CHANGED
|
@@ -1,429 +1,412 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* aether-cli network - Aether Network Status
|
|
4
|
-
*
|
|
5
|
-
* Queries the Aether network for broad health metrics:
|
|
6
|
-
* - Network-wide slot, block height, slot production
|
|
7
|
-
* - Connected peers and their info
|
|
8
|
-
* - Consensus status and epoch info
|
|
9
|
-
* - TPS estimates from the network
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* aether
|
|
16
|
-
* aether
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const path = require('path');
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
function
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
console.log();
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
console.log();
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
console.log();
|
|
316
|
-
console.log(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
console.log();
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
} else if (opts.showEpoch) {
|
|
414
|
-
renderEpoch(epochData, rpc);
|
|
415
|
-
} else {
|
|
416
|
-
renderSummary(data, rpc);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
module.exports = { main, networkCommand: main };
|
|
421
|
-
|
|
422
|
-
if (require.main === module) {
|
|
423
|
-
main().catch((err) => {
|
|
424
|
-
console.error(`\n${C.red}✗ Network command failed:${C.reset} ${err.message}`);
|
|
425
|
-
console.error(` ${C.dim}Check that your validator is running and RPC is accessible.${C.reset}`);
|
|
426
|
-
console.error(` ${C.dim}Set custom RPC: AETHER_RPC=http://your-rpc-url${C.reset}\n`);
|
|
427
|
-
process.exit(1);
|
|
428
|
-
});
|
|
429
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* aether-cli network - Aether Network Status
|
|
4
|
+
*
|
|
5
|
+
* Queries the Aether network for broad health metrics:
|
|
6
|
+
* - Network-wide slot, block height, slot production
|
|
7
|
+
* - Connected peers and their info
|
|
8
|
+
* - Consensus status and epoch info
|
|
9
|
+
* - TPS estimates from the network
|
|
10
|
+
*
|
|
11
|
+
* FULLY WIRED TO SDK - Uses @jellylegsai/aether-sdk for all blockchain calls.
|
|
12
|
+
* No manual HTTP - all calls go through AetherClient with real RPC.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* aether network # Interactive summary view
|
|
16
|
+
* aether network --json # JSON output for scripting
|
|
17
|
+
* aether network --rpc <url> # Query a specific RPC endpoint
|
|
18
|
+
* aether network --peers # Detailed peer list
|
|
19
|
+
* aether network --epoch # Current epoch and consensus info
|
|
20
|
+
* aether network --wait # Wait for node to sync
|
|
21
|
+
* aether network --ping # Include latency measurements
|
|
22
|
+
*
|
|
23
|
+
* SDK Methods Used:
|
|
24
|
+
* - client.getSlot() → GET /v1/slot
|
|
25
|
+
* - client.getBlockHeight() → GET /v1/blockheight
|
|
26
|
+
* - client.getValidators() → GET /v1/validators
|
|
27
|
+
* - client.getEpochInfo() → GET /v1/epoch
|
|
28
|
+
* - client.getTPS() → GET /v1/tps
|
|
29
|
+
* - client.getSupply() → GET /v1/supply
|
|
30
|
+
* - client.getHealth() → GET /v1/health
|
|
31
|
+
* - client.getVersion() → GET /v1/version
|
|
32
|
+
* - client.getSlotProduction() → POST /v1/slot_production
|
|
33
|
+
* - client.ping() → Health check with latency
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const path = require('path');
|
|
37
|
+
|
|
38
|
+
// Import SDK for real blockchain RPC calls
|
|
39
|
+
const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
|
|
40
|
+
const aether = require(sdkPath);
|
|
41
|
+
|
|
42
|
+
// Import UI framework
|
|
43
|
+
const { C, indicators, startSpinner, stopSpinner, drawBox, drawTable,
|
|
44
|
+
success, error, warning, info, code, highlight, value,
|
|
45
|
+
formatHealth, formatLatency } = require('../lib/ui');
|
|
46
|
+
|
|
47
|
+
const DEFAULT_RPC = process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// SDK Client Setup
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
function createClient(rpc) {
|
|
54
|
+
return new aether.AetherClient({ rpcUrl: rpc });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Argument Parsing
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
function parseArgs() {
|
|
62
|
+
const args = process.argv.slice(2);
|
|
63
|
+
const options = {
|
|
64
|
+
rpc: DEFAULT_RPC,
|
|
65
|
+
showPeers: false,
|
|
66
|
+
showEpoch: false,
|
|
67
|
+
asJson: false,
|
|
68
|
+
wait: false,
|
|
69
|
+
doPing: false,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < args.length; i++) {
|
|
73
|
+
switch (args[i]) {
|
|
74
|
+
case '-r':
|
|
75
|
+
case '--rpc':
|
|
76
|
+
options.rpc = args[++i];
|
|
77
|
+
break;
|
|
78
|
+
case '-p':
|
|
79
|
+
case '--peers':
|
|
80
|
+
options.showPeers = true;
|
|
81
|
+
break;
|
|
82
|
+
case '-e':
|
|
83
|
+
case '--epoch':
|
|
84
|
+
options.showEpoch = true;
|
|
85
|
+
break;
|
|
86
|
+
case '-j':
|
|
87
|
+
case '--json':
|
|
88
|
+
options.asJson = true;
|
|
89
|
+
break;
|
|
90
|
+
case '-w':
|
|
91
|
+
case '--wait':
|
|
92
|
+
options.wait = true;
|
|
93
|
+
break;
|
|
94
|
+
case '--ping':
|
|
95
|
+
options.doPing = true;
|
|
96
|
+
break;
|
|
97
|
+
case '-h':
|
|
98
|
+
case '--help':
|
|
99
|
+
showHelp();
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return options;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function showHelp() {
|
|
108
|
+
console.log(`
|
|
109
|
+
${C.cyan}${C.bright}network${C.reset} — Aether Network Status
|
|
110
|
+
|
|
111
|
+
${C.bright}USAGE${C.reset}
|
|
112
|
+
aether network [options]
|
|
113
|
+
|
|
114
|
+
${C.bright}OPTIONS${C.reset}
|
|
115
|
+
-r, --rpc <url> RPC endpoint (default: ${DEFAULT_RPC})
|
|
116
|
+
-p, --peers Show detailed peer list
|
|
117
|
+
-e, --epoch Show epoch and consensus information
|
|
118
|
+
-j, --json Output raw JSON for scripting
|
|
119
|
+
-w, --wait Wait for node to sync
|
|
120
|
+
--ping Include latency measurements
|
|
121
|
+
-h, --help Show this help message
|
|
122
|
+
|
|
123
|
+
${C.bright}SDK METHODS${C.reset}
|
|
124
|
+
getSlot(), getBlockHeight(), getValidators(), getEpochInfo()
|
|
125
|
+
getTPS(), getSupply(), getHealth(), getVersion(), getSlotProduction()
|
|
126
|
+
|
|
127
|
+
${C.bright}EXAMPLES${C.reset}
|
|
128
|
+
aether network # Summary view
|
|
129
|
+
aether network --json # JSON output
|
|
130
|
+
aether network --peers # Detailed peer list
|
|
131
|
+
aether network --epoch # Epoch info
|
|
132
|
+
aether network --rpc http://my-rpc:8899
|
|
133
|
+
`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// SDK Data Fetchers - REAL RPC CALLS
|
|
138
|
+
// ============================================================================
|
|
139
|
+
|
|
140
|
+
async function fetchNetworkData(rpc, asJson) {
|
|
141
|
+
const client = createClient(rpc);
|
|
142
|
+
|
|
143
|
+
const startTime = Date.now();
|
|
144
|
+
|
|
145
|
+
if (!asJson) {
|
|
146
|
+
startSpinner('Querying network via SDK');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const results = await Promise.allSettled([
|
|
150
|
+
client.getSlot().catch(() => null),
|
|
151
|
+
client.getBlockHeight().catch(() => null),
|
|
152
|
+
client.getValidators().catch(() => []),
|
|
153
|
+
client.getEpochInfo().catch(() => null),
|
|
154
|
+
client.getTPS().catch(() => null),
|
|
155
|
+
client.getSupply().catch(() => null),
|
|
156
|
+
client.getHealth().catch(() => null),
|
|
157
|
+
client.getVersion().catch(() => null),
|
|
158
|
+
client.getSlotProduction().catch(() => null),
|
|
159
|
+
aether.ping(rpc).catch(() => ({ ok: false, latency: null })),
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
const latency = Date.now() - startTime;
|
|
163
|
+
|
|
164
|
+
if (!asJson) {
|
|
165
|
+
stopSpinner(true, 'Network data retrieved');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const [
|
|
169
|
+
slot,
|
|
170
|
+
blockHeight,
|
|
171
|
+
validators,
|
|
172
|
+
epochInfo,
|
|
173
|
+
tps,
|
|
174
|
+
supply,
|
|
175
|
+
health,
|
|
176
|
+
version,
|
|
177
|
+
slotProduction,
|
|
178
|
+
pingResult,
|
|
179
|
+
] = results.map(r => r.status === 'fulfilled' ? r.value : null);
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
slot,
|
|
183
|
+
blockHeight,
|
|
184
|
+
validators: Array.isArray(validators) ? validators : [],
|
|
185
|
+
epochInfo,
|
|
186
|
+
tps,
|
|
187
|
+
supply,
|
|
188
|
+
health,
|
|
189
|
+
version,
|
|
190
|
+
slotProduction,
|
|
191
|
+
pingResult,
|
|
192
|
+
latency,
|
|
193
|
+
rpc,
|
|
194
|
+
fetchedAt: new Date().toISOString(),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ============================================================================
|
|
199
|
+
// Format Helpers
|
|
200
|
+
// ============================================================================
|
|
201
|
+
|
|
202
|
+
function formatNumber(n) {
|
|
203
|
+
if (n === null || n === undefined) return `${C.dim}N/A${C.reset}`;
|
|
204
|
+
return n.toLocaleString();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function formatAether(lamports) {
|
|
208
|
+
if (!lamports && lamports !== 0) return `${C.dim}N/A${C.reset}`;
|
|
209
|
+
const aeth = Number(lamports) / 1e9;
|
|
210
|
+
if (aeth === 0) return '0 AETH';
|
|
211
|
+
return aeth.toFixed(4).replace(/\.?0+$/, '') + ' AETH';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function uptimeString(seconds) {
|
|
215
|
+
if (!seconds) return `${C.dim}unknown${C.reset}`;
|
|
216
|
+
const d = Math.floor(seconds / 86400);
|
|
217
|
+
const h = Math.floor((seconds % 86400) / 3600);
|
|
218
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
219
|
+
const parts = [];
|
|
220
|
+
if (d > 0) parts.push(`${d}d`);
|
|
221
|
+
if (h > 0) parts.push(`${h}h`);
|
|
222
|
+
if (m > 0) parts.push(`${m}m`);
|
|
223
|
+
return parts.length > 0 ? parts.join(' ') : `${seconds}s`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function statusColor(status) {
|
|
227
|
+
const s = (status || '').toLowerCase();
|
|
228
|
+
if (s === 'active' || s === 'ok' || s === 'healthy') return C.green;
|
|
229
|
+
if (s === 'delinquent' || s === 'error') return C.red;
|
|
230
|
+
if (s === 'inactive' || s === 'syncing') return C.yellow;
|
|
231
|
+
return C.dim;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ============================================================================
|
|
235
|
+
// Output Renderers
|
|
236
|
+
// ============================================================================
|
|
237
|
+
|
|
238
|
+
function renderSummary(data) {
|
|
239
|
+
const { slot, blockHeight, validators, epochInfo, tps, supply, health, version, pingResult, latency } = data;
|
|
240
|
+
const peerCount = validators.length;
|
|
241
|
+
|
|
242
|
+
// Health status
|
|
243
|
+
const isHealthy = health === 'ok' || health === 'healthy';
|
|
244
|
+
const healthStatus = isHealthy ?
|
|
245
|
+
`${C.green}${indicators.success} Healthy${C.reset}` :
|
|
246
|
+
health ? `${C.yellow}${indicators.warning} ${health}${C.reset}` :
|
|
247
|
+
`${C.red}${indicators.error} Unknown${C.reset}`;
|
|
248
|
+
|
|
249
|
+
// Version string
|
|
250
|
+
const versionStr = version ?
|
|
251
|
+
(version.aetherCore || version.featureSet || JSON.stringify(version)) :
|
|
252
|
+
`${C.dim}unknown${C.reset}`;
|
|
253
|
+
|
|
254
|
+
console.log();
|
|
255
|
+
console.log(drawBox(
|
|
256
|
+
`
|
|
257
|
+
${C.bright}AETHER NETWORK STATUS${C.reset} ${C.dim}${data.fetchedAt}${C.reset}
|
|
258
|
+
|
|
259
|
+
${C.cyan}Health:${C.reset} ${healthStatus}
|
|
260
|
+
${C.cyan}RPC:${C.reset} ${data.rpc}
|
|
261
|
+
${C.cyan}Version:${C.reset} ${versionStr}
|
|
262
|
+
${C.cyan}Latency:${C.reset} ${formatLatency(pingResult?.latency || latency)}
|
|
263
|
+
|
|
264
|
+
${C.cyan}Current Slot:${C.reset} ${highlight(formatNumber(slot))}
|
|
265
|
+
${C.cyan}Block Height:${C.reset} ${C.green}${formatNumber(blockHeight)}${C.reset}
|
|
266
|
+
${C.cyan}Active Peers:${C.reset} ${C.magenta}${formatNumber(peerCount)}${C.reset}
|
|
267
|
+
${C.cyan}TPS:${C.reset} ${tps !== null ? `${C.cyan}${tps.toFixed(2)}${C.reset}` : `${C.dim}N/A${C.reset}`}
|
|
268
|
+
|
|
269
|
+
${epochInfo ? `${C.cyan}Epoch:${C.reset} ${C.bright}${epochInfo.epoch}${C.reset} (${formatNumber(epochInfo.slotIndex)}/${formatNumber(epochInfo.slotsInEpoch)} slots)` : ''}
|
|
270
|
+
${supply ? `${C.cyan}Total Supply:${C.reset} ${C.green}${formatAether(supply.total)}${C.reset}` : ''}
|
|
271
|
+
|
|
272
|
+
${C.dim}SDK: @jellylegsai/aether-sdk${C.reset}
|
|
273
|
+
`.trim(),
|
|
274
|
+
{ style: 'double', title: 'AETHER NETWORK', titleColor: C.cyan + C.bright }
|
|
275
|
+
));
|
|
276
|
+
|
|
277
|
+
console.log();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function renderPeers(validators, rpc) {
|
|
281
|
+
if (!validators || validators.length === 0) {
|
|
282
|
+
console.log(`\n ${warning('No peer information available')}`);
|
|
283
|
+
console.log(` ${C.dim}Peers may not be exposed by your validator's RPC configuration.${C.reset}\n`);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const rows = validators.slice(0, 50).map((v, i) => {
|
|
288
|
+
const addr = (v.address || v.pubkey || v.id || v.vote_account || 'unknown').slice(0, 32);
|
|
289
|
+
const tier = (v.tier || v.node_type || 'unknown').toUpperCase();
|
|
290
|
+
const stake = formatAether(v.stake_lamports || v.stake || v.activated_stake || 0);
|
|
291
|
+
const status = v.status || 'active';
|
|
292
|
+
const statusCol = statusColor(status);
|
|
293
|
+
|
|
294
|
+
return [
|
|
295
|
+
`${statusCol}●${C.reset}`,
|
|
296
|
+
`${i + 1}`,
|
|
297
|
+
addr,
|
|
298
|
+
tier,
|
|
299
|
+
stake,
|
|
300
|
+
];
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
console.log();
|
|
304
|
+
console.log(drawTable(
|
|
305
|
+
['', '#', 'Validator', 'Tier', 'Stake'],
|
|
306
|
+
rows,
|
|
307
|
+
{ borderStyle: 'single', headerColor: C.cyan + C.bright }
|
|
308
|
+
));
|
|
309
|
+
|
|
310
|
+
if (validators.length > 50) {
|
|
311
|
+
console.log(`\n ${C.dim}... and ${validators.length - 50} more validators (use --json for full list)${C.reset}`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
console.log();
|
|
315
|
+
console.log(` ${C.bright}Total validators:${C.reset} ${C.magenta}${validators.length}${C.reset}`);
|
|
316
|
+
console.log();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function renderEpoch(epochInfo) {
|
|
320
|
+
console.log();
|
|
321
|
+
|
|
322
|
+
if (!epochInfo) {
|
|
323
|
+
console.log(`\n ${warning('Epoch information not available')}`);
|
|
324
|
+
console.log(` ${C.dim}Is your validator fully synced?${C.reset}\n`);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const progress = epochInfo.slotsInEpoch > 0 ?
|
|
329
|
+
((epochInfo.slotIndex / epochInfo.slotsInEpoch) * 100).toFixed(2) : '0.00';
|
|
330
|
+
|
|
331
|
+
console.log(drawBox(
|
|
332
|
+
`
|
|
333
|
+
${C.bright}Epoch ${epochInfo.epoch}${C.reset}
|
|
334
|
+
|
|
335
|
+
${C.cyan}Slots in Epoch:${C.reset} ${formatNumber(epochInfo.slotsInEpoch)}
|
|
336
|
+
${C.cyan}Current Slot:${C.reset} ${formatNumber(epochInfo.slotIndex)}
|
|
337
|
+
${C.cyan}Progress:${C.reset} ${highlight(progress + '%')}
|
|
338
|
+
${C.cyan}Blocks Remaining:${C.reset} ${formatNumber(epochInfo.slotsInEpoch - epochInfo.slotIndex)}
|
|
339
|
+
|
|
340
|
+
${epochInfo.absoluteSlot ? `${C.cyan}Absolute Slot:${C.reset} ${formatNumber(epochInfo.absoluteSlot)}` : ''}
|
|
341
|
+
${epochInfo.blockHeight ? `${C.cyan}Block Height:${C.reset} ${formatNumber(epochInfo.blockHeight)}` : ''}
|
|
342
|
+
`.trim(),
|
|
343
|
+
{ style: 'single', title: 'EPOCH INFO', titleColor: C.cyan }
|
|
344
|
+
));
|
|
345
|
+
|
|
346
|
+
// Progress bar
|
|
347
|
+
const barWidth = 40;
|
|
348
|
+
const filled = Math.floor((progress / 100) * barWidth);
|
|
349
|
+
const empty = barWidth - filled;
|
|
350
|
+
const progressBar = C.green + '█'.repeat(filled) + C.dim + '░'.repeat(empty) + C.reset;
|
|
351
|
+
|
|
352
|
+
console.log(`\n ${progressBar} ${C.bright}${progress}%${C.reset}\n`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function renderJson(data) {
|
|
356
|
+
console.log(JSON.stringify(data, (key, value) => {
|
|
357
|
+
if (typeof value === 'bigint') return value.toString();
|
|
358
|
+
return value;
|
|
359
|
+
}, 2));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ============================================================================
|
|
363
|
+
// Main Command
|
|
364
|
+
// ============================================================================
|
|
365
|
+
|
|
366
|
+
async function networkCommand() {
|
|
367
|
+
const opts = parseArgs();
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
const data = await fetchNetworkData(opts.rpc, opts.asJson);
|
|
371
|
+
|
|
372
|
+
if (opts.asJson) {
|
|
373
|
+
renderJson(data);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (opts.showPeers) {
|
|
378
|
+
renderPeers(data.validators, opts.rpc);
|
|
379
|
+
} else if (opts.showEpoch) {
|
|
380
|
+
renderEpoch(data.epochInfo);
|
|
381
|
+
} else {
|
|
382
|
+
renderSummary(data);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
} catch (err) {
|
|
386
|
+
if (opts.asJson) {
|
|
387
|
+
console.log(JSON.stringify({
|
|
388
|
+
error: err.message,
|
|
389
|
+
rpc: opts.rpc,
|
|
390
|
+
timestamp: new Date().toISOString(),
|
|
391
|
+
}, null, 2));
|
|
392
|
+
} else {
|
|
393
|
+
console.log(`\n ${error('Network query failed')}`);
|
|
394
|
+
console.log(` ${C.dim}${err.message}${C.reset}\n`);
|
|
395
|
+
console.log(` ${C.bright}Troubleshooting:${C.reset}`);
|
|
396
|
+
console.log(` • Is your validator running? ${code('aether ping')}`);
|
|
397
|
+
console.log(` • Check RPC endpoint: ${C.dim}${opts.rpc}${C.reset}`);
|
|
398
|
+
console.log(` • Set custom RPC: ${C.dim}AETHER_RPC=https://your-rpc-url${C.reset}`);
|
|
399
|
+
console.log();
|
|
400
|
+
}
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
module.exports = { networkCommand, main: networkCommand };
|
|
406
|
+
|
|
407
|
+
if (require.main === module) {
|
|
408
|
+
networkCommand().catch(err => {
|
|
409
|
+
console.error(`\n ${error('Network command failed')}: ${err.message}\n`);
|
|
410
|
+
process.exit(1);
|
|
411
|
+
});
|
|
412
|
+
}
|