@jellylegsai/aether-cli 1.9.2 → 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 +2 -0
- 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 -602
- package/lib/ui.js +623 -0
- package/package.json +10 -3
- package/sdk/index.d.ts +546 -0
- package/sdk/index.js +130 -0
- package/sdk/package.json +2 -1
package/lib/ui.js
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aether-cli/lib/ui.js
|
|
3
|
+
*
|
|
4
|
+
* Unified UI/UX Framework for Aether CLI
|
|
5
|
+
*
|
|
6
|
+
* Provides:
|
|
7
|
+
* - Consistent ASCII art branding
|
|
8
|
+
* - Standardized color palette
|
|
9
|
+
* - Status indicators and spinners
|
|
10
|
+
* - Progress feedback helpers
|
|
11
|
+
* - Box drawing utilities
|
|
12
|
+
* - Table formatting
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// ANSI Color Palette - Consistent across all commands
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
const colors = {
|
|
20
|
+
// Standard colors
|
|
21
|
+
reset: '\x1b[0m',
|
|
22
|
+
bright: '\x1b[1m',
|
|
23
|
+
dim: '\x1b[2m',
|
|
24
|
+
underscore: '\x1b[4m',
|
|
25
|
+
|
|
26
|
+
// Foreground colors
|
|
27
|
+
black: '\x1b[30m',
|
|
28
|
+
red: '\x1b[31m',
|
|
29
|
+
green: '\x1b[32m',
|
|
30
|
+
yellow: '\x1b[33m',
|
|
31
|
+
blue: '\x1b[34m',
|
|
32
|
+
magenta: '\x1b[35m',
|
|
33
|
+
cyan: '\x1b[36m',
|
|
34
|
+
white: '\x1b[37m',
|
|
35
|
+
|
|
36
|
+
// Bright variants
|
|
37
|
+
brightRed: '\x1b[91m',
|
|
38
|
+
brightGreen: '\x1b[92m',
|
|
39
|
+
brightYellow: '\x1b[93m',
|
|
40
|
+
brightBlue: '\x1b[94m',
|
|
41
|
+
brightMagenta: '\x1b[95m',
|
|
42
|
+
brightCyan: '\x1b[96m',
|
|
43
|
+
brightWhite: '\x1b[97m',
|
|
44
|
+
|
|
45
|
+
// Background colors
|
|
46
|
+
bgBlack: '\x1b[40m',
|
|
47
|
+
bgRed: '\x1b[41m',
|
|
48
|
+
bgGreen: '\x1b[42m',
|
|
49
|
+
bgYellow: '\x1b[43m',
|
|
50
|
+
bgBlue: '\x1b[44m',
|
|
51
|
+
bgMagenta: '\x1b[45m',
|
|
52
|
+
bgCyan: '\x1b[46m',
|
|
53
|
+
bgWhite: '\x1b[47m',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Short aliases for convenience
|
|
57
|
+
const C = colors;
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// ASCII Art Branding
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
const BRANDING = {
|
|
64
|
+
// Main Aether logo - cosmic blockchain aesthetic
|
|
65
|
+
logo: `
|
|
66
|
+
${C.cyan} ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ${C.reset}
|
|
67
|
+
${C.cyan} ▄▀░░░░░░░░░░░░░░░░░░░░░░░▀▄ ${C.reset}
|
|
68
|
+
${C.cyan} ▄▀░░░░░░░░░${C.bright}◆${C.cyan}░░░░░░░░░░░░░░░░░▀▄ ${C.reset}
|
|
69
|
+
${C.cyan} █░░░░░${C.blue}╔═╗╔═╗╔╦╗╔═╗╔═╗╦═╗${C.cyan}░░░░░░░█ ${C.reset}
|
|
70
|
+
${C.cyan} █░░░░░░${C.blue}╠═╣╠╦╝║║║║ ╦║╣ ╠╦╝${C.cyan}░░░░░░░░█ ${C.reset}
|
|
71
|
+
${C.cyan} █░░░░░░${C.blue}╩ ╩╩╚═╩ ╩╚═╝╚═╝╩╚═${C.cyan}░░░░░░░░█ ${C.reset}
|
|
72
|
+
${C.cyan} █░░░░░░░░░${C.bright}◆${C.cyan}░░░░░░░░░░░░░░░░░█ ${C.reset}
|
|
73
|
+
${C.cyan} ▀▄░░░░░░░░░░░░░░░░░░░░░▄▀ ${C.reset}
|
|
74
|
+
${C.cyan} ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ${C.reset}`,
|
|
75
|
+
|
|
76
|
+
// Compact logo for tight spaces
|
|
77
|
+
logoCompact: `
|
|
78
|
+
${C.cyan} ╔═╗╔═╗╔╦╗╔═╗╔═╗╦═╗${C.reset}
|
|
79
|
+
${C.cyan} ╠═╣╠╦╝║║║║ ╦║╣ ╠╦╝${C.reset}
|
|
80
|
+
${C.cyan} ╩ ╩╩╚═╩ ╩╚═╝╚═╝╩╚═${C.reset}`,
|
|
81
|
+
|
|
82
|
+
// Validator node branding
|
|
83
|
+
validatorLogo: `
|
|
84
|
+
${C.cyan} ▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄ ${C.reset}
|
|
85
|
+
${C.cyan} █ ${C.bright}${C.cyan}AETHER${C.reset} ${C.bright}VALIDATOR NODE${C.reset} █ ${C.reset}
|
|
86
|
+
${C.cyan} █ █ ${C.reset}
|
|
87
|
+
${C.cyan} ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ${C.reset}`,
|
|
88
|
+
|
|
89
|
+
// CLI header with version - cosmic theme
|
|
90
|
+
header: (version) => `
|
|
91
|
+
${C.cyan}╔══════════════════════════════════════════════════════════════════════════╗${C.reset}
|
|
92
|
+
${C.cyan}║${C.reset} ${C.cyan}║${C.reset}
|
|
93
|
+
${C.cyan}║${C.reset} ${C.cyan}╔═╗${C.bright}${C.cyan}AETHER${C.reset}${C.cyan}╔═╗${C.reset} ${C.bright}BLOCKCHAIN CLI${C.reset}${' '.repeat(34 - version.length)}${C.dim}v${version}${C.reset} ${C.cyan}║${C.reset}
|
|
94
|
+
${C.cyan}║${C.reset} ${C.cyan}╠═╣${C.bright}${C.cyan}NETWORK${C.reset}${C.cyan}╠═╣${C.reset} ${C.dim}◆ Decentralized Infrastructure for the Future ◆${C.reset} ${C.cyan}║${C.reset}
|
|
95
|
+
${C.cyan}║${C.reset} ${C.cyan}╚═╝${C.reset}${' '.repeat(10)}${C.cyan}╚═╝${C.reset} ${C.cyan}║${C.reset}
|
|
96
|
+
${C.cyan}║${C.reset} ${C.cyan}║${C.reset}
|
|
97
|
+
${C.cyan}╚══════════════════════════════════════════════════════════════════════════╝${C.reset}`,
|
|
98
|
+
|
|
99
|
+
// Subtle header without borders
|
|
100
|
+
headerMinimal: (version) => `
|
|
101
|
+
${C.cyan} ◆ AETHER CLI${C.reset} ${C.dim}v${version}${C.reset} ${C.dim}─ Decentralized Infrastructure ◆${C.reset}
|
|
102
|
+
`,
|
|
103
|
+
|
|
104
|
+
// Section headers - cosmic dividers
|
|
105
|
+
section: (title) => `
|
|
106
|
+
${C.cyan} ════════════════════════════════════════════════════════════════════${C.reset}
|
|
107
|
+
${C.cyan} ◆${C.reset} ${C.bright}${title.toUpperCase()}${C.reset}
|
|
108
|
+
${C.cyan} ────────────────────────────────────────────────────────────────────${C.reset}`,
|
|
109
|
+
|
|
110
|
+
// Subsection
|
|
111
|
+
subsection: (title) => `
|
|
112
|
+
${C.dim}┌─ ${C.bright}${C.cyan}${title}${C.reset}${C.dim} ─────────────────────────────────────────┐${C.reset}`,
|
|
113
|
+
|
|
114
|
+
// Command banner
|
|
115
|
+
commandBanner: (cmd, desc) => `
|
|
116
|
+
${C.cyan}┌──────────────────────────────────────────────────────────────────────────┐${C.reset}
|
|
117
|
+
${C.cyan}│${C.reset} ${C.bright}${C.cyan}COMMAND:${C.reset} ${C.bright}${cmd}${C.reset}${' '.repeat(60 - cmd.length)}${C.cyan}│${C.reset}
|
|
118
|
+
${C.cyan}│${C.reset} ${C.dim}${desc}${C.reset}${' '.repeat(66 - desc.length)}${C.cyan}│${C.reset}
|
|
119
|
+
${C.cyan}└──────────────────────────────────────────────────────────────────────────┘${C.reset}`,
|
|
120
|
+
|
|
121
|
+
// Welcome banner for init
|
|
122
|
+
welcomeBanner: `
|
|
123
|
+
${C.cyan}╔══════════════════════════════════════════════════════════════════════════╗${C.reset}
|
|
124
|
+
${C.cyan}║${C.reset} ${C.cyan}║${C.reset}
|
|
125
|
+
${C.cyan}║${C.reset} ${C.cyan}╔═╗${C.bright}${C.cyan}WELCOME TO AETHER${C.reset}${C.cyan}╔═╗${C.reset}${' '.repeat(39)}${C.cyan}║${C.reset}
|
|
126
|
+
${C.cyan}║${C.reset} ${C.cyan}╚═╝${C.reset} ${C.dim}Validator Onboarding & Node Management${C.reset} ${C.cyan}║${C.reset}
|
|
127
|
+
${C.cyan}║${C.reset} ${C.cyan}║${C.reset}
|
|
128
|
+
${C.cyan}╚══════════════════════════════════════════════════════════════════════════╝${C.reset}`,
|
|
129
|
+
|
|
130
|
+
// Success banner
|
|
131
|
+
successBanner: (text) => `
|
|
132
|
+
${C.cyan}╔══════════════════════════════════════════════════════════════════════════╗${C.reset}
|
|
133
|
+
${C.cyan}║${C.reset} ${C.green}${C.bright}✓ ${text}${C.reset}${' '.repeat(66 - text.length)}${C.cyan}║${C.reset}
|
|
134
|
+
${C.cyan}╚══════════════════════════════════════════════════════════════════════════╝${C.reset}`,
|
|
135
|
+
|
|
136
|
+
// Error banner
|
|
137
|
+
errorBanner: (text) => `
|
|
138
|
+
${C.cyan}╔══════════════════════════════════════════════════════════════════════════╗${C.reset}
|
|
139
|
+
${C.cyan}║${C.reset} ${C.red}${C.bright}✗ ${text}${C.reset}${' '.repeat(66 - text.length)}${C.cyan}║${C.reset}
|
|
140
|
+
${C.cyan}╚══════════════════════════════════════════════════════════════════════════╝${C.reset}`,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// Status Indicators - Consistent across all commands
|
|
145
|
+
// ============================================================================
|
|
146
|
+
|
|
147
|
+
const indicators = {
|
|
148
|
+
// Success states
|
|
149
|
+
success: `${C.green}✓${C.reset}`,
|
|
150
|
+
successBright: `${C.green}${C.bright}✓${C.reset}`,
|
|
151
|
+
successBox: `${C.green}[✓]${C.reset}`,
|
|
152
|
+
|
|
153
|
+
// Error states
|
|
154
|
+
error: `${C.red}✗${C.reset}`,
|
|
155
|
+
errorBright: `${C.red}${C.bright}✗${C.reset}`,
|
|
156
|
+
errorBox: `${C.red}[✗]${C.reset}`,
|
|
157
|
+
|
|
158
|
+
// Warning states
|
|
159
|
+
warning: `${C.yellow}⚠${C.reset}`,
|
|
160
|
+
warningBright: `${C.yellow}${C.bright}⚠${C.reset}`,
|
|
161
|
+
warningBox: `${C.yellow}[⚠]${C.reset}`,
|
|
162
|
+
|
|
163
|
+
// Info states
|
|
164
|
+
info: `${C.cyan}ℹ${C.reset}`,
|
|
165
|
+
infoBright: `${C.cyan}${C.bright}ℹ${C.reset}`,
|
|
166
|
+
infoBox: `${C.cyan}[ℹ]${C.reset}`,
|
|
167
|
+
|
|
168
|
+
// Progress/loading
|
|
169
|
+
bullet: `${C.cyan}●${C.reset}`,
|
|
170
|
+
bulletDim: `${C.dim}●${C.reset}`,
|
|
171
|
+
arrow: `${C.cyan}→${C.reset}`,
|
|
172
|
+
arrowRight: `${C.cyan}→${C.reset}`,
|
|
173
|
+
arrowLeft: `${C.cyan}←${C.reset}`,
|
|
174
|
+
|
|
175
|
+
// Network/connection
|
|
176
|
+
connected: `${C.green}●${C.reset}`,
|
|
177
|
+
disconnected: `${C.red}●${C.reset}`,
|
|
178
|
+
syncing: `${C.yellow}◐${C.reset}`,
|
|
179
|
+
|
|
180
|
+
// Checkboxes
|
|
181
|
+
checked: `${C.green}[✓]${C.reset}`,
|
|
182
|
+
unchecked: `${C.dim}[ ]${C.reset}`,
|
|
183
|
+
|
|
184
|
+
// Stars/ratings
|
|
185
|
+
star: `${C.yellow}★${C.reset}`,
|
|
186
|
+
starDim: `${C.dim}★${C.reset}`,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// ============================================================================
|
|
190
|
+
// Box Drawing - Consistent borders
|
|
191
|
+
// ============================================================================
|
|
192
|
+
|
|
193
|
+
const box = {
|
|
194
|
+
// Single line borders
|
|
195
|
+
single: {
|
|
196
|
+
topLeft: '┌', topRight: '┐', bottomLeft: '└', bottomRight: '┘',
|
|
197
|
+
horizontal: '─', vertical: '│',
|
|
198
|
+
leftT: '├', rightT: '┤', topT: '┬', bottomT: '┴', cross: '┼'
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Double line borders
|
|
202
|
+
double: {
|
|
203
|
+
topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝',
|
|
204
|
+
horizontal: '═', vertical: '║',
|
|
205
|
+
leftT: '╠', rightT: '╣', topT: '╦', bottomT: '╩', cross: '╬'
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// Rounded corners
|
|
209
|
+
rounded: {
|
|
210
|
+
topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯',
|
|
211
|
+
horizontal: '─', vertical: '│'
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// Thick borders
|
|
215
|
+
thick: {
|
|
216
|
+
topLeft: '▛', topRight: '▜', bottomLeft: '▙', bottomRight: '▟',
|
|
217
|
+
horizontal: '━', vertical: '┃'
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// Message Helpers
|
|
223
|
+
// ============================================================================
|
|
224
|
+
|
|
225
|
+
function success(text) {
|
|
226
|
+
return `${indicators.success} ${text}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function error(text) {
|
|
230
|
+
return `${indicators.error} ${text}`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function warning(text) {
|
|
234
|
+
return `${indicators.warning} ${text}`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function info(text) {
|
|
238
|
+
return `${indicators.info} ${text}`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function dim(text) {
|
|
242
|
+
return `${C.dim}${text}${C.reset}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function bright(text) {
|
|
246
|
+
return `${C.bright}${text}${C.reset}`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function highlight(text) {
|
|
250
|
+
return `${C.cyan}${C.bright}${text}${C.reset}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function code(text) {
|
|
254
|
+
return `${C.cyan}${text}${C.reset}`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function key(text) {
|
|
258
|
+
return `${C.yellow}${C.bright}${text}${C.reset}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function value(text) {
|
|
262
|
+
return `${C.green}${text}${C.reset}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ============================================================================
|
|
266
|
+
// Progress/Spinner Helpers
|
|
267
|
+
// ============================================================================
|
|
268
|
+
|
|
269
|
+
const spinnerFrames = ['◐', '◓', '◑', '◒'];
|
|
270
|
+
let spinnerInterval = null;
|
|
271
|
+
let spinnerIndex = 0;
|
|
272
|
+
|
|
273
|
+
function startSpinner(text = 'Loading') {
|
|
274
|
+
if (spinnerInterval) clearSpinner();
|
|
275
|
+
|
|
276
|
+
spinnerIndex = 0;
|
|
277
|
+
process.stdout.write(` ${C.dim}${spinnerFrames[0]}${C.reset} ${text}...`);
|
|
278
|
+
|
|
279
|
+
spinnerInterval = setInterval(() => {
|
|
280
|
+
spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
|
|
281
|
+
process.stdout.write(`\r ${C.cyan}${spinnerFrames[spinnerIndex]}${C.reset} ${text}...`);
|
|
282
|
+
}, 120);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function updateSpinner(text) {
|
|
286
|
+
if (spinnerInterval) {
|
|
287
|
+
process.stdout.write(`\r ${C.cyan}${spinnerFrames[spinnerIndex]}${C.reset} ${text}...`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function stopSpinner(success = true, finalText = null) {
|
|
292
|
+
if (spinnerInterval) {
|
|
293
|
+
clearInterval(spinnerInterval);
|
|
294
|
+
spinnerInterval = null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const icon = success ? indicators.success : indicators.error;
|
|
298
|
+
const text = finalText || (success ? 'Done' : 'Failed');
|
|
299
|
+
process.stdout.write(`\r ${icon} ${text}\n`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function clearSpinner() {
|
|
303
|
+
if (spinnerInterval) {
|
|
304
|
+
clearInterval(spinnerInterval);
|
|
305
|
+
spinnerInterval = null;
|
|
306
|
+
process.stdout.write('\r' + ' '.repeat(80) + '\r');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ============================================================================
|
|
311
|
+
// Progress Bar
|
|
312
|
+
// ============================================================================
|
|
313
|
+
|
|
314
|
+
function progressBar(current, total, width = 40) {
|
|
315
|
+
const pct = Math.min(100, Math.max(0, (current / total) * 100));
|
|
316
|
+
const filled = Math.round((pct / 100) * width);
|
|
317
|
+
const empty = width - filled;
|
|
318
|
+
|
|
319
|
+
const filledBar = C.green + '█'.repeat(filled) + C.reset;
|
|
320
|
+
const emptyBar = C.dim + '░'.repeat(empty) + C.reset;
|
|
321
|
+
|
|
322
|
+
return `[${filledBar}${emptyBar}] ${C.bright}${pct.toFixed(1)}%${C.reset}`;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function progressBarColored(current, total, width = 40) {
|
|
326
|
+
const pct = Math.min(100, Math.max(0, (current / total) * 100));
|
|
327
|
+
const filled = Math.round((pct / 100) * width);
|
|
328
|
+
const empty = width - filled;
|
|
329
|
+
|
|
330
|
+
// Color based on progress
|
|
331
|
+
let color = C.red;
|
|
332
|
+
if (pct > 30) color = C.yellow;
|
|
333
|
+
if (pct > 70) color = C.green;
|
|
334
|
+
|
|
335
|
+
const filledBar = color + '█'.repeat(filled) + C.reset;
|
|
336
|
+
const emptyBar = C.dim + '░'.repeat(empty) + C.reset;
|
|
337
|
+
|
|
338
|
+
return `[${filledBar}${emptyBar}] ${C.bright}${pct.toFixed(1)}%${C.reset}`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ============================================================================
|
|
342
|
+
// Box Drawing Functions
|
|
343
|
+
// ============================================================================
|
|
344
|
+
|
|
345
|
+
function drawBox(content, options = {}) {
|
|
346
|
+
const {
|
|
347
|
+
style = 'single',
|
|
348
|
+
padding = 1,
|
|
349
|
+
width = null,
|
|
350
|
+
title = null,
|
|
351
|
+
titleColor = C.cyan,
|
|
352
|
+
borderColor = C.dim,
|
|
353
|
+
align = 'left'
|
|
354
|
+
} = options;
|
|
355
|
+
|
|
356
|
+
const lines = content.split('\n');
|
|
357
|
+
const maxWidth = width || Math.max(...lines.map(l => stripAnsi(l).length));
|
|
358
|
+
const b = box[style];
|
|
359
|
+
|
|
360
|
+
let result = '';
|
|
361
|
+
|
|
362
|
+
// Top border with optional title
|
|
363
|
+
if (title) {
|
|
364
|
+
const titleText = ` ${title} `;
|
|
365
|
+
const sideWidth = Math.max(0, (maxWidth + padding * 2 - stripAnsi(titleText).length) / 2);
|
|
366
|
+
const leftPad = b.horizontal.repeat(Math.floor(sideWidth));
|
|
367
|
+
const rightPad = b.horizontal.repeat(Math.ceil(sideWidth));
|
|
368
|
+
result += `${borderColor}${b.topLeft}${leftPad}${titleColor}${titleText}${borderColor}${rightPad}${b.topRight}${C.reset}\n`;
|
|
369
|
+
} else {
|
|
370
|
+
result += `${borderColor}${b.topLeft}${b.horizontal.repeat(maxWidth + padding * 2)}${b.topRight}${C.reset}\n`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Content lines
|
|
374
|
+
for (const line of lines) {
|
|
375
|
+
const stripped = stripAnsi(line);
|
|
376
|
+
const padLeft = ' '.repeat(padding);
|
|
377
|
+
const padRight = ' '.repeat(maxWidth - stripped.length + padding);
|
|
378
|
+
result += `${borderColor}${b.vertical}${C.reset}${padLeft}${line}${padRight}${borderColor}${b.vertical}${C.reset}\n`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Bottom border
|
|
382
|
+
result += `${borderColor}${b.bottomLeft}${b.horizontal.repeat(maxWidth + padding * 2)}${b.bottomRight}${C.reset}`;
|
|
383
|
+
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function drawTable(headers, rows, options = {}) {
|
|
388
|
+
const {
|
|
389
|
+
borderStyle = 'single',
|
|
390
|
+
headerColor = C.cyan + C.bright,
|
|
391
|
+
borderColor = C.dim,
|
|
392
|
+
cellPadding = 1
|
|
393
|
+
} = options;
|
|
394
|
+
|
|
395
|
+
// Calculate column widths
|
|
396
|
+
const colWidths = headers.map((h, i) => {
|
|
397
|
+
const headerWidth = stripAnsi(h).length;
|
|
398
|
+
const maxDataWidth = Math.max(...rows.map(r => stripAnsi(String(r[i] || '')).length));
|
|
399
|
+
return Math.max(headerWidth, maxDataWidth) + cellPadding * 2;
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
const b = box[borderStyle];
|
|
403
|
+
let result = '';
|
|
404
|
+
|
|
405
|
+
// Top border
|
|
406
|
+
const topLine = colWidths.map(w => b.horizontal.repeat(w)).join(b.topT);
|
|
407
|
+
result += `${borderColor}${b.topLeft}${topLine}${b.topRight}${C.reset}\n`;
|
|
408
|
+
|
|
409
|
+
// Header row
|
|
410
|
+
const headerLine = headers.map((h, i) => {
|
|
411
|
+
const pad = colWidths[i] - stripAnsi(h).length;
|
|
412
|
+
const left = Math.floor(pad / 2);
|
|
413
|
+
const right = pad - left;
|
|
414
|
+
return ' '.repeat(left) + headerColor + h + C.reset + ' '.repeat(right);
|
|
415
|
+
}).join(borderColor + b.vertical + C.reset);
|
|
416
|
+
result += `${borderColor}${b.vertical}${C.reset}${headerLine}${borderColor}${b.vertical}${C.reset}\n`;
|
|
417
|
+
|
|
418
|
+
// Separator
|
|
419
|
+
const sepLine = colWidths.map(w => b.horizontal.repeat(w)).join(b.cross);
|
|
420
|
+
result += `${borderColor}${b.leftT}${sepLine}${b.rightT}${C.reset}\n`;
|
|
421
|
+
|
|
422
|
+
// Data rows
|
|
423
|
+
for (const row of rows) {
|
|
424
|
+
const line = row.map((cell, i) => {
|
|
425
|
+
const text = String(cell || '');
|
|
426
|
+
const pad = colWidths[i] - stripAnsi(text).length;
|
|
427
|
+
const left = cellPadding;
|
|
428
|
+
const right = pad - left;
|
|
429
|
+
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
430
|
+
}).join(borderColor + b.vertical + C.reset);
|
|
431
|
+
result += `${borderColor}${b.vertical}${C.reset}${line}${borderColor}${b.vertical}${C.reset}\n`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Bottom border
|
|
435
|
+
const bottomLine = colWidths.map(w => b.horizontal.repeat(w)).join(b.bottomT);
|
|
436
|
+
result += `${borderColor}${b.bottomLeft}${bottomLine}${b.bottomRight}${C.reset}`;
|
|
437
|
+
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ============================================================================
|
|
442
|
+
// Utility Functions
|
|
443
|
+
// ============================================================================
|
|
444
|
+
|
|
445
|
+
function stripAnsi(str) {
|
|
446
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function center(text, width) {
|
|
450
|
+
const stripped = stripAnsi(text);
|
|
451
|
+
const pad = Math.max(0, width - stripped.length);
|
|
452
|
+
const left = Math.floor(pad / 2);
|
|
453
|
+
const right = pad - left;
|
|
454
|
+
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function pad(text, width, align = 'left') {
|
|
458
|
+
const stripped = stripAnsi(text);
|
|
459
|
+
const padLen = Math.max(0, width - stripped.length);
|
|
460
|
+
|
|
461
|
+
if (align === 'right') return ' '.repeat(padLen) + text;
|
|
462
|
+
if (align === 'center') {
|
|
463
|
+
const left = Math.floor(padLen / 2);
|
|
464
|
+
const right = padLen - left;
|
|
465
|
+
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
466
|
+
}
|
|
467
|
+
return text + ' '.repeat(padLen);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function truncate(text, maxLen, suffix = '...') {
|
|
471
|
+
const stripped = stripAnsi(text);
|
|
472
|
+
if (stripped.length <= maxLen) return text;
|
|
473
|
+
return stripped.slice(0, maxLen - suffix.length) + suffix;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function wrap(text, width) {
|
|
477
|
+
const words = text.split(' ');
|
|
478
|
+
const lines = [];
|
|
479
|
+
let currentLine = '';
|
|
480
|
+
|
|
481
|
+
for (const word of words) {
|
|
482
|
+
if (stripAnsi(currentLine + ' ' + word).length > width) {
|
|
483
|
+
lines.push(currentLine);
|
|
484
|
+
currentLine = word;
|
|
485
|
+
} else {
|
|
486
|
+
currentLine += (currentLine ? ' ' : '') + word;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (currentLine) lines.push(currentLine);
|
|
490
|
+
|
|
491
|
+
return lines;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// ============================================================================
|
|
495
|
+
// Help Formatting
|
|
496
|
+
// ============================================================================
|
|
497
|
+
|
|
498
|
+
function formatHelp(title, description, usage, options, examples) {
|
|
499
|
+
let result = '';
|
|
500
|
+
|
|
501
|
+
// Title
|
|
502
|
+
result += `\n${C.cyan}${C.bright}${title}${C.reset}\n`;
|
|
503
|
+
|
|
504
|
+
// Description
|
|
505
|
+
if (description) {
|
|
506
|
+
result += `\n${C.dim}${description}${C.reset}\n`;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Usage
|
|
510
|
+
if (usage) {
|
|
511
|
+
result += `\n${C.bright}USAGE${C.reset}\n`;
|
|
512
|
+
result += ` ${C.cyan}${usage}${C.reset}\n`;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Options
|
|
516
|
+
if (options && options.length > 0) {
|
|
517
|
+
result += `\n${C.bright}OPTIONS${C.reset}\n`;
|
|
518
|
+
const maxFlagLen = Math.max(...options.map(o => stripAnsi(o.flag).length));
|
|
519
|
+
for (const opt of options) {
|
|
520
|
+
result += ` ${C.cyan}${pad(opt.flag, maxFlagLen)}${C.reset} ${opt.desc}\n`;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Examples
|
|
525
|
+
if (examples && examples.length > 0) {
|
|
526
|
+
result += `\n${C.bright}EXAMPLES${C.reset}\n`;
|
|
527
|
+
for (const ex of examples) {
|
|
528
|
+
result += ` ${C.dim}$${C.reset} ${C.cyan}${ex.cmd}${C.reset}\n`;
|
|
529
|
+
if (ex.desc) {
|
|
530
|
+
result += ` ${C.dim}${' '.repeat(ex.cmd.length + 1)}${ex.desc}${C.reset}\n`;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
result += '\n';
|
|
536
|
+
return result;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// ============================================================================
|
|
540
|
+
// Network Status Helpers
|
|
541
|
+
// ============================================================================
|
|
542
|
+
|
|
543
|
+
function formatLatency(ms) {
|
|
544
|
+
if (ms < 50) return `${C.green}${ms}ms${C.reset}`;
|
|
545
|
+
if (ms < 150) return `${C.yellow}${ms}ms${C.reset}`;
|
|
546
|
+
return `${C.red}${ms}ms${C.reset}`;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function formatHealth(status) {
|
|
550
|
+
if (!status) return `${C.dim}unknown${C.reset}`;
|
|
551
|
+
const s = status.toLowerCase();
|
|
552
|
+
if (s === 'ok' || s === 'healthy') return `${C.green}●${C.reset} ${C.green}${status}${C.reset}`;
|
|
553
|
+
if (s === 'degraded') return `${C.yellow}●${C.reset} ${C.yellow}${status}${C.reset}`;
|
|
554
|
+
return `${C.red}●${C.reset} ${C.red}${status}${C.reset}`;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function formatSyncStatus(currentSlot, targetSlot) {
|
|
558
|
+
if (!currentSlot || !targetSlot) return `${C.dim}unknown${C.reset}`;
|
|
559
|
+
const pct = (currentSlot / targetSlot) * 100;
|
|
560
|
+
const behind = targetSlot - currentSlot;
|
|
561
|
+
|
|
562
|
+
if (pct >= 99.9) return `${C.green}synced${C.reset}`;
|
|
563
|
+
if (pct >= 95) return `${C.yellow}syncing${C.reset} ${C.dim}(${behind} behind)${C.reset}`;
|
|
564
|
+
return `${C.red}syncing${C.reset} ${C.dim}(${behind} behind)${C.reset}`;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// ============================================================================
|
|
568
|
+
// Export
|
|
569
|
+
// ============================================================================
|
|
570
|
+
|
|
571
|
+
module.exports = {
|
|
572
|
+
// Colors
|
|
573
|
+
colors,
|
|
574
|
+
C,
|
|
575
|
+
|
|
576
|
+
// Branding
|
|
577
|
+
BRANDING,
|
|
578
|
+
|
|
579
|
+
// Indicators
|
|
580
|
+
indicators,
|
|
581
|
+
|
|
582
|
+
// Box characters
|
|
583
|
+
box,
|
|
584
|
+
|
|
585
|
+
// Message helpers
|
|
586
|
+
success,
|
|
587
|
+
error,
|
|
588
|
+
warning,
|
|
589
|
+
info,
|
|
590
|
+
dim,
|
|
591
|
+
bright,
|
|
592
|
+
highlight,
|
|
593
|
+
code,
|
|
594
|
+
key,
|
|
595
|
+
value,
|
|
596
|
+
|
|
597
|
+
// Spinner/progress
|
|
598
|
+
startSpinner,
|
|
599
|
+
updateSpinner,
|
|
600
|
+
stopSpinner,
|
|
601
|
+
clearSpinner,
|
|
602
|
+
progressBar,
|
|
603
|
+
progressBarColored,
|
|
604
|
+
|
|
605
|
+
// Box drawing
|
|
606
|
+
drawBox,
|
|
607
|
+
drawTable,
|
|
608
|
+
|
|
609
|
+
// Utilities
|
|
610
|
+
stripAnsi,
|
|
611
|
+
center,
|
|
612
|
+
pad,
|
|
613
|
+
truncate,
|
|
614
|
+
wrap,
|
|
615
|
+
|
|
616
|
+
// Help formatting
|
|
617
|
+
formatHelp,
|
|
618
|
+
|
|
619
|
+
// Network helpers
|
|
620
|
+
formatLatency,
|
|
621
|
+
formatHealth,
|
|
622
|
+
formatSyncStatus,
|
|
623
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jellylegsai/aether-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Aether CLI - Validator Onboarding, Staking, and Blockchain Operations",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"aether": "index.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"
|
|
11
|
+
"build": "echo 'CLI package requires no build step'",
|
|
12
|
+
"test": "cd test && node doctor.test.js && cd ../sdk && node test.js",
|
|
12
13
|
"lint": "echo 'No linting configured'",
|
|
13
14
|
"doctor": "node index.js doctor",
|
|
14
15
|
"init": "node index.js init",
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
"tx-history": "node index.js tx-history",
|
|
36
37
|
"tx": "node index.js tx-history",
|
|
37
38
|
"network": "node index.js network",
|
|
39
|
+
"network-diagnostics": "node index.js network-diagnostics",
|
|
38
40
|
"epoch": "node index.js epoch",
|
|
39
41
|
"supply": "node index.js supply",
|
|
40
42
|
"status": "node index.js status",
|
|
@@ -47,6 +49,11 @@
|
|
|
47
49
|
"emergency": "node index.js emergency",
|
|
48
50
|
"snapshot": "node index.js snapshot",
|
|
49
51
|
"nft": "node index.js nft",
|
|
52
|
+
"deploy": "node index.js deploy",
|
|
53
|
+
"call": "node index.js call",
|
|
54
|
+
"blockheight": "node index.js blockheight",
|
|
55
|
+
"token-accounts": "node index.js token-accounts",
|
|
56
|
+
"version": "node index.js version",
|
|
50
57
|
"prepare-publish": "npm pack && echo 'Package ready for publishing'",
|
|
51
58
|
"version-bump": "node -e \"const fs=require('fs');let p=JSON.parse(fs.readFileSync('package.json'));let v=p.version.split('.');v[2]=parseInt(v[2])+1;p.version=v.join('.');fs.writeFileSync('package.json',JSON.stringify(p,null,2));console.log('Version bumped to',p.version);\""
|
|
52
59
|
},
|
|
@@ -73,4 +80,4 @@
|
|
|
73
80
|
"tweetnacl": "^1.0.3",
|
|
74
81
|
"bs58": "^5.0.0"
|
|
75
82
|
}
|
|
76
|
-
}
|
|
83
|
+
}
|