errorhub-cli 1.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +323 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
- package/src/index.ts +426 -0
- package/tsconfig.json +18 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import open from 'open';
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import clc from 'cli-color';
|
|
10
|
+
const program = new Command();
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
const CONFIG_PATH = path.join(os.homedir(), '.my-cli-config.json');
|
|
14
|
+
// ─── Color Palette ────────────────────────────────────────────────────────────
|
|
15
|
+
const c = {
|
|
16
|
+
cyan: clc.xterm(51).bold,
|
|
17
|
+
cyanDim: clc.xterm(38),
|
|
18
|
+
teal: clc.xterm(6).bold,
|
|
19
|
+
green: clc.xterm(46).bold,
|
|
20
|
+
greenDim: clc.xterm(34),
|
|
21
|
+
red: clc.xterm(196).bold,
|
|
22
|
+
orange: clc.xterm(214).bold,
|
|
23
|
+
yellow: clc.xterm(226).bold,
|
|
24
|
+
white: clc.xterm(255).bold,
|
|
25
|
+
dim: clc.xterm(240),
|
|
26
|
+
bgTeal: clc.bgXterm(6),
|
|
27
|
+
bgRed: clc.bgXterm(196),
|
|
28
|
+
bgGreen: clc.bgXterm(28),
|
|
29
|
+
bgOrange: clc.bgXterm(208),
|
|
30
|
+
};
|
|
31
|
+
// ─── Layout Helpers ───────────────────────────────────────────────────────────
|
|
32
|
+
const W = 62; // box width
|
|
33
|
+
const line = (char = '─') => c.teal('─'.repeat(W));
|
|
34
|
+
const dLine = () => c.teal('═'.repeat(W));
|
|
35
|
+
const blank = () => console.log(c.teal('│') + ' '.repeat(W - 2) + c.teal('│'));
|
|
36
|
+
const boxRow = (text, color = c.white) => {
|
|
37
|
+
const inner = W - 4; // 2 border chars + 2 padding chars
|
|
38
|
+
const stripped = text.replace(/\x1B\[[0-9;]*m/g, '');
|
|
39
|
+
const pad = Math.max(0, inner - stripped.length);
|
|
40
|
+
const left = Math.floor(pad / 2);
|
|
41
|
+
const right = pad - left;
|
|
42
|
+
console.log(c.teal('│') + ' ' + ' '.repeat(left) + color(text) + ' '.repeat(right) + ' ' + c.teal('│'));
|
|
43
|
+
};
|
|
44
|
+
const labelRow = (label, value) => {
|
|
45
|
+
const l = c.cyanDim(label.padEnd(14));
|
|
46
|
+
const v = c.white(value);
|
|
47
|
+
const raw = label.padEnd(14) + value;
|
|
48
|
+
const pad = Math.max(0, W - 4 - raw.length);
|
|
49
|
+
console.log(c.teal('│') + ' ' + l + v + ' '.repeat(pad) + ' ' + c.teal('│'));
|
|
50
|
+
};
|
|
51
|
+
// ─── ASCII Logo ───────────────────────────────────────────────────────────────
|
|
52
|
+
const LOGO = [
|
|
53
|
+
'███████╗██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗██████╗ ',
|
|
54
|
+
'██╔════╝██╔══██╗██╔══██╗██╔═══██╗██╔══██╗██║ ██║██║ ██║██╔══██╗',
|
|
55
|
+
'█████╗ ██████╔╝██████╔╝██║ ██║██████╔╝███████║██║ ██║██████╔╝',
|
|
56
|
+
'██╔══╝ ██╔══██╗██╔══██╗██║ ██║██╔══██╗██╔══██║██║ ██║██╔══██╗',
|
|
57
|
+
'███████╗██║ ██║██║ ██║╚██████╔╝██║ ██║██║ ██║╚██████╔╝██████╔╝',
|
|
58
|
+
'╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝',
|
|
59
|
+
'',
|
|
60
|
+
' ██████╗██╗ ██╗ ',
|
|
61
|
+
' ██╔════╝██║ ██║ ',
|
|
62
|
+
' ██║ ██║ ██║ ',
|
|
63
|
+
' ██║ ██║ ██║ ',
|
|
64
|
+
' ╚██████╗███████╗██║ ',
|
|
65
|
+
' ╚═════╝╚══════╝╚═╝ ',
|
|
66
|
+
];
|
|
67
|
+
const printLogo = () => {
|
|
68
|
+
console.log('');
|
|
69
|
+
LOGO.forEach((row, i) => {
|
|
70
|
+
// Gradient: cyan → teal → green
|
|
71
|
+
const color = i < 3 ? c.cyan : i < 6 ? c.teal : c.green;
|
|
72
|
+
console.log(color(row));
|
|
73
|
+
});
|
|
74
|
+
console.log('');
|
|
75
|
+
};
|
|
76
|
+
// ─── Banner ───────────────────────────────────────────────────────────────────
|
|
77
|
+
const printBanner = (title, subtitle) => {
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log(c.teal('╔' + '═'.repeat(W - 2) + '╗'));
|
|
80
|
+
boxRow('');
|
|
81
|
+
boxRow(title, c.cyan);
|
|
82
|
+
if (subtitle)
|
|
83
|
+
boxRow(subtitle, c.dim);
|
|
84
|
+
boxRow('');
|
|
85
|
+
console.log(c.teal('╠' + '═'.repeat(W - 2) + '╣'));
|
|
86
|
+
};
|
|
87
|
+
const closeBanner = () => {
|
|
88
|
+
console.log(c.teal('╚' + '═'.repeat(W - 2) + '╝'));
|
|
89
|
+
console.log('');
|
|
90
|
+
};
|
|
91
|
+
// ─── Status Badges ────────────────────────────────────────────────────────────
|
|
92
|
+
const badge = {
|
|
93
|
+
ok: (msg) => console.log(c.bgGreen(' ✔ OK ') + ' ' + c.green(msg)),
|
|
94
|
+
error: (msg) => console.log(c.bgRed(' ✘ ERR ') + ' ' + c.red(msg)),
|
|
95
|
+
info: (msg) => console.log(c.bgTeal(' ℹ INFO') + ' ' + c.cyanDim(msg)),
|
|
96
|
+
warn: (msg) => console.log(c.bgOrange(' ⚠ WARN') + ' ' + c.orange(msg)),
|
|
97
|
+
loading: (msg) => console.log(c.cyan(' ⟳ ') + c.cyanDim(msg)),
|
|
98
|
+
};
|
|
99
|
+
// ─── Spinner (simple dot progress) ───────────────────────────────────────────
|
|
100
|
+
const spinner = (label) => {
|
|
101
|
+
const frames = ['⠋', '⠙', '⠸', '⠴', '⠦', '⠇'];
|
|
102
|
+
let i = 0;
|
|
103
|
+
const iv = setInterval(() => {
|
|
104
|
+
process.stdout.write('\r' + c.cyan(frames[i++ % frames.length]) + ' ' + c.cyanDim(label));
|
|
105
|
+
}, 80);
|
|
106
|
+
return () => {
|
|
107
|
+
clearInterval(iv);
|
|
108
|
+
process.stdout.write('\r' + ' '.repeat(label.length + 4) + '\r');
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
// ─── Section Divider ──────────────────────────────────────────────────────────
|
|
112
|
+
const section = (title) => {
|
|
113
|
+
console.log('');
|
|
114
|
+
const line = '─'.repeat(4);
|
|
115
|
+
console.log(c.teal(line) + ' ' + c.yellow(title.toUpperCase()) + ' ' + c.teal(line));
|
|
116
|
+
};
|
|
117
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
// CLI Setup
|
|
119
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
program
|
|
121
|
+
.name('LOGIN-CLI')
|
|
122
|
+
.description('CLI with JWT login')
|
|
123
|
+
.version('1.0.0');
|
|
124
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
125
|
+
// INIT
|
|
126
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
127
|
+
program
|
|
128
|
+
.command('init')
|
|
129
|
+
.description('Initialize project')
|
|
130
|
+
.action(() => {
|
|
131
|
+
printLogo();
|
|
132
|
+
printBanner('PROJECT INITIALIZER', 'Setting up your workspace...');
|
|
133
|
+
console.log(c.teal('│'));
|
|
134
|
+
labelRow('Version:', '1.0.0');
|
|
135
|
+
labelRow('Node:', process.version);
|
|
136
|
+
labelRow('Platform:', process.platform);
|
|
137
|
+
labelRow('CWD:', process.cwd().slice(0, 40));
|
|
138
|
+
console.log(c.teal('│'));
|
|
139
|
+
closeBanner();
|
|
140
|
+
section('Launching Auth Server');
|
|
141
|
+
console.log('');
|
|
142
|
+
const app = express();
|
|
143
|
+
const port = 4000;
|
|
144
|
+
const stop = spinner('Starting local server on port 4000...');
|
|
145
|
+
const server = app.listen(port, () => {
|
|
146
|
+
stop();
|
|
147
|
+
badge.ok(`Server listening on ${c.cyan(`http://localhost:${port}`)}`);
|
|
148
|
+
const loginUrl = `http://localhost:3000/login?callback=http://localhost:${port}/callback`;
|
|
149
|
+
badge.info(`Opening: ${c.cyanDim(loginUrl)}`);
|
|
150
|
+
console.log('');
|
|
151
|
+
open(loginUrl);
|
|
152
|
+
});
|
|
153
|
+
section('Ready');
|
|
154
|
+
console.log('');
|
|
155
|
+
console.log(c.dim(' Waiting for browser login flow to complete...'));
|
|
156
|
+
console.log('');
|
|
157
|
+
});
|
|
158
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
159
|
+
// LOGIN
|
|
160
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
161
|
+
program
|
|
162
|
+
.command('login')
|
|
163
|
+
.description('Login and store JWT')
|
|
164
|
+
.action(async () => {
|
|
165
|
+
printLogo();
|
|
166
|
+
printBanner('AUTHENTICATION', 'Secure JWT login flow');
|
|
167
|
+
labelRow('Callback Port:', '4000');
|
|
168
|
+
labelRow('Auth Server:', 'http://localhost:3000');
|
|
169
|
+
labelRow('Config Path:', CONFIG_PATH.replace(os.homedir(), '~'));
|
|
170
|
+
console.log(c.teal('│'));
|
|
171
|
+
closeBanner();
|
|
172
|
+
section('Starting OAuth Flow');
|
|
173
|
+
console.log('');
|
|
174
|
+
const app = express();
|
|
175
|
+
const port = 4000;
|
|
176
|
+
const stop = spinner('Binding to port 4000...');
|
|
177
|
+
const server = app.listen(port, () => {
|
|
178
|
+
stop();
|
|
179
|
+
const loginUrl = `http://localhost:3000/login?callback=http://localhost:${port}/callback`;
|
|
180
|
+
badge.ok('Local callback server ready');
|
|
181
|
+
badge.loading('Opening browser for login...');
|
|
182
|
+
console.log('');
|
|
183
|
+
open(loginUrl);
|
|
184
|
+
});
|
|
185
|
+
app.get('/callback', (req, res) => {
|
|
186
|
+
const token = req.query.token;
|
|
187
|
+
if (!token) {
|
|
188
|
+
res.status(400).send(renderHtmlPage('Error', '✘ No token received.', '#ff2d2d'));
|
|
189
|
+
badge.error('Callback hit but no token was received.');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// Save token
|
|
193
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify({ token }, null, 2));
|
|
194
|
+
res.send(renderHtmlPage('Success!', '✔ Login successful — you may close this tab.', '#00e5a0'));
|
|
195
|
+
section('Login Complete');
|
|
196
|
+
console.log('');
|
|
197
|
+
badge.ok('JWT received and stored successfully!');
|
|
198
|
+
console.log('');
|
|
199
|
+
console.log(c.teal('┌' + '─'.repeat(W - 2) + '┐'));
|
|
200
|
+
const preview = token.length > 50
|
|
201
|
+
? token.slice(0, 24) + c.dim(' ··· ') + token.slice(-24)
|
|
202
|
+
: token;
|
|
203
|
+
boxRow('TOKEN PREVIEW', c.yellow);
|
|
204
|
+
boxRow(preview, c.cyanDim);
|
|
205
|
+
console.log(c.teal('└' + '─'.repeat(W - 2) + '┘'));
|
|
206
|
+
console.log('');
|
|
207
|
+
console.log(c.dim(` Saved to: ${CONFIG_PATH}`));
|
|
208
|
+
console.log('');
|
|
209
|
+
server.close();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
213
|
+
// WHOAMI
|
|
214
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
215
|
+
program
|
|
216
|
+
.command('whoami')
|
|
217
|
+
.description('Read stored JWT')
|
|
218
|
+
.action(() => {
|
|
219
|
+
printLogo();
|
|
220
|
+
printBanner('IDENTITY CHECK', 'Reading stored credentials');
|
|
221
|
+
labelRow('Config:', CONFIG_PATH.replace(os.homedir(), '~'));
|
|
222
|
+
console.log(c.teal('│'));
|
|
223
|
+
closeBanner();
|
|
224
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
225
|
+
console.log('');
|
|
226
|
+
badge.error('No credentials found. Run ' + c.cyan('login-cli login') + ' first.');
|
|
227
|
+
console.log('');
|
|
228
|
+
console.log(c.teal('┌' + '─'.repeat(W - 2) + '┐'));
|
|
229
|
+
boxRow('NOT AUTHENTICATED', c.red);
|
|
230
|
+
boxRow('Use: login-cli login', c.dim);
|
|
231
|
+
console.log(c.teal('└' + '─'.repeat(W - 2) + '┘'));
|
|
232
|
+
console.log('');
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const data = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
236
|
+
const { token } = data;
|
|
237
|
+
// Decode JWT header/payload for display (no verification)
|
|
238
|
+
let jwtInfo = {};
|
|
239
|
+
try {
|
|
240
|
+
const [h, p] = token.split('.');
|
|
241
|
+
jwtInfo.header = JSON.parse(Buffer.from(h, 'base64').toString());
|
|
242
|
+
jwtInfo.payload = JSON.parse(Buffer.from(p, 'base64').toString());
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// not a valid JWT, just show raw
|
|
246
|
+
}
|
|
247
|
+
section('Stored Token');
|
|
248
|
+
console.log('');
|
|
249
|
+
console.log(c.teal('┌' + '─'.repeat(W - 2) + '┐'));
|
|
250
|
+
if (jwtInfo.payload) {
|
|
251
|
+
const p = jwtInfo.payload;
|
|
252
|
+
// Header meta
|
|
253
|
+
boxRow('JWT DECODED', c.yellow);
|
|
254
|
+
console.log(c.teal('├' + '─'.repeat(W - 2) + '┤'));
|
|
255
|
+
const fields = Object.entries({
|
|
256
|
+
sub: p.sub ?? '—',
|
|
257
|
+
name: p.name ?? '—',
|
|
258
|
+
email: p.email ?? '—',
|
|
259
|
+
role: p.role ?? '—',
|
|
260
|
+
iat: p.iat ? new Date(p.iat * 1000).toISOString() : '—',
|
|
261
|
+
exp: p.exp ? new Date(p.exp * 1000).toISOString() : '—',
|
|
262
|
+
alg: jwtInfo.header?.alg ?? '—',
|
|
263
|
+
});
|
|
264
|
+
for (const [k, v] of fields) {
|
|
265
|
+
labelRow(`${k}:`, String(v));
|
|
266
|
+
}
|
|
267
|
+
// Expiry warning
|
|
268
|
+
if (jwtInfo.payload.exp) {
|
|
269
|
+
const expired = Date.now() / 1000 > jwtInfo.payload.exp;
|
|
270
|
+
console.log(c.teal('├' + '─'.repeat(W - 2) + '┤'));
|
|
271
|
+
boxRow(expired ? '⚠ TOKEN EXPIRED' : '✔ TOKEN VALID', expired ? c.red : c.green);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
console.log(c.teal('├' + '─'.repeat(W - 2) + '┤'));
|
|
275
|
+
boxRow('RAW TOKEN', c.yellow);
|
|
276
|
+
// Print token split at 58 chars per line
|
|
277
|
+
const chunks = token.match(/.{1,54}/g) ?? [token];
|
|
278
|
+
for (const chunk of chunks) {
|
|
279
|
+
boxRow(chunk, c.cyanDim);
|
|
280
|
+
}
|
|
281
|
+
console.log(c.teal('└' + '─'.repeat(W - 2) + '┘'));
|
|
282
|
+
console.log('');
|
|
283
|
+
});
|
|
284
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
285
|
+
// HTML response page helper
|
|
286
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
287
|
+
function renderHtmlPage(title, message, color) {
|
|
288
|
+
return `<!DOCTYPE html>
|
|
289
|
+
<html>
|
|
290
|
+
<head>
|
|
291
|
+
<meta charset="UTF-8"/>
|
|
292
|
+
<title>${title}</title>
|
|
293
|
+
<style>
|
|
294
|
+
* { margin:0; padding:0; box-sizing:border-box; }
|
|
295
|
+
body {
|
|
296
|
+
background: #0a0e14;
|
|
297
|
+
color: #e0e0e0;
|
|
298
|
+
font-family: 'Courier New', monospace;
|
|
299
|
+
display: flex;
|
|
300
|
+
align-items: center;
|
|
301
|
+
justify-content: center;
|
|
302
|
+
height: 100vh;
|
|
303
|
+
}
|
|
304
|
+
.card {
|
|
305
|
+
border: 1px solid ${color};
|
|
306
|
+
padding: 3rem 4rem;
|
|
307
|
+
text-align: center;
|
|
308
|
+
box-shadow: 0 0 40px ${color}44;
|
|
309
|
+
}
|
|
310
|
+
h1 { font-size: 2.5rem; color: ${color}; letter-spacing: 0.2em; }
|
|
311
|
+
p { margin-top: 1rem; color: #888; font-size: 0.9rem; }
|
|
312
|
+
</style>
|
|
313
|
+
</head>
|
|
314
|
+
<body>
|
|
315
|
+
<div class="card">
|
|
316
|
+
<h1>${title}</h1>
|
|
317
|
+
<p>${message}</p>
|
|
318
|
+
</div>
|
|
319
|
+
</body>
|
|
320
|
+
</html>`;
|
|
321
|
+
}
|
|
322
|
+
program.parse();
|
|
323
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "errorhub-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"my-cli": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/cli-color": "^2.0.6",
|
|
18
|
+
"@types/commander": "^2.12.0",
|
|
19
|
+
"@types/express": "^5.0.6",
|
|
20
|
+
"@types/node": "^25.2.3",
|
|
21
|
+
"ts-node": "^10.9.2",
|
|
22
|
+
"typescript": "^5.9.3"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"chalk": "^5.6.2",
|
|
26
|
+
"cli-color": "^2.0.4",
|
|
27
|
+
"commander": "^14.0.3",
|
|
28
|
+
"express": "^5.2.1",
|
|
29
|
+
"open": "^11.0.0",
|
|
30
|
+
"tsx": "^4.21.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import open from 'open';
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import clc from 'cli-color';
|
|
11
|
+
import { json } from 'stream/consumers';
|
|
12
|
+
|
|
13
|
+
const program = new Command();
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
const CONFIG_PATH = path.join(os.homedir(), '.token.json');
|
|
17
|
+
const PROJECTS = path.join(os.homedir(), 'projectId.json')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// ─── Color Palette ──────────────────────────────────────────
|
|
21
|
+
// ─────────────────
|
|
22
|
+
const c = {
|
|
23
|
+
cyan: clc.xterm(51).bold,
|
|
24
|
+
cyanDim: clc.xterm(38),
|
|
25
|
+
teal: clc.xterm(6).bold,
|
|
26
|
+
green: clc.xterm(46).bold,
|
|
27
|
+
greenDim: clc.xterm(34),
|
|
28
|
+
red: clc.xterm(196).bold,
|
|
29
|
+
orange: clc.xterm(214).bold,
|
|
30
|
+
yellow: clc.xterm(226).bold,
|
|
31
|
+
white: clc.xterm(255).bold,
|
|
32
|
+
dim: clc.xterm(240),
|
|
33
|
+
bgTeal: clc.bgXterm(6),
|
|
34
|
+
bgRed: clc.bgXterm(196),
|
|
35
|
+
bgGreen: clc.bgXterm(28),
|
|
36
|
+
bgOrange: clc.bgXterm(208),
|
|
37
|
+
};
|
|
38
|
+
const FRONTENDBASE = "https://errorhub.vercel.app";
|
|
39
|
+
// ─── Layout Helpers ───────────────────────────────────────────────────────────
|
|
40
|
+
const W = 62; // box width
|
|
41
|
+
|
|
42
|
+
const line = (char = '─') => c.teal('─'.repeat(W));
|
|
43
|
+
const dLine = () => c.teal('═'.repeat(W));
|
|
44
|
+
const blank = () => console.log(c.teal('│') + ' '.repeat(W - 2) + c.teal('│'));
|
|
45
|
+
|
|
46
|
+
const boxRow = (text: string, color = c.white) => {
|
|
47
|
+
const inner = W - 4; // 2 border chars + 2 padding chars
|
|
48
|
+
const stripped = text.replace(/\x1B\[[0-9;]*m/g, '');
|
|
49
|
+
const pad = Math.max(0, inner - stripped.length);
|
|
50
|
+
const left = Math.floor(pad / 2);
|
|
51
|
+
const right = pad - left;
|
|
52
|
+
console.log(c.teal('│') + ' ' + ' '.repeat(left) + color(text) + ' '.repeat(right) + ' ' + c.teal('│'));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const labelRow = (label: string, value: string) => {
|
|
56
|
+
const l = c.cyanDim(label.padEnd(14));
|
|
57
|
+
const v = c.white(value);
|
|
58
|
+
const raw = label.padEnd(14) + value;
|
|
59
|
+
const pad = Math.max(0, W - 4 - raw.length);
|
|
60
|
+
console.log(c.teal('│') + ' ' + l + v + ' '.repeat(pad) + ' ' + c.teal('│'));
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ─── ASCII Logo ───────────────────────────────────────────────────────────────
|
|
64
|
+
const LOGO = [
|
|
65
|
+
'███████╗██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗██████╗ ',
|
|
66
|
+
'██╔════╝██╔══██╗██╔══██╗██╔═══██╗██╔══██╗██║ ██║██║ ██║██╔══██╗',
|
|
67
|
+
'█████╗ ██████╔╝██████╔╝██║ ██║██████╔╝███████║██║ ██║██████╔╝',
|
|
68
|
+
'██╔══╝ ██╔══██╗██╔══██╗██║ ██║██╔══██╗██╔══██║██║ ██║██╔══██╗',
|
|
69
|
+
'███████╗██║ ██║██║ ██║╚██████╔╝██║ ██║██║ ██║╚██████╔╝██████╔╝',
|
|
70
|
+
'╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝',
|
|
71
|
+
'',
|
|
72
|
+
' ██████╗██╗ ██╗ ',
|
|
73
|
+
' ██╔════╝██║ ██║ ',
|
|
74
|
+
' ██║ ██║ ██║ ',
|
|
75
|
+
' ██║ ██║ ██║ ',
|
|
76
|
+
' ╚██████╗███████╗██║ ',
|
|
77
|
+
' ╚═════╝╚══════╝╚═╝ ',
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const printLogo = () => {
|
|
81
|
+
console.log('');
|
|
82
|
+
LOGO.forEach((row, i) => {
|
|
83
|
+
// Gradient: cyan → teal → green
|
|
84
|
+
const color = i < 3 ? c.cyan : i < 6 ? c.teal : c.green;
|
|
85
|
+
console.log(color(row));
|
|
86
|
+
});
|
|
87
|
+
console.log('');
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// ─── Banner ───────────────────────────────────────────────────────────────────
|
|
91
|
+
const printBanner = (title: string, subtitle?: string) => {
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log(c.teal('╔' + '═'.repeat(W - 2) + '╗'));
|
|
94
|
+
boxRow('');
|
|
95
|
+
boxRow(title, c.cyan);
|
|
96
|
+
if (subtitle) boxRow(subtitle, c.dim);
|
|
97
|
+
boxRow('');
|
|
98
|
+
console.log(c.teal('╠' + '═'.repeat(W - 2) + '╣'));
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const closeBanner = () => {
|
|
102
|
+
console.log(c.teal('╚' + '═'.repeat(W - 2) + '╝'));
|
|
103
|
+
console.log('');
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// ─── Status Badges ────────────────────────────────────────────────────────────
|
|
107
|
+
const badge = {
|
|
108
|
+
ok: (msg: string) => console.log(c.bgGreen(' ✔ OK ') + ' ' + c.green(msg)),
|
|
109
|
+
error: (msg: string) => console.log(c.bgRed(' ✘ ERR ') + ' ' + c.red(msg)),
|
|
110
|
+
info: (msg: string) => console.log(c.bgTeal(' ℹ INFO') + ' ' + c.cyanDim(msg)),
|
|
111
|
+
warn: (msg: string) => console.log(c.bgOrange(' ⚠ WARN') + ' ' + c.orange(msg)),
|
|
112
|
+
loading: (msg: string) => console.log(c.cyan(' ⟳ ') + c.cyanDim(msg)),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// ─── Spinner (simple dot progress) ───────────────────────────────────────────
|
|
116
|
+
const spinner = (label: string) => {
|
|
117
|
+
const frames = ['⠋', '⠙', '⠸', '⠴', '⠦', '⠇'];
|
|
118
|
+
let i = 0;
|
|
119
|
+
const iv = setInterval(() => {
|
|
120
|
+
process.stdout.write('\r' + c.cyan(frames[i++ % frames.length]) + ' ' + c.cyanDim(label));
|
|
121
|
+
}, 80);
|
|
122
|
+
return () => {
|
|
123
|
+
clearInterval(iv);
|
|
124
|
+
process.stdout.write('\r' + ' '.repeat(label.length + 4) + '\r');
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// ─── Section Divider ──────────────────────────────────────────────────────────
|
|
129
|
+
const section = (title: string) => {
|
|
130
|
+
console.log('');
|
|
131
|
+
const line = '─'.repeat(4);
|
|
132
|
+
console.log(c.teal(line) + ' ' + c.yellow(title.toUpperCase()) + ' ' + c.teal(line));
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
136
|
+
// CLI Setup
|
|
137
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
program
|
|
140
|
+
.name('errorhub')
|
|
141
|
+
.description('CLI with JWT login')
|
|
142
|
+
.version('1.0.0');
|
|
143
|
+
|
|
144
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
145
|
+
// INIT
|
|
146
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
program
|
|
149
|
+
.command('init')
|
|
150
|
+
.description('Initialize project')
|
|
151
|
+
.action(() => {
|
|
152
|
+
printLogo();
|
|
153
|
+
|
|
154
|
+
printBanner('PROJECT INITIALIZER', 'Setting up your workspace...');
|
|
155
|
+
|
|
156
|
+
console.log(c.teal('│'));
|
|
157
|
+
labelRow('Version:', '1.0.0');
|
|
158
|
+
labelRow('Node:', process.version);
|
|
159
|
+
labelRow('Platform:', process.platform);
|
|
160
|
+
labelRow('CWD:', process.cwd().slice(0, 40));
|
|
161
|
+
console.log(c.teal('│'));
|
|
162
|
+
|
|
163
|
+
closeBanner();
|
|
164
|
+
|
|
165
|
+
section('Launching Auth Server');
|
|
166
|
+
console.log('');
|
|
167
|
+
|
|
168
|
+
const app = express();
|
|
169
|
+
const port = 4000;
|
|
170
|
+
const stop = spinner('Starting local server on port 4000...');
|
|
171
|
+
|
|
172
|
+
const server = app.listen(port, () => {
|
|
173
|
+
stop();
|
|
174
|
+
badge.ok(`Server listening on ${c.cyan(`http://localhost:${port}`)}`);
|
|
175
|
+
|
|
176
|
+
const loginUrl = `${FRONTENDBASE}/login?callback=http://localhost:${port}/callback`;
|
|
177
|
+
|
|
178
|
+
badge.info(`Opening: ${c.cyanDim(loginUrl)}`);
|
|
179
|
+
console.log('');
|
|
180
|
+
open(loginUrl);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
section('Ready');
|
|
184
|
+
console.log('');
|
|
185
|
+
console.log(c.dim(' Waiting for browser login flow to complete...'));
|
|
186
|
+
console.log('');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
190
|
+
// LOGIN
|
|
191
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
program
|
|
194
|
+
.command('login')
|
|
195
|
+
.description('Login and store JWT')
|
|
196
|
+
.action(async () => {
|
|
197
|
+
printLogo();
|
|
198
|
+
|
|
199
|
+
printBanner('AUTHENTICATION', 'Secure JWT login flow');
|
|
200
|
+
|
|
201
|
+
labelRow('Callback Port:', '4000');
|
|
202
|
+
labelRow('Auth Server:', FRONTENDBASE);
|
|
203
|
+
labelRow('Config Path:', CONFIG_PATH.replace(os.homedir(), '~'));
|
|
204
|
+
console.log(c.teal('│'));
|
|
205
|
+
|
|
206
|
+
closeBanner();
|
|
207
|
+
|
|
208
|
+
section('Starting OAuth Flow');
|
|
209
|
+
console.log('');
|
|
210
|
+
|
|
211
|
+
const app = express();
|
|
212
|
+
const port = 4000;
|
|
213
|
+
const stop = spinner('Binding to port 4000...');
|
|
214
|
+
|
|
215
|
+
const server = app.listen(port, () => {
|
|
216
|
+
stop();
|
|
217
|
+
const loginUrl = `${FRONTENDBASE}/login?callback=http://localhost:${port}/callback`;
|
|
218
|
+
|
|
219
|
+
badge.ok('Local callback server ready');
|
|
220
|
+
badge.loading('Opening browser for login...');
|
|
221
|
+
console.log('');
|
|
222
|
+
open(loginUrl);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
app.get('/callback', (req, res) => {
|
|
226
|
+
const token = req.query.token as string;
|
|
227
|
+
|
|
228
|
+
if (!token) {
|
|
229
|
+
res.status(400).send(renderHtmlPage('Error', '✘ No token received.', '#ff2d2d'));
|
|
230
|
+
badge.error('Callback hit but no token was received.');
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Save token
|
|
235
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify({ token }, null, 2));
|
|
236
|
+
|
|
237
|
+
res.send(renderHtmlPage('Success!', '✔ Login successful — you may close this tab.', '#00e5a0'));
|
|
238
|
+
|
|
239
|
+
section('Login Complete');
|
|
240
|
+
console.log('');
|
|
241
|
+
badge.ok('JWT received and stored successfully!');
|
|
242
|
+
console.log('');
|
|
243
|
+
|
|
244
|
+
console.log(c.teal('┌' + '─'.repeat(W - 2) + '┐'));
|
|
245
|
+
const preview = token.length > 50
|
|
246
|
+
? token.slice(0, 24) + c.dim(' ··· ') + token.slice(-24)
|
|
247
|
+
: token;
|
|
248
|
+
boxRow('TOKEN PREVIEW', c.yellow);
|
|
249
|
+
boxRow(preview, c.cyanDim);
|
|
250
|
+
console.log(c.teal('└' + '─'.repeat(W - 2) + '┘'));
|
|
251
|
+
console.log('');
|
|
252
|
+
console.log(c.dim(` Saved to: ${CONFIG_PATH}`));
|
|
253
|
+
console.log('');
|
|
254
|
+
|
|
255
|
+
server.close();
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
260
|
+
// WHOAMI
|
|
261
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
program
|
|
264
|
+
.command('whoami')
|
|
265
|
+
.description('Read stored JWT')
|
|
266
|
+
.action(() => {
|
|
267
|
+
printLogo();
|
|
268
|
+
printBanner('IDENTITY CHECK', 'Reading stored credentials');
|
|
269
|
+
labelRow('Config:', CONFIG_PATH.replace(os.homedir(), '~'));
|
|
270
|
+
console.log(c.teal('│'));
|
|
271
|
+
closeBanner();
|
|
272
|
+
|
|
273
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
274
|
+
console.log('');
|
|
275
|
+
badge.error('No credentials found. Run ' + c.cyan('login-cli login') + ' first.');
|
|
276
|
+
console.log('');
|
|
277
|
+
console.log(c.teal('┌' + '─'.repeat(W - 2) + '┐'));
|
|
278
|
+
boxRow('NOT AUTHENTICATED', c.red);
|
|
279
|
+
boxRow('Use: login-cli login', c.dim);
|
|
280
|
+
console.log(c.teal('└' + '─'.repeat(W - 2) + '┘'));
|
|
281
|
+
console.log('');
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const data = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
286
|
+
const { token } = data;
|
|
287
|
+
|
|
288
|
+
// Decode JWT header/payload for display (no verification)
|
|
289
|
+
let jwtInfo: { header?: any; payload?: any } = {};
|
|
290
|
+
try {
|
|
291
|
+
const [h, p] = token.split('.');
|
|
292
|
+
jwtInfo.header = JSON.parse(Buffer.from(h, 'base64').toString());
|
|
293
|
+
jwtInfo.payload = JSON.parse(Buffer.from(p, 'base64').toString());
|
|
294
|
+
} catch {
|
|
295
|
+
// not a valid JWT, just show raw
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
section('Stored Token');
|
|
299
|
+
console.log('');
|
|
300
|
+
|
|
301
|
+
console.log(c.teal('┌' + '─'.repeat(W - 2) + '┐'));
|
|
302
|
+
|
|
303
|
+
if (jwtInfo.payload) {
|
|
304
|
+
const p = jwtInfo.payload;
|
|
305
|
+
|
|
306
|
+
// Header meta
|
|
307
|
+
boxRow('JWT DECODED', c.yellow);
|
|
308
|
+
console.log(c.teal('├' + '─'.repeat(W - 2) + '┤'));
|
|
309
|
+
|
|
310
|
+
const fields: [string, any][] = Object.entries({
|
|
311
|
+
sub: p.sub ?? '—',
|
|
312
|
+
name: p.name ?? '—',
|
|
313
|
+
email: p.email ?? '—',
|
|
314
|
+
role: p.role ?? '—',
|
|
315
|
+
iat: p.iat ? new Date(p.iat * 1000).toISOString() : '—',
|
|
316
|
+
exp: p.exp ? new Date(p.exp * 1000).toISOString() : '—',
|
|
317
|
+
alg: jwtInfo.header?.alg ?? '—',
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
for (const [k, v] of fields) {
|
|
321
|
+
labelRow(`${k}:`, String(v));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Expiry warning
|
|
325
|
+
if (jwtInfo.payload.exp) {
|
|
326
|
+
const expired = Date.now() / 1000 > jwtInfo.payload.exp;
|
|
327
|
+
console.log(c.teal('├' + '─'.repeat(W - 2) + '┤'));
|
|
328
|
+
boxRow(expired ? '⚠ TOKEN EXPIRED' : '✔ TOKEN VALID', expired ? c.red : c.green);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
console.log(c.teal('├' + '─'.repeat(W - 2) + '┤'));
|
|
333
|
+
boxRow('RAW TOKEN', c.yellow);
|
|
334
|
+
|
|
335
|
+
// Print token split at 58 chars per line
|
|
336
|
+
const chunks = token.match(/.{1,54}/g) ?? [token];
|
|
337
|
+
for (const chunk of chunks) {
|
|
338
|
+
boxRow(chunk, c.cyanDim);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
console.log(c.teal('└' + '─'.repeat(W - 2) + '┘'));
|
|
342
|
+
console.log('');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
program
|
|
348
|
+
.command('project <apiKey>')
|
|
349
|
+
.description('Find and store project by API key')
|
|
350
|
+
.action(async (apiKey) => {
|
|
351
|
+
// 1. check if token exists
|
|
352
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
353
|
+
badge.error('Not logged in. Run errorhub login first.')
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// 2. read token
|
|
358
|
+
const data = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'))
|
|
359
|
+
const token = data.token
|
|
360
|
+
|
|
361
|
+
// 3. fetch all projects
|
|
362
|
+
const response = await fetch(`${FRONTENDBASE}/projects`, {
|
|
363
|
+
headers: {
|
|
364
|
+
Authorization: `Bearer ${token}`
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
const projects = await response.json()
|
|
368
|
+
|
|
369
|
+
// 4. find matching project
|
|
370
|
+
const match = projects.find((p: any) => p.api_key === apiKey)
|
|
371
|
+
|
|
372
|
+
// 5. no match found
|
|
373
|
+
if (!match) {
|
|
374
|
+
badge.error('No project found with that API key.')
|
|
375
|
+
return
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 6. save projectId
|
|
379
|
+
fs.writeFileSync(PROJECTS, JSON.stringify({ projectId: match.id }, null, 2))
|
|
380
|
+
|
|
381
|
+
// 7. confirm
|
|
382
|
+
badge.ok(`Project found and saved: ${match.id}`)
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
// ─────────────────────────=────────────────────────────────────────────────────
|
|
387
|
+
// HTML response page helper
|
|
388
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
389
|
+
|
|
390
|
+
function renderHtmlPage(title: string, message: string, color: string): string {
|
|
391
|
+
return `<!DOCTYPE html>
|
|
392
|
+
<html>
|
|
393
|
+
<head>
|
|
394
|
+
<meta charset="UTF-8"/>
|
|
395
|
+
<title>${title}</title>
|
|
396
|
+
<style>
|
|
397
|
+
* { margin:0; padding:0; box-sizing:border-box; }
|
|
398
|
+
body {
|
|
399
|
+
background: #0a0e14;
|
|
400
|
+
color: #e0e0e0;
|
|
401
|
+
font-family: 'Courier New', monospace;
|
|
402
|
+
display: flex;
|
|
403
|
+
align-items: center;
|
|
404
|
+
justify-content: center;
|
|
405
|
+
height: 100vh;
|
|
406
|
+
}
|
|
407
|
+
.card {
|
|
408
|
+
border: 1px solid ${color};
|
|
409
|
+
padding: 3rem 4rem;
|
|
410
|
+
text-align: center;
|
|
411
|
+
box-shadow: 0 0 40px ${color}44;
|
|
412
|
+
}
|
|
413
|
+
h1 { font-size: 2.5rem; color: ${color}; letter-spacing: 0.2em; }
|
|
414
|
+
p { margin-top: 1rem; color: #888; font-size: 0.9rem; }
|
|
415
|
+
</style>
|
|
416
|
+
</head>
|
|
417
|
+
<body>
|
|
418
|
+
<div class="card">
|
|
419
|
+
<h1>${title}</h1>
|
|
420
|
+
<p>${message}</p>
|
|
421
|
+
</div>
|
|
422
|
+
</body>
|
|
423
|
+
</html>`;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
program.parse();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ES2020",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"rootDir": "./src",
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"isolatedModules": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src"]
|
|
18
|
+
}
|