icoa-cli 2.19.98 → 2.19.100
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/commands/env.js +2 -1
- package/dist/commands/lang.js +2 -2
- package/dist/commands/log.js +2 -0
- package/dist/commands/note.js +9 -1
- package/dist/commands/ref.js +7 -2
- package/dist/commands/setup.js +88 -69
- package/dist/commands/theme.js +2 -2
- package/dist/repl.js +42 -11
- package/package.json +1 -1
package/dist/commands/env.js
CHANGED
|
@@ -506,8 +506,9 @@ function showStatus() {
|
|
|
506
506
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
507
507
|
console.log(` ${chalk.green(`✓ ${installed}/${total}`)} ${missing > 0 ? chalk.red(`✗ ${missing} missing`) : chalk.green('All 110 ready!')}`);
|
|
508
508
|
if (missing > 0) {
|
|
509
|
-
console.log(chalk.gray(' Install everything: ') + chalk.white('env setup'));
|
|
509
|
+
console.log(chalk.gray(' Install everything: ') + chalk.white('env setup') + chalk.gray(' (~5 min, one-time)'));
|
|
510
510
|
}
|
|
511
|
+
console.log(chalk.gray(' You are back at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt. ') + chalk.cyan('help') + chalk.gray(' for all commands.'));
|
|
511
512
|
console.log();
|
|
512
513
|
}
|
|
513
514
|
async function installAll() {
|
package/dist/commands/lang.js
CHANGED
|
@@ -39,8 +39,8 @@ export function registerLangCommand(program) {
|
|
|
39
39
|
console.log(` ${chalk.white(lang)} ${LANG_NAMES[lang]}${current}`);
|
|
40
40
|
}
|
|
41
41
|
console.log();
|
|
42
|
-
console.log(chalk.gray('
|
|
43
|
-
console.log(chalk.gray('
|
|
42
|
+
console.log(chalk.gray(' Switch now: ') + chalk.cyan('lang <code>') + chalk.gray(' (e.g. ') + chalk.cyan('lang es') + chalk.gray(')'));
|
|
43
|
+
console.log(chalk.gray(' No "back" needed — you are still at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt.'));
|
|
44
44
|
console.log();
|
|
45
45
|
return;
|
|
46
46
|
}
|
package/dist/commands/log.js
CHANGED
|
@@ -53,6 +53,8 @@ function showLog() {
|
|
|
53
53
|
printTable(['Time', 'Type', 'Content'], rows);
|
|
54
54
|
console.log(chalk.gray(` ${entries.length} entries total`));
|
|
55
55
|
console.log();
|
|
56
|
+
console.log(chalk.gray(' You are at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt. Also: ') + chalk.cyan('log stats') + chalk.gray(' · ') + chalk.cyan('log export') + chalk.gray(' · ') + chalk.cyan('help') + chalk.gray(' all commands.'));
|
|
57
|
+
console.log();
|
|
56
58
|
}
|
|
57
59
|
async function exportLog() {
|
|
58
60
|
const config = getConfig();
|
package/dist/commands/note.js
CHANGED
|
@@ -14,12 +14,20 @@ export function registerNoteCommand(program) {
|
|
|
14
14
|
if (!words || words.length === 0) {
|
|
15
15
|
// Display existing notes
|
|
16
16
|
if (!existsSync(NOTES_FILE)) {
|
|
17
|
-
printInfo('No notes yet. Add one with:
|
|
17
|
+
printInfo('No notes yet. Add one with: note "your note here"');
|
|
18
|
+
console.log();
|
|
19
|
+
console.log(chalk.gray(' You are at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt. Type another command or ') + chalk.cyan('help') + chalk.gray(' for the list.'));
|
|
20
|
+
console.log();
|
|
18
21
|
return;
|
|
19
22
|
}
|
|
20
23
|
const content = readFileSync(NOTES_FILE, 'utf-8');
|
|
21
24
|
printHeader('Notes');
|
|
22
25
|
console.log(content);
|
|
26
|
+
console.log();
|
|
27
|
+
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
28
|
+
console.log(chalk.gray(' End of notes. You are back at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt.'));
|
|
29
|
+
console.log(chalk.gray(' Add one: ') + chalk.cyan('note "your text"') + chalk.gray(' · ') + chalk.cyan('help') + chalk.gray(' for all commands'));
|
|
30
|
+
console.log();
|
|
23
31
|
return;
|
|
24
32
|
}
|
|
25
33
|
// Add new note
|
package/dist/commands/ref.js
CHANGED
|
@@ -44,8 +44,8 @@ export function registerRefCommand(program) {
|
|
|
44
44
|
const rows = files.map((f) => [chalk.white(f)]);
|
|
45
45
|
printTable(['Topic'], rows);
|
|
46
46
|
console.log();
|
|
47
|
-
console.log(chalk.gray(
|
|
48
|
-
console.log(chalk.gray(
|
|
47
|
+
console.log(chalk.gray(' Open one: ') + chalk.cyan('ref <topic>') + chalk.gray(' (e.g. ') + chalk.cyan('ref python') + chalk.gray(')'));
|
|
48
|
+
console.log(chalk.gray(' No "back" needed — ref just prints and returns to the ') + chalk.cyan('icoa>') + chalk.gray(' prompt.'));
|
|
49
49
|
console.log();
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
@@ -59,5 +59,10 @@ export function registerRefCommand(program) {
|
|
|
59
59
|
const content = readFileSync(filePath, 'utf-8');
|
|
60
60
|
printHeader(`Reference: ${topic}`);
|
|
61
61
|
console.log(content);
|
|
62
|
+
console.log();
|
|
63
|
+
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
64
|
+
console.log(chalk.gray(' End of ') + chalk.cyan(`ref ${topic}`) + chalk.gray('. You are back at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt.'));
|
|
65
|
+
console.log(chalk.gray(' Next: ') + chalk.cyan('ref') + chalk.gray(' list topics · ') + chalk.cyan('ref <topic>') + chalk.gray(' open another · ') + chalk.cyan('help') + chalk.gray(' all commands'));
|
|
66
|
+
console.log();
|
|
62
67
|
});
|
|
63
68
|
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -13,6 +13,8 @@ export function registerSetupCommand(program) {
|
|
|
13
13
|
const config = getConfig();
|
|
14
14
|
printHeader('ICOA CLI Setup');
|
|
15
15
|
console.log();
|
|
16
|
+
console.log(chalk.gray(' Tip: press ') + chalk.cyan('Ctrl+C') + chalk.gray(' any time to cancel setup and return to the ') + chalk.cyan('icoa>') + chalk.gray(' prompt — nothing is saved until you confirm.'));
|
|
17
|
+
console.log();
|
|
16
18
|
// Show current configuration
|
|
17
19
|
printKeyValue('CTFd URL', config.ctfdUrl || chalk.gray('Not configured'));
|
|
18
20
|
printKeyValue('CTFd Token', config.token ? chalk.green('Configured') : chalk.gray('Not configured'));
|
|
@@ -21,83 +23,100 @@ export function registerSetupCommand(program) {
|
|
|
21
23
|
printKeyValue('Mode', config.mode || chalk.gray('Not set'));
|
|
22
24
|
printKeyValue('Session ID', config.sessionId.substring(0, 8) + '...');
|
|
23
25
|
console.log();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
case 'gemini': {
|
|
50
|
-
console.log();
|
|
51
|
-
printInfo('Get your API key from: https://aistudio.google.com/apikey');
|
|
52
|
-
console.log();
|
|
53
|
-
const key = await input({ message: 'Enter Gemini API Key:' });
|
|
54
|
-
if (key.trim()) {
|
|
55
|
-
setApiKey(key.trim());
|
|
56
|
-
printSuccess('Gemini API key saved.');
|
|
26
|
+
try {
|
|
27
|
+
const action = await select({
|
|
28
|
+
message: 'What would you like to configure?',
|
|
29
|
+
choices: [
|
|
30
|
+
{ name: 'Switch Mode', value: 'mode' },
|
|
31
|
+
{ name: 'Gemini API Key', value: 'gemini' },
|
|
32
|
+
{ name: 'CTFd Connection', value: 'ctfd' },
|
|
33
|
+
{ name: 'Reset Hint Budget', value: 'budget' },
|
|
34
|
+
{ name: 'View All Settings', value: 'view' },
|
|
35
|
+
{ name: 'Exit', value: 'exit' },
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
switch (action) {
|
|
39
|
+
case 'mode': {
|
|
40
|
+
const newMode = await select({
|
|
41
|
+
message: 'Select mode:',
|
|
42
|
+
choices: [
|
|
43
|
+
{ name: 'National Selection — demo, exam, lightweight', value: 'selection' },
|
|
44
|
+
{ name: 'International Olympiad — Full CTF with AI assistance', value: 'olympiad' },
|
|
45
|
+
{ name: 'National/Regional Partner — Organizer management', value: 'organizer' },
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
saveConfig({ mode: newMode });
|
|
49
|
+
printSuccess(`Mode switched to: ${newMode}. Restart ICOA CLI to apply.`);
|
|
50
|
+
break;
|
|
57
51
|
}
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
case 'gemini': {
|
|
53
|
+
console.log();
|
|
54
|
+
printInfo('Get your API key from: https://aistudio.google.com/apikey');
|
|
55
|
+
console.log();
|
|
56
|
+
const key = await input({ message: 'Enter Gemini API Key:' });
|
|
57
|
+
if (key.trim()) {
|
|
58
|
+
setApiKey(key.trim());
|
|
59
|
+
printSuccess('Gemini API key saved.');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
printError('No key provided.');
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
60
65
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
printInfo('Use "join <url>" to connect to a CTFd instance.');
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
case 'budget': {
|
|
68
|
-
const { confirm } = await import('@inquirer/prompts');
|
|
69
|
-
const proceed = await confirm({
|
|
70
|
-
message: 'Reset hint budget to defaults (A:50, B:10, C:2)? This cannot be undone.',
|
|
71
|
-
default: false,
|
|
72
|
-
});
|
|
73
|
-
if (proceed) {
|
|
74
|
-
const { saveBudget } = await import('../lib/config.js');
|
|
75
|
-
const { DEFAULT_BUDGET } = await import('../types/index.js');
|
|
76
|
-
saveBudget({ ...DEFAULT_BUDGET });
|
|
77
|
-
printSuccess('Hint budget reset to defaults.');
|
|
66
|
+
case 'ctfd': {
|
|
67
|
+
printInfo('Use "join <url>" to connect to a CTFd instance.');
|
|
68
|
+
break;
|
|
78
69
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
70
|
+
case 'budget': {
|
|
71
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
72
|
+
const proceed = await confirm({
|
|
73
|
+
message: 'Reset hint budget to defaults (A:50, B:10, C:2)? This cannot be undone.',
|
|
74
|
+
default: false,
|
|
75
|
+
});
|
|
76
|
+
if (proceed) {
|
|
77
|
+
const { saveBudget } = await import('../lib/config.js');
|
|
78
|
+
const { DEFAULT_BUDGET } = await import('../types/index.js');
|
|
79
|
+
saveBudget({ ...DEFAULT_BUDGET });
|
|
80
|
+
printSuccess('Hint budget reset to defaults.');
|
|
88
81
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case 'view': {
|
|
85
|
+
console.log();
|
|
86
|
+
printHeader('Full Configuration');
|
|
87
|
+
const full = getConfig();
|
|
88
|
+
for (const [key, value] of Object.entries(full)) {
|
|
89
|
+
if (key === 'token' && value) {
|
|
90
|
+
printKeyValue(key, value.toString().substring(0, 8) + '...');
|
|
91
|
+
}
|
|
92
|
+
else if (key === 'geminiApiKey' && value) {
|
|
93
|
+
printKeyValue(key, value.toString().substring(0, 8) + '...');
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
printKeyValue(key, String(value ?? 'null'));
|
|
97
|
+
}
|
|
94
98
|
}
|
|
99
|
+
console.log();
|
|
100
|
+
break;
|
|
95
101
|
}
|
|
102
|
+
case 'exit':
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
console.log();
|
|
106
|
+
console.log(chalk.gray(' Setup complete. You are back at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt.'));
|
|
107
|
+
console.log();
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
// @inquirer/prompts throws ExitPromptError on Ctrl+C. Swallow it cleanly
|
|
111
|
+
// so users don't see a scary stack trace — setup is entirely reversible,
|
|
112
|
+
// and every save in this wizard is behind an explicit confirm step.
|
|
113
|
+
if (err?.name === 'ExitPromptError') {
|
|
114
|
+
console.log();
|
|
115
|
+
console.log(chalk.gray(' Setup cancelled. You are back at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt. Nothing was saved.'));
|
|
96
116
|
console.log();
|
|
97
|
-
|
|
117
|
+
return;
|
|
98
118
|
}
|
|
99
|
-
|
|
100
|
-
break;
|
|
119
|
+
throw err;
|
|
101
120
|
}
|
|
102
121
|
});
|
|
103
122
|
}
|
package/dist/commands/theme.js
CHANGED
|
@@ -29,8 +29,8 @@ export function registerThemeCommand(program) {
|
|
|
29
29
|
console.log(' ' + chalk.white('dark ') + chalk.gray('Darcula — gray on dark gray (default)'));
|
|
30
30
|
console.log(' ' + chalk.white('high-contrast ') + chalk.gray('Pure white on pure black — low vision / projectors'));
|
|
31
31
|
console.log();
|
|
32
|
-
console.log(chalk.gray('
|
|
33
|
-
console.log(chalk.gray('
|
|
32
|
+
console.log(chalk.gray(' Switch: ') + chalk.cyan('theme <name>') + chalk.gray(' (applies on next ') + chalk.cyan('icoa') + chalk.gray(' launch)'));
|
|
33
|
+
console.log(chalk.gray(' No "back" needed — you are still at the ') + chalk.cyan('icoa>') + chalk.gray(' prompt.'));
|
|
34
34
|
console.log();
|
|
35
35
|
return;
|
|
36
36
|
}
|
package/dist/repl.js
CHANGED
|
@@ -454,6 +454,10 @@ export async function startRepl(program, resumeMode) {
|
|
|
454
454
|
console.log(chalk.white(' status') + chalk.gray(' Your score & hint budget'));
|
|
455
455
|
console.log(chalk.white(' scoreboard') + chalk.gray(' Live rankings'));
|
|
456
456
|
console.log(chalk.white(' help') + chalk.gray(' Full command list'));
|
|
457
|
+
console.log();
|
|
458
|
+
console.log(chalk.gray(' Tool environment:'));
|
|
459
|
+
console.log(chalk.white(' env') + chalk.gray(' See which of the 110 CTF tools are installed'));
|
|
460
|
+
console.log(chalk.white(' env setup') + chalk.gray(' Install anything missing (~5 min, one-time)'));
|
|
457
461
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
458
462
|
console.log(chalk.gray(' Tip: ') + chalk.cyan('help') + chalk.gray(' · ') + chalk.cyan('Ctrl+C') + chalk.gray(' pauses · ') + chalk.cyan('quit') + chalk.gray(' closes'));
|
|
459
463
|
console.log();
|
|
@@ -470,7 +474,11 @@ export async function startRepl(program, resumeMode) {
|
|
|
470
474
|
console.log(chalk.white(' Step 2 ') + chalk.bold.cyan('challenges') + chalk.gray(' Browse & solve challenges'));
|
|
471
475
|
console.log(chalk.white(' Step 3 ') + chalk.bold.cyan('hint') + chalk.gray(' Ask AI when stuck'));
|
|
472
476
|
console.log();
|
|
473
|
-
console.log(chalk.gray('
|
|
477
|
+
console.log(chalk.gray(' Before Step 1 — make sure your tools are ready:'));
|
|
478
|
+
console.log(chalk.white(' env') + chalk.gray(' See which of the 110 CTF tools are installed'));
|
|
479
|
+
console.log(chalk.white(' env setup') + chalk.gray(' Install anything missing (~5 min, one-time)'));
|
|
480
|
+
console.log();
|
|
481
|
+
console.log(chalk.gray(' Also: ') + chalk.white('help') + chalk.gray(' all commands'));
|
|
474
482
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
475
483
|
console.log(chalk.gray(' Tip: ') + chalk.cyan('Ctrl+C') + chalk.gray(' pauses · ') + chalk.cyan('exit') + chalk.gray(' → menu · ') + chalk.cyan('quit') + chalk.gray(' closes CLI'));
|
|
476
484
|
console.log();
|
|
@@ -486,7 +494,8 @@ export async function startRepl(program, resumeMode) {
|
|
|
486
494
|
console.log(chalk.gray(' While waiting, explore:'));
|
|
487
495
|
console.log(chalk.white(' ref linux') + chalk.gray(' Quick reference for Linux'));
|
|
488
496
|
console.log(chalk.white(' ref web') + chalk.gray(' Quick reference for Web'));
|
|
489
|
-
console.log(chalk.white(' env') + chalk.gray('
|
|
497
|
+
console.log(chalk.white(' env') + chalk.gray(' See which of the 110 CTF tools are installed'));
|
|
498
|
+
console.log(chalk.white(' env setup') + chalk.gray(' Install anything missing (~5 min, one-time)'));
|
|
490
499
|
console.log(chalk.white(' help') + chalk.gray(' All available commands'));
|
|
491
500
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
492
501
|
console.log(chalk.gray(' Tip: ') + chalk.cyan('Ctrl+C') + chalk.gray(' pauses · ') + chalk.cyan('exit') + chalk.gray(' → menu · ') + chalk.cyan('quit') + chalk.gray(' closes CLI'));
|
|
@@ -575,13 +584,30 @@ export async function startRepl(program, resumeMode) {
|
|
|
575
584
|
rl.prompt();
|
|
576
585
|
return;
|
|
577
586
|
}
|
|
578
|
-
// Explicit quit — `quit`
|
|
579
|
-
|
|
580
|
-
|
|
587
|
+
// Explicit quit — `quit` / `q` close the CLI. During a live **real** exam
|
|
588
|
+
// (token-gated, timed, graded) we require `quit confirm` as a second step
|
|
589
|
+
// to prevent accidental loss of a 90-minute session to a single keystroke.
|
|
590
|
+
// Demo has no time pressure and no scoring, so demo quit stays one-step.
|
|
591
|
+
if (input === 'quit' || input === 'q' || input === 'quit confirm') {
|
|
592
|
+
const state = getExamState();
|
|
593
|
+
const isRealExam = state && state.session.examId !== 'demo-free';
|
|
594
|
+
if (isRealExam && input !== 'quit confirm') {
|
|
595
|
+
console.log();
|
|
596
|
+
console.log(chalk.yellow(' ⚠ A real exam is in progress.'));
|
|
597
|
+
console.log(chalk.gray(' Your answers are auto-saved on the server, but the exam timer keeps ticking'));
|
|
598
|
+
console.log(chalk.gray(' on the server side even if you close the CLI.'));
|
|
581
599
|
console.log();
|
|
582
|
-
console.log(chalk.
|
|
583
|
-
console.log(chalk.gray('
|
|
600
|
+
console.log(chalk.white(' To leave the CLI but keep the exam alive, type: ') + chalk.bold.cyan('back'));
|
|
601
|
+
console.log(chalk.gray(' (recommended — you can resume with ') + chalk.cyan('exam q 1') + chalk.gray(' after relaunching icoa)'));
|
|
584
602
|
console.log();
|
|
603
|
+
console.log(chalk.white(' To really close ICOA CLI, type: ') + chalk.bold.cyan('quit confirm'));
|
|
604
|
+
console.log();
|
|
605
|
+
rl.prompt();
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
if (state && state.session.examId === 'demo-free') {
|
|
609
|
+
console.log();
|
|
610
|
+
console.log(chalk.gray(' Demo paused. Resume with: ') + chalk.white('demo') + chalk.gray(' (fresh) or ') + chalk.white('exam q 1') + chalk.gray(' (continue).'));
|
|
585
611
|
}
|
|
586
612
|
stopLogSync();
|
|
587
613
|
recordExit();
|
|
@@ -1077,16 +1103,21 @@ export async function startRepl(program, resumeMode) {
|
|
|
1077
1103
|
rl.on('SIGINT', () => {
|
|
1078
1104
|
console.log();
|
|
1079
1105
|
if (isChatActive() || isCtf4aiActive()) {
|
|
1080
|
-
console.log(chalk.yellow('
|
|
1106
|
+
console.log(chalk.yellow(' Ctrl+C did not close ICOA CLI — you are still in the AI chat.'));
|
|
1107
|
+
console.log(chalk.white(' Type ') + chalk.bold.cyan('exit') + chalk.white(' to leave the chat and return to the menu.'));
|
|
1081
1108
|
}
|
|
1082
1109
|
else if (getExamState()) {
|
|
1083
|
-
|
|
1110
|
+
const isReal = getExamState().session.examId !== 'demo-free';
|
|
1111
|
+
console.log(chalk.yellow(' Ctrl+C did NOT close ICOA CLI.'));
|
|
1112
|
+
console.log(chalk.gray(` Your ${isReal ? 'exam' : 'demo'} is paused and every answer is auto-saved.`));
|
|
1113
|
+
console.log();
|
|
1084
1114
|
console.log(chalk.white(' Resume: ') + chalk.cyan('exam q 1') +
|
|
1085
1115
|
chalk.gray(' · Back to menu: ') + chalk.cyan('back') +
|
|
1086
|
-
chalk.gray(' · Close CLI: ') + chalk.cyan('quit'));
|
|
1116
|
+
chalk.gray(' · Close CLI: ') + chalk.cyan(isReal ? 'quit confirm' : 'quit'));
|
|
1087
1117
|
}
|
|
1088
1118
|
else {
|
|
1089
|
-
console.log(chalk.yellow('
|
|
1119
|
+
console.log(chalk.yellow(' Ctrl+C did not close ICOA CLI — you are still at the ') + chalk.cyan('icoa>') + chalk.yellow(' prompt.'));
|
|
1120
|
+
console.log(chalk.gray(' Keep typing — ') + chalk.cyan('help') + chalk.gray(' lists commands. (Only ') + chalk.cyan('quit') + chalk.gray(' or Ctrl+D actually close the CLI.)'));
|
|
1090
1121
|
}
|
|
1091
1122
|
console.log();
|
|
1092
1123
|
rl.prompt();
|