@jellylegsai/aether-cli 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/aether-cli-1.0.0.tgz +0 -0
  4. package/aether-cli-1.8.0.tgz +0 -0
  5. package/aether-hub-1.0.5.tgz +0 -0
  6. package/aether-hub-1.1.8.tgz +0 -0
  7. package/aether-hub-1.2.1.tgz +0 -0
  8. package/commands/account.js +280 -0
  9. package/commands/apy.js +499 -0
  10. package/commands/balance.js +241 -0
  11. package/commands/blockhash.js +181 -0
  12. package/commands/broadcast.js +387 -0
  13. package/commands/claim.js +490 -0
  14. package/commands/config.js +851 -0
  15. package/commands/delegations.js +582 -0
  16. package/commands/doctor.js +769 -0
  17. package/commands/emergency.js +667 -0
  18. package/commands/epoch.js +275 -0
  19. package/commands/fees.js +276 -0
  20. package/commands/index.js +78 -0
  21. package/commands/info.js +495 -0
  22. package/commands/init.js +816 -0
  23. package/commands/install.js +666 -0
  24. package/commands/kyc.js +272 -0
  25. package/commands/logs.js +315 -0
  26. package/commands/monitor.js +431 -0
  27. package/commands/multisig.js +701 -0
  28. package/commands/network.js +429 -0
  29. package/commands/nft.js +857 -0
  30. package/commands/ping.js +266 -0
  31. package/commands/price.js +253 -0
  32. package/commands/rewards.js +931 -0
  33. package/commands/sdk-test.js +477 -0
  34. package/commands/sdk.js +656 -0
  35. package/commands/slot.js +155 -0
  36. package/commands/snapshot.js +470 -0
  37. package/commands/stake-info.js +139 -0
  38. package/commands/stake-positions.js +205 -0
  39. package/commands/stake.js +516 -0
  40. package/commands/stats.js +396 -0
  41. package/commands/status.js +327 -0
  42. package/commands/supply.js +391 -0
  43. package/commands/tps.js +238 -0
  44. package/commands/transfer.js +495 -0
  45. package/commands/tx-history.js +346 -0
  46. package/commands/unstake.js +597 -0
  47. package/commands/validator-info.js +657 -0
  48. package/commands/validator-register.js +593 -0
  49. package/commands/validator-start.js +323 -0
  50. package/commands/validator-status.js +227 -0
  51. package/commands/validators.js +626 -0
  52. package/commands/wallet.js +1570 -0
  53. package/index.js +593 -0
  54. package/lib/errors.js +398 -0
  55. package/package.json +76 -0
  56. package/sdk/README.md +210 -0
  57. package/sdk/index.js +1639 -0
  58. package/sdk/package.json +34 -0
  59. package/sdk/rpc.js +254 -0
  60. package/sdk/test.js +85 -0
  61. package/test/doctor.test.js +76 -0
  62. package/validator-identity.json +4 -0
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aether-cli status
4
+ *
5
+ * Single-command dashboard: epoch + network + supply + validator + rewards
6
+ * Gives a full node/network overview in one shot — no need to run multiple commands.
7
+ *
8
+ * Usage:
9
+ * aether status Show full status dashboard
10
+ * aether status --json JSON output for scripting/monitoring
11
+ * aether status --rpc <url> Query a specific RPC endpoint
12
+ * aether status --validator Include local validator info
13
+ * aether status --compact One-line summary
14
+ *
15
+ * Requires AETHER_RPC env var (default: http://127.0.0.1:8899)
16
+ */
17
+
18
+ const os = require('os');
19
+ const path = require('path');
20
+
21
+ // ANSI colours
22
+ const C = {
23
+ reset: '\x1b[0m',
24
+ bright: '\x1b[1m',
25
+ dim: '\x1b[2m',
26
+ red: '\x1b[31m',
27
+ green: '\x1b[32m',
28
+ yellow: '\x1b[33m',
29
+ cyan: '\x1b[36m',
30
+ magenta: '\x1b[35m',
31
+ };
32
+
33
+ const CLI_VERSION = '1.0.0';
34
+
35
+ // Import SDK for real blockchain RPC calls
36
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
37
+ const aether = require(sdkPath);
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Helpers
41
+ // ---------------------------------------------------------------------------
42
+
43
+ function getDefaultRpc() {
44
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
45
+ }
46
+
47
+ function createClient(rpc) {
48
+ return new aether.AetherClient({ rpcUrl: rpc });
49
+ }
50
+
51
+ function formatAether(lamports) {
52
+ const aeth = (Number(lamports) / 1e9).toFixed(4);
53
+ return aeth + ' AETH';
54
+ }
55
+
56
+ function loadConfig() {
57
+ const fs = require('fs');
58
+ const path = require('path');
59
+ const aetherDir = path.join(os.homedir(), '.aether');
60
+ const cfgPath = path.join(aetherDir, 'config.json');
61
+ if (!fs.existsSync(cfgPath)) return { defaultWallet: null };
62
+ try { return JSON.parse(fs.readFileSync(cfgPath, 'utf8')); }
63
+ catch { return { defaultWallet: null }; }
64
+ }
65
+
66
+ function loadIdentity() {
67
+ const fs = require('fs');
68
+ const path = require('path');
69
+ const idPath = path.join(os.homedir(), '.aether', 'validator-identity.json');
70
+ if (!fs.existsSync(idPath)) return null;
71
+ try { return JSON.parse(fs.readFileSync(idPath, 'utf8')); }
72
+ catch { return null; }
73
+ }
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // Status command
77
+ // ---------------------------------------------------------------------------
78
+
79
+ async function statusCommand() {
80
+ const args = process.argv.slice(2); // [node, status.js, ...]
81
+ const isJson = args.includes('--json') || args.includes('-j');
82
+ const isCompact = args.includes('--compact');
83
+ const includeValidator = args.includes('--validator');
84
+ const rpcIdx = args.findIndex(a => a === '--rpc');
85
+ const rpc = rpcIdx !== -1 && args[rpcIdx + 1] ? args[rpcIdx + 1] : getDefaultRpc();
86
+
87
+ const errors = {};
88
+ const data = {};
89
+
90
+ // Fetch all data in parallel
91
+ const promises = [
92
+ fetchEpochInfo(rpc).then(d => { data.epoch = d; }).catch(e => { errors.epoch = e.message; }),
93
+ fetchSupplyInfo(rpc).then(d => { data.supply = d; }).catch(e => { errors.supply = e.message; }),
94
+ fetchNetworkInfo(rpc).then(d => { data.network = d; }).catch(e => { errors.network = e.message; }),
95
+ fetchVersionInfo(rpc).then(d => { data.version = d; }).catch(e => { errors.version = e.message; }),
96
+ ];
97
+
98
+ await Promise.all(promises);
99
+
100
+ // Validator identity (local file)
101
+ data.validator = loadIdentity();
102
+
103
+ // Rewards for default wallet (optional)
104
+ const config = loadConfig();
105
+ if (config.defaultWallet) {
106
+ data.defaultWallet = config.defaultWallet;
107
+ try {
108
+ data.rewards = await fetchRewardsSummary(rpc, config.defaultWallet);
109
+ } catch (e) {
110
+ errors.rewards = e.message;
111
+ }
112
+ }
113
+
114
+ if (isJson) {
115
+ console.log(JSON.stringify({ rpc, errors: Object.keys(errors).length ? errors : undefined, ...data }, null, 2));
116
+ return;
117
+ }
118
+
119
+ if (isCompact) {
120
+ printCompact(data, errors);
121
+ return;
122
+ }
123
+
124
+ printDashboard(data, errors, includeValidator, rpc);
125
+ }
126
+
127
+ async function fetchEpochInfo(rpc) {
128
+ const client = createClient(rpc);
129
+ try {
130
+ const epoch = await client.getEpochInfo();
131
+ const currentSlot = epoch.absoluteSlot || epoch.slot || 0;
132
+ const slotsInEpoch = epoch.slotsInEpoch || 432000;
133
+ const slotIndex = epoch.slotIndex || (currentSlot % slotsInEpoch);
134
+ const epochProgress = slotsInEpoch > 0 ? (slotIndex / slotsInEpoch * 100).toFixed(1) : '0';
135
+
136
+ // Estimate time remaining (assuming 400ms slots)
137
+ const slotsRemaining = slotsInEpoch - slotIndex;
138
+ const secsRemaining = Math.round(slotsRemaining * 0.4);
139
+ const minsRemaining = Math.round(secsRemaining / 60);
140
+ const timeStr = minsRemaining >= 60
141
+ ? `${Math.floor(minsRemaining / 60)}h ${minsRemaining % 60}m`
142
+ : `${minsRemaining}m`;
143
+
144
+ return {
145
+ epoch: epoch.epoch || 0,
146
+ absoluteSlot: currentSlot,
147
+ slotIndex,
148
+ slotsInEpoch,
149
+ progress: epochProgress,
150
+ timeRemaining: timeStr,
151
+ totalSlots: epoch.totalSlots || 0,
152
+ };
153
+ } catch (e) {
154
+ throw new Error('Epoch info fetch failed: ' + e.message);
155
+ }
156
+ }
157
+
158
+ async function fetchSupplyInfo(rpc) {
159
+ const client = createClient(rpc);
160
+ try {
161
+ const supply = await client.getSupply();
162
+ const total = BigInt(supply.total || 0);
163
+ const circulating = BigInt(supply.circulating || 0);
164
+ const nonCirculating = BigInt(supply.nonCirculating || 0);
165
+
166
+ return {
167
+ total: total.toString(),
168
+ totalFormatted: formatAether(total.toString()),
169
+ circulating: circulating.toString(),
170
+ circulatingFormatted: formatAether(circulating.toString()),
171
+ nonCirculating: nonCirculating.toString(),
172
+ nonCirculatingFormatted: formatAether(nonCirculating.toString()),
173
+ };
174
+ } catch (e) {
175
+ throw new Error('Supply info fetch failed: ' + e.message);
176
+ }
177
+ }
178
+
179
+ async function fetchNetworkInfo(rpc) {
180
+ const client = createClient(rpc);
181
+ try {
182
+ const [slot, blockHeight, peers] = await Promise.all([
183
+ client.getSlot(),
184
+ client.getBlockHeight(),
185
+ client.getClusterPeers(),
186
+ ]);
187
+
188
+ return {
189
+ blockHeight: blockHeight || slot || 0,
190
+ blockTime: null,
191
+ peerCount: Array.isArray(peers) ? peers.length : 0,
192
+ peers: (Array.isArray(peers) ? peers : []).slice(0, 5),
193
+ };
194
+ } catch (e) {
195
+ throw new Error('Network info fetch failed: ' + e.message);
196
+ }
197
+ }
198
+
199
+ async function fetchVersionInfo(rpc) {
200
+ const client = createClient(rpc);
201
+ try {
202
+ return await client.getVersion();
203
+ } catch {
204
+ return {};
205
+ }
206
+ }
207
+
208
+ async function fetchRewardsSummary(rpc, address) {
209
+ const client = createClient(rpc);
210
+ try {
211
+ // Get stake positions for the address (real RPC call via SDK)
212
+ const stakePositions = await client.getStakePositions(address);
213
+
214
+ if (!stakePositions || stakePositions.length === 0) return null;
215
+
216
+ const rewardsResults = await Promise.all(
217
+ stakePositions.slice(0, 10).map(async (stake) => {
218
+ try {
219
+ const stakeAccount = stake.stakeAccount || stake.account || stake.pubkey || stake;
220
+ const rewards = await client.getRewards(stakeAccount);
221
+ const total = BigInt(rewards.total || rewards.amount || 0);
222
+ return {
223
+ stakeAccount,
224
+ estimatedRewards: total.toString(),
225
+ estimatedRewardsFormatted: formatAether(total.toString())
226
+ };
227
+ } catch {
228
+ return { stakeAccount: stake.stakeAccount || stake, estimatedRewards: '0', estimatedRewardsFormatted: '0 AETH' };
229
+ }
230
+ })
231
+ );
232
+
233
+ let totalRewards = BigInt(0);
234
+ for (const r of rewardsResults) {
235
+ totalRewards += BigInt(r.estimatedRewards);
236
+ }
237
+
238
+ return {
239
+ address,
240
+ totalRewards: totalRewards.toString(),
241
+ totalRewardsFormatted: formatAether(totalRewards.toString()),
242
+ activeAccounts: rewardsResults.filter(r => BigInt(r.estimatedRewards) > 0n).length,
243
+ totalAccounts: rewardsResults.length,
244
+ };
245
+ } catch (e) {
246
+ throw new Error('Rewards fetch failed: ' + e.message);
247
+ }
248
+ }
249
+
250
+ function printDashboard(data, errors, includeValidator, rpc) {
251
+ const { epoch, supply, network, version, validator, rewards, defaultWallet } = data;
252
+
253
+ console.log(`\n${C.bright}${C.cyan} ╔══════════════════════════════════════════════════════════╗${C.reset}`);
254
+ console.log(`${C.bright}${C.cyan} ║ AETHER STATUS DASHBOARD ║${C.reset}`);
255
+ console.log(`${C.bright}${C.cyan} ╚══════════════════════════════════════════════════════════╝${C.reset}\n`);
256
+
257
+ // Epoch row
258
+ if (epoch) {
259
+ console.log(` ${C.bright}Epoch${C.reset} ${C.cyan}E${epoch.epoch}${C.reset} │ Slot ${C.bright}${epoch.absoluteSlot.toLocaleString()}${C.reset} (${epoch.progress}%) │ ${epoch.timeRemaining} remaining`);
260
+ } else {
261
+ console.log(` ${C.red}✗ Epoch info unavailable${errors.epoch ? ': ' + errors.epoch : ''}${C.reset}`);
262
+ }
263
+
264
+ // Network row
265
+ if (network) {
266
+ const peerStr = network.peerCount > 0 ? `${C.green}${network.peerCount} peers${C.reset}` : `${C.yellow}no peers${C.reset}`;
267
+ console.log(` ${C.bright}Network${C.reset} │ Block ${C.bright}${network.blockHeight.toLocaleString()}${C.reset} │ ${peerStr}`);
268
+ } else {
269
+ console.log(` ${C.red}✗ Network info unavailable${errors.network ? ': ' + errors.network : ''}${C.reset}`);
270
+ }
271
+
272
+ // Supply row
273
+ if (supply) {
274
+ console.log(` ${C.bright}Supply${C.reset} │ Total ${C.cyan}${supply.totalFormatted}${C.reset} │ Circulating ${C.green}${supply.circulatingFormatted}${C.reset}`);
275
+ console.log(` │ Staked (non-circulating) ${C.yellow}${supply.nonCirculatingFormatted}${C.reset}`);
276
+ } else {
277
+ console.log(` ${C.red}✗ Supply info unavailable${errors.supply ? ': ' + errors.supply : ''}${C.reset}`);
278
+ }
279
+
280
+ // Version row
281
+ if (version && Object.keys(version).length > 0) {
282
+ console.log(` ${C.bright}Version${C.reset} │ ${C.dim}${JSON.stringify(version)}${C.reset}`);
283
+ }
284
+
285
+ // Validator row
286
+ if (includeValidator && validator) {
287
+ const identity = validator.identity || validator.nodeKey || 'unknown';
288
+ const shortId = identity.length > 16 ? identity.substring(0, 16) + '...' : identity;
289
+ const stake = validator.delegatedStake ? formatAether(validator.delegatedStake) : 'unknown';
290
+ console.log(` ${C.bright}Validator${C.reset} │ ${C.magenta}${shortId}${C.reset} │ Stake: ${stake}`);
291
+ } else if (includeValidator && !validator) {
292
+ console.log(` ${C.bright}Validator${C.reset} │ ${C.yellow}No validator identity found (run aether init)${C.reset}`);
293
+ }
294
+
295
+ // Rewards row
296
+ if (rewards && defaultWallet) {
297
+ const shortAddr = defaultWallet.length > 16 ? defaultWallet.substring(0, 16) + '...' : defaultWallet;
298
+ console.log(` ${C.bright}Rewards${C.reset} │ ${C.green}${rewards.totalRewardsFormatted}${C.reset} est. │ Wallet: ${C.dim}${shortAddr}${C.reset}`);
299
+ }
300
+
301
+ console.log(` ${C.dim}RPC: ${rpc || getDefaultRpc()}${C.reset}\n`);
302
+ }
303
+
304
+ function printCompact(data, errors) {
305
+ const parts = [];
306
+ if (data.epoch) parts.push(`E${data.epoch.epoch}`);
307
+ if (data.network) parts.push(`blk ${data.network.blockHeight}`);
308
+ if (data.network) parts.push(`p${data.network.peerCount}`);
309
+ if (data.supply) parts.push(`total ${data.supply.totalFormatted}`);
310
+ if (data.rewards) parts.push(`rwd ${data.rewards.totalRewardsFormatted}`);
311
+ if (Object.keys(errors).length > 0) parts.push(`err:${Object.keys(errors).join(',')}`);
312
+ console.log(parts.join(' │ '));
313
+ }
314
+
315
+ // ---------------------------------------------------------------------------
316
+ // Entry point
317
+ // ---------------------------------------------------------------------------
318
+
319
+ module.exports = { statusCommand };
320
+
321
+ // Run directly
322
+ if (require.main === module) {
323
+ statusCommand().catch(err => {
324
+ console.error(`${C.red}✗ Status command failed:${C.reset} ${err.message}`);
325
+ process.exit(1);
326
+ });
327
+ }