icoa-cli 2.19.63 → 2.19.65
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.js +35 -1
- package/dist/lib/update-check.js +25 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -65,19 +65,53 @@ process.on('unhandledRejection', (reason) => {
|
|
|
65
65
|
console.error(chalk.red('Error:'), msg);
|
|
66
66
|
process.exit(1);
|
|
67
67
|
});
|
|
68
|
+
// Pause for ms, skippable by any key press (so users can read the banner)
|
|
69
|
+
async function pauseWithSkip(ms) {
|
|
70
|
+
const stdin = process.stdin;
|
|
71
|
+
if (!stdin.isTTY || typeof stdin.setRawMode !== 'function') {
|
|
72
|
+
await new Promise((r) => setTimeout(r, ms));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
return new Promise((resolve) => {
|
|
76
|
+
let done = false;
|
|
77
|
+
const finish = () => {
|
|
78
|
+
if (done)
|
|
79
|
+
return;
|
|
80
|
+
done = true;
|
|
81
|
+
stdin.removeListener('data', onKey);
|
|
82
|
+
try {
|
|
83
|
+
stdin.setRawMode(false);
|
|
84
|
+
}
|
|
85
|
+
catch { }
|
|
86
|
+
stdin.pause();
|
|
87
|
+
resolve();
|
|
88
|
+
};
|
|
89
|
+
const onKey = () => finish();
|
|
90
|
+
stdin.setRawMode(true);
|
|
91
|
+
stdin.resume();
|
|
92
|
+
stdin.once('data', onKey);
|
|
93
|
+
console.log(chalk.gray(' (press any key to continue...)'));
|
|
94
|
+
setTimeout(finish, ms);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
68
97
|
const program = new Command();
|
|
69
98
|
program
|
|
70
99
|
.name('icoa')
|
|
71
100
|
.version('1.2.0')
|
|
72
101
|
.description('ICOA CLI — CLI-Native CTF Competition Terminal')
|
|
73
102
|
.option('--resume', 'Resume previous session')
|
|
74
|
-
.action((opts) => {
|
|
103
|
+
.action(async (opts) => {
|
|
75
104
|
// Force hacker theme: black background + green text
|
|
76
105
|
setTerminalTheme();
|
|
77
106
|
checkForUpdates();
|
|
78
107
|
console.log(BANNER);
|
|
79
108
|
// If running interactively (no extra args or --resume), start REPL
|
|
80
109
|
if (process.argv.length <= 2 || opts.resume) {
|
|
110
|
+
// Brief pause so users can read the banner + logo before mode selector appears
|
|
111
|
+
// Skip on --resume (user is coming back, doesn't need the intro)
|
|
112
|
+
if (!opts.resume) {
|
|
113
|
+
await pauseWithSkip(3000);
|
|
114
|
+
}
|
|
81
115
|
startRepl(program, !!opts.resume);
|
|
82
116
|
return;
|
|
83
117
|
}
|
package/dist/lib/update-check.js
CHANGED
|
@@ -11,6 +11,29 @@ const installCmd = platform() === 'win32'
|
|
|
11
11
|
: 'sudo npm install -g icoa-cli@latest';
|
|
12
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
13
|
const SIX_HOURS_MS = 6 * 60 * 60 * 1000;
|
|
14
|
+
function versionsBehind(latest, current) {
|
|
15
|
+
// Count PATCH-level diff only (e.g. 2.19.40 vs 2.19.46 = 6)
|
|
16
|
+
const l = latest.split('.').map(Number);
|
|
17
|
+
const c = current.split('.').map(Number);
|
|
18
|
+
if (l[0] !== c[0] || l[1] !== c[1])
|
|
19
|
+
return -1; // major/minor diff
|
|
20
|
+
return (l[2] ?? 0) - (c[2] ?? 0);
|
|
21
|
+
}
|
|
22
|
+
function printUpdateBanner(current, latest) {
|
|
23
|
+
const behind = versionsBehind(latest, current);
|
|
24
|
+
const behindStr = behind > 0 ? chalk.gray(` (${behind} version${behind === 1 ? '' : 's'} behind)`) : '';
|
|
25
|
+
const line = chalk.yellow(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
26
|
+
console.log();
|
|
27
|
+
console.log(line);
|
|
28
|
+
console.log(chalk.bold.yellow(' ⬆ New version available!'));
|
|
29
|
+
console.log();
|
|
30
|
+
console.log(chalk.white(' Current: ') + chalk.gray('v' + current));
|
|
31
|
+
console.log(chalk.white(' Latest: ') + chalk.bold.green('v' + latest) + behindStr);
|
|
32
|
+
console.log();
|
|
33
|
+
console.log(chalk.white(' Update: ') + chalk.bold.cyan(installCmd));
|
|
34
|
+
console.log(line);
|
|
35
|
+
console.log();
|
|
36
|
+
}
|
|
14
37
|
function isNewer(latest, current) {
|
|
15
38
|
const l = latest.split('.').map(Number);
|
|
16
39
|
const c = current.split('.').map(Number);
|
|
@@ -38,9 +61,7 @@ export function checkForUpdates() {
|
|
|
38
61
|
if (Date.now() - cache.lastCheck < SIX_HOURS_MS) {
|
|
39
62
|
// Still within cooldown — but show banner if cached version is newer
|
|
40
63
|
if (cache.latestVersion && isNewer(cache.latestVersion, current)) {
|
|
41
|
-
|
|
42
|
-
chalk.white('v' + current + ' \u2192 v' + cache.latestVersion) +
|
|
43
|
-
chalk.gray(` Run: ${installCmd}`));
|
|
64
|
+
printUpdateBanner(current, cache.latestVersion);
|
|
44
65
|
}
|
|
45
66
|
return;
|
|
46
67
|
}
|
|
@@ -71,9 +92,7 @@ export function checkForUpdates() {
|
|
|
71
92
|
writeFileSync(cacheFile, JSON.stringify(cache, null, 2));
|
|
72
93
|
// Print banner if newer
|
|
73
94
|
if (isNewer(latest, current)) {
|
|
74
|
-
|
|
75
|
-
chalk.white('v' + current + ' \u2192 v' + latest) +
|
|
76
|
-
chalk.gray(` Run: ${installCmd}`));
|
|
95
|
+
printUpdateBanner(current, latest);
|
|
77
96
|
}
|
|
78
97
|
}
|
|
79
98
|
catch {
|