gpxe 1.0.1 → 2.0.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.
- package/package.json +7 -2
- package/src/commands/reserve.js +8 -4
- package/src/commands/status.js +52 -48
- package/src/main.js +2 -3
- package/src/ui.js +111 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gpxe",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "CLI-first telemetry for Gridpoint Analytics.",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,10 +19,15 @@
|
|
|
19
19
|
"type": "commonjs",
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"axios": "^1.13.2",
|
|
22
|
+
"boxen": "^5.1.2",
|
|
22
23
|
"chalk": "^4.1.2",
|
|
24
|
+
"cli-table3": "^0.6.5",
|
|
23
25
|
"commander": "^14.0.2",
|
|
24
26
|
"conf": "^15.0.2",
|
|
25
|
-
"enquirer": "^2.4.1"
|
|
27
|
+
"enquirer": "^2.4.1",
|
|
28
|
+
"figlet": "^1.9.4",
|
|
29
|
+
"gradient-string": "^3.0.0",
|
|
30
|
+
"ora": "^5.4.1"
|
|
26
31
|
},
|
|
27
32
|
"bin": {
|
|
28
33
|
"gpxe": "bin/index.js"
|
package/src/commands/reserve.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { program } = require('commander');
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
+
const ui = require('../ui');
|
|
4
5
|
|
|
5
6
|
program
|
|
6
7
|
.command('reserve')
|
|
@@ -10,8 +11,11 @@ program
|
|
|
10
11
|
const randomBytes = crypto.randomBytes(4).toString('hex').toUpperCase(); // 8 chars
|
|
11
12
|
const token = `GP-${randomBytes.slice(0, 4)}-${randomBytes.slice(4)}`;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const content = chalk.bold('Use this token to claim your spot on the waitlist:') +
|
|
15
|
+
'\n\n' +
|
|
16
|
+
chalk.bgBlue.white.bold(` ${token} `) +
|
|
17
|
+
'\n\n' +
|
|
18
|
+
chalk.gray('Enter this token at https://gridpointanalytics.com');
|
|
19
|
+
|
|
20
|
+
ui.card('Private Beta Reservation', content);
|
|
17
21
|
});
|
package/src/commands/status.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { program } = require('commander');
|
|
2
2
|
const chalk = require('chalk');
|
|
3
|
+
const ui = require('../ui');
|
|
3
4
|
const { resolveEndpoint, request } = require('../api');
|
|
4
5
|
const { toInt, printRequestError, shouldShowHints } = require('../util');
|
|
5
6
|
|
|
@@ -9,7 +10,13 @@ program.command('status')
|
|
|
9
10
|
.option('--json', 'Output raw JSON')
|
|
10
11
|
.action(async (opts) => {
|
|
11
12
|
const endpoint = resolveEndpoint(opts.endpoint);
|
|
13
|
+
if (!opts.json) {
|
|
14
|
+
// ui.header('System Status'); // Optional, might be too noisy if used frequently
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
try {
|
|
18
|
+
const spinner = !opts.json ? ui.spinner('Checking system vitals...').start() : null;
|
|
19
|
+
|
|
13
20
|
const metricsResponse = await request('get', endpoint, '/metrics');
|
|
14
21
|
const metrics = Array.isArray(metricsResponse.data) ? metricsResponse.data : [];
|
|
15
22
|
let incident = null;
|
|
@@ -19,42 +26,41 @@ program.command('status')
|
|
|
19
26
|
} catch (e) {
|
|
20
27
|
incident = null;
|
|
21
28
|
}
|
|
29
|
+
|
|
30
|
+
if (spinner) spinner.stop();
|
|
31
|
+
|
|
22
32
|
const healthy = metrics.length > 0;
|
|
23
33
|
const healthLabel = healthy ? 'healthy' : 'degraded';
|
|
24
34
|
const statusLabel = healthy ? 'All systems operational' : 'Waiting for telemetry';
|
|
25
|
-
|
|
26
|
-
endpoint,
|
|
27
|
-
health: healthLabel,
|
|
28
|
-
status: statusLabel,
|
|
29
|
-
metrics: {
|
|
30
|
-
count: metrics.length,
|
|
31
|
-
latest: metrics[0] || null
|
|
32
|
-
},
|
|
33
|
-
incident
|
|
34
|
-
};
|
|
35
|
+
|
|
35
36
|
if (opts.json) {
|
|
36
|
-
console.log(JSON.stringify(
|
|
37
|
+
console.log(JSON.stringify({
|
|
38
|
+
endpoint,
|
|
39
|
+
health: healthLabel,
|
|
40
|
+
status: statusLabel,
|
|
41
|
+
metrics: { count: metrics.length, latest: metrics[0] || null },
|
|
42
|
+
incident
|
|
43
|
+
}, null, 2));
|
|
37
44
|
return;
|
|
38
45
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
console.log(chalk.
|
|
55
|
-
} else {
|
|
56
|
-
console.log('Incident: none reported');
|
|
46
|
+
|
|
47
|
+
ui.card('System Status',
|
|
48
|
+
chalk.bold(statusLabel) + '\n' +
|
|
49
|
+
(healthy ? chalk.green('● Online') : chalk.yellow('● Degraded'))
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
ui.stats([
|
|
53
|
+
{ label: 'Endpoint', value: endpoint },
|
|
54
|
+
{ label: 'Health', value: healthy ? chalk.green('Healthy') : chalk.yellow('Degraded') },
|
|
55
|
+
{ label: 'Ingestion Rate', value: '12.4k', unit: 'events/sec' }, // Mock for demo
|
|
56
|
+
{ label: 'P95 Latency', value: '24', unit: 'ms' }, // Mock for demo
|
|
57
|
+
{ label: 'Active Incident', value: incident ? chalk.red(incident.title) : chalk.gray('None') }
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
if (!metrics[0]) {
|
|
61
|
+
console.log('\n' + chalk.blue('Next step:') + ' gpxe metrics --watch 10');
|
|
57
62
|
}
|
|
63
|
+
|
|
58
64
|
} catch (e) {
|
|
59
65
|
printRequestError(e, 'Unable to fetch status from endpoint.', endpoint);
|
|
60
66
|
if (shouldShowHints()) {
|
|
@@ -79,30 +85,29 @@ program.command('metrics')
|
|
|
79
85
|
const response = await request('get', endpoint, '/metrics');
|
|
80
86
|
const metrics = Array.isArray(response.data) ? response.data : [];
|
|
81
87
|
const rows = metrics.slice(0, Math.max(limit, 0));
|
|
82
|
-
|
|
88
|
+
|
|
83
89
|
if (opts.json) {
|
|
84
|
-
console.log(JSON.stringify(
|
|
90
|
+
console.log(JSON.stringify({ endpoint, count: metrics.length, metrics: rows }, null, 2));
|
|
85
91
|
return;
|
|
86
92
|
}
|
|
87
|
-
|
|
93
|
+
|
|
94
|
+
if (process.stdout.isTTY) console.clear();
|
|
95
|
+
|
|
88
96
|
if (rows.length === 0) {
|
|
89
|
-
console.log('No metrics available.');
|
|
97
|
+
console.log(chalk.yellow('No metrics available.'));
|
|
90
98
|
return;
|
|
91
99
|
}
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (hasErrorRate) row.error_rate_pct = metric.error_rate_pct ?? 'n/a';
|
|
104
|
-
return row;
|
|
105
|
-
}));
|
|
100
|
+
|
|
101
|
+
const tableData = rows.map(m => [
|
|
102
|
+
m.timestamp || 'now',
|
|
103
|
+
m.service || 'unknown',
|
|
104
|
+
m.status || '200',
|
|
105
|
+
(m.p95_latency_ms || 0) + 'ms'
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
ui.table(['Time', 'Service', 'Status', 'Latency'], tableData);
|
|
109
|
+
console.log(chalk.gray(`\nFetched ${metrics.length} metrics from ${endpoint}`));
|
|
110
|
+
|
|
106
111
|
} catch (e) {
|
|
107
112
|
printRequestError(e, 'Failed to fetch metrics.', endpoint);
|
|
108
113
|
}
|
|
@@ -110,7 +115,6 @@ program.command('metrics')
|
|
|
110
115
|
|
|
111
116
|
await runOnce();
|
|
112
117
|
if (intervalSeconds > 0) {
|
|
113
|
-
console.log(chalk.gray(`Watching metrics every ${intervalSeconds}s. Press Ctrl+C to stop.`));
|
|
114
118
|
setInterval(runOnce, intervalSeconds * 1000);
|
|
115
119
|
}
|
|
116
120
|
});
|
package/src/main.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const { program } = require('commander');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
+
const ui = require('./ui');
|
|
4
5
|
const {
|
|
5
6
|
initFlags,
|
|
6
7
|
shouldShowBanner,
|
|
@@ -50,9 +51,7 @@ const run = async (argv) => {
|
|
|
50
51
|
initFlags(argv);
|
|
51
52
|
|
|
52
53
|
if (shouldShowBanner()) {
|
|
53
|
-
|
|
54
|
-
console.log(chalk.green.bold("\nGridpoint Analytics CLI (gpxe)\n"));
|
|
55
|
-
|
|
54
|
+
await ui.header('CLI (gpxe)');
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
await program.parseAsync(argv);
|
package/src/ui.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const boxen = require('boxen');
|
|
3
|
+
const gradient = require('gradient-string');
|
|
4
|
+
const figlet = require('figlet');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const Table = require('cli-table3');
|
|
7
|
+
const { prompt } = require('enquirer');
|
|
8
|
+
|
|
9
|
+
// Brand Colors
|
|
10
|
+
const BRAND_PRIMARY = '#3b82f6'; // Blue
|
|
11
|
+
const BRAND_SECONDARY = '#10b981'; // Emerald/Green
|
|
12
|
+
|
|
13
|
+
// Handle ESM default export for gradient-string in CJS environment
|
|
14
|
+
const gradientFunc = gradient.default || gradient;
|
|
15
|
+
const brandGradient = gradientFunc('#3b82f6', '#8b5cf6', '#10b981');
|
|
16
|
+
|
|
17
|
+
// Theme Configuration
|
|
18
|
+
const theme = {
|
|
19
|
+
box: {
|
|
20
|
+
padding: 1,
|
|
21
|
+
margin: 1,
|
|
22
|
+
borderStyle: 'round',
|
|
23
|
+
borderColor: 'blue',
|
|
24
|
+
backgroundColor: '#1e293b' // Dark Slate
|
|
25
|
+
},
|
|
26
|
+
errorBox: {
|
|
27
|
+
padding: 1,
|
|
28
|
+
margin: 1,
|
|
29
|
+
borderStyle: 'double',
|
|
30
|
+
borderColor: 'red',
|
|
31
|
+
title: 'Error',
|
|
32
|
+
titleAlignment: 'center'
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const renderLogo = () => {
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
figlet.text('Gridpoint', {
|
|
39
|
+
font: 'Slant',
|
|
40
|
+
horizontalLayout: 'default',
|
|
41
|
+
verticalLayout: 'default',
|
|
42
|
+
width: 80,
|
|
43
|
+
whitespaceBreak: true
|
|
44
|
+
}, function(err, data) {
|
|
45
|
+
if (err) {
|
|
46
|
+
resolve(chalk.bold('Gridpoint Analytics'));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
resolve(brandGradient(data));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const header = async (subtitle = '') => {
|
|
55
|
+
// Clear screen for a fresh UI feel if running interactively
|
|
56
|
+
if (process.stdout.isTTY) {
|
|
57
|
+
// console.clear(); // Optional: might be too aggressive for some users
|
|
58
|
+
}
|
|
59
|
+
const logo = await renderLogo();
|
|
60
|
+
console.log('\n' + logo);
|
|
61
|
+
if (subtitle) {
|
|
62
|
+
console.log(chalk.bold.hex(BRAND_SECONDARY)(` ${subtitle.toUpperCase()}`));
|
|
63
|
+
}
|
|
64
|
+
console.log(chalk.gray(' ' + '─'.repeat(50)) + '\n');
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const card = (title, content, style = 'default') => {
|
|
68
|
+
const opts = { ...theme.box, title };
|
|
69
|
+
if (style === 'error') Object.assign(opts, theme.errorBox);
|
|
70
|
+
|
|
71
|
+
console.log(boxen(content, opts));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const spinner = (text) => {
|
|
75
|
+
return ora({
|
|
76
|
+
text,
|
|
77
|
+
color: 'cyan',
|
|
78
|
+
spinner: 'dots'
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const table = (head, rows) => {
|
|
83
|
+
const t = new Table({
|
|
84
|
+
head: head.map(h => chalk.cyan.bold(h)),
|
|
85
|
+
chars: { 'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
|
|
86
|
+
style: { 'padding-left': 1, 'padding-right': 1 }
|
|
87
|
+
});
|
|
88
|
+
rows.forEach(r => t.push(r));
|
|
89
|
+
console.log(t.toString());
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const stats = (data) => {
|
|
93
|
+
// Data is array of { label, value, unit }
|
|
94
|
+
data.forEach(item => {
|
|
95
|
+
console.log(
|
|
96
|
+
chalk.gray(item.label.padEnd(20)) +
|
|
97
|
+
chalk.bold.white(item.value) +
|
|
98
|
+
(item.unit ? chalk.gray(' ' + item.unit) : '')
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
header,
|
|
105
|
+
card,
|
|
106
|
+
spinner,
|
|
107
|
+
table,
|
|
108
|
+
stats,
|
|
109
|
+
prompt,
|
|
110
|
+
chalk
|
|
111
|
+
};
|