icoa-cli 2.19.96 → 2.19.98
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/exam.js +16 -1
- package/dist/lib/paper-upgrade.d.ts +32 -0
- package/dist/lib/paper-upgrade.js +117 -0
- package/dist/lib/platform.d.ts +58 -0
- package/dist/lib/platform.js +86 -0
- package/dist/repl.js +26 -12
- package/package.json +1 -1
package/dist/commands/exam.js
CHANGED
|
@@ -1447,6 +1447,7 @@ export function registerExamCommand(program) {
|
|
|
1447
1447
|
interactions: (state.interactions || []).length,
|
|
1448
1448
|
durationMin: state.session.durationMinutes || 0,
|
|
1449
1449
|
confirmedAt: state.session.confirmedAt || state.session.startedAt,
|
|
1450
|
+
examId: state.session.examId,
|
|
1450
1451
|
};
|
|
1451
1452
|
clearExamState();
|
|
1452
1453
|
const elapsedSec = Math.max(0, Math.round((Date.now() - new Date(preSubmit.confirmedAt).getTime()) / 1000));
|
|
@@ -1524,6 +1525,11 @@ export function registerExamCommand(program) {
|
|
|
1524
1525
|
console.log(chalk.gray(' · Keep sharpening: ') + chalk.white('demo') + chalk.gray(' is free, unlimited practice.'));
|
|
1525
1526
|
console.log(chalk.gray(' · Reference guides for 38 tools: ') + chalk.white('ref'));
|
|
1526
1527
|
console.log();
|
|
1528
|
+
// v2.19.98 — Paper C → B upgrade funnel. Students who pass the entry
|
|
1529
|
+
// MCQ paper get a platform-aware nudge toward Paper B (K-12 + AI).
|
|
1530
|
+
// No-op for non-C papers and for non-passers. See src/lib/paper-upgrade.ts.
|
|
1531
|
+
const { showCToBUpgradePrompt } = await import('../lib/paper-upgrade.js');
|
|
1532
|
+
showCToBUpgradePrompt(preSubmit.examId, result.passed);
|
|
1527
1533
|
console.log(chalk.yellow(' ICOA 2026 · Sydney, Australia · Jun 27 – Jul 2'));
|
|
1528
1534
|
console.log(chalk.cyan.underline(' https://icoa2026.au'));
|
|
1529
1535
|
console.log();
|
|
@@ -1658,7 +1664,16 @@ export function registerExamCommand(program) {
|
|
|
1658
1664
|
return;
|
|
1659
1665
|
}
|
|
1660
1666
|
// Gate: require exam setup
|
|
1661
|
-
|
|
1667
|
+
// v2.19.97 — Windows cmd users bypass the exam-setup gate entirely.
|
|
1668
|
+
// Their recommended path is C paper (MCQ only, no Python / no Unix tools),
|
|
1669
|
+
// so forcing them through `exam setup` (which installs pip packages) would
|
|
1670
|
+
// be both unnecessary and likely to fail. Server enforces token→exam_id
|
|
1671
|
+
// binding (1:1 FK), so if they use a B/A token on cmd they'll still get
|
|
1672
|
+
// routed to the correct exam; but the missing tools may cost them points
|
|
1673
|
+
// on Q31-36. That's the trade-off they opted into.
|
|
1674
|
+
const { isNativeWindowsCmd: _isNativeWindowsCmd } = await import('../lib/platform.js');
|
|
1675
|
+
const cmdPath = _isNativeWindowsCmd();
|
|
1676
|
+
if (!cmdPath && !isExamSetupComplete()) {
|
|
1662
1677
|
console.log();
|
|
1663
1678
|
printWarning('Pre-exam setup required before entering a token.');
|
|
1664
1679
|
console.log(chalk.gray(' → ') + chalk.bold.cyan('exam setup'));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paper C → Paper B upgrade prompt.
|
|
3
|
+
*
|
|
4
|
+
* Shown after a successful Paper C submission. Paper C is the K-12 entry
|
|
5
|
+
* funnel (MCQ only, no tools). Paper B is the next step — adds AI4CTF
|
|
6
|
+
* and CTF4AI practical challenges for 150-point total. The upgrade path
|
|
7
|
+
* depends on which platform the student is on.
|
|
8
|
+
*
|
|
9
|
+
* Policy:
|
|
10
|
+
* - Only triggered after Paper C submission (examId ends in `-c` or
|
|
11
|
+
* starts with a country code + `-2026-c`).
|
|
12
|
+
* - Only triggered if student passed. Students who failed C should
|
|
13
|
+
* retry C before being pushed toward a harder paper.
|
|
14
|
+
* - Always points at https://icoa2026.au/selectionguide/ for detailed
|
|
15
|
+
* platform-specific install instructions, so the CLI copy stays
|
|
16
|
+
* concise and the canonical guide lives in one place.
|
|
17
|
+
* - Never prescribes a token — students must request one from their
|
|
18
|
+
* organizer. This prevents the CLI from implying self-service token
|
|
19
|
+
* issuance.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* True when the just-submitted exam is a C paper (MCQ entry funnel).
|
|
23
|
+
* Matches `ua-2026-c`, `pe-2026-c`, `cn-2026-c`, etc. Case-insensitive.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isCPaper(examId: string | undefined): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Renders the C→B upgrade prompt to stdout. No-op if conditions aren't met.
|
|
28
|
+
*
|
|
29
|
+
* @param examId The submitted exam ID (from state.session.examId before clear)
|
|
30
|
+
* @param passed Whether the student met the passing bar
|
|
31
|
+
*/
|
|
32
|
+
export declare function showCToBUpgradePrompt(examId: string | undefined, passed: boolean): void;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paper C → Paper B upgrade prompt.
|
|
3
|
+
*
|
|
4
|
+
* Shown after a successful Paper C submission. Paper C is the K-12 entry
|
|
5
|
+
* funnel (MCQ only, no tools). Paper B is the next step — adds AI4CTF
|
|
6
|
+
* and CTF4AI practical challenges for 150-point total. The upgrade path
|
|
7
|
+
* depends on which platform the student is on.
|
|
8
|
+
*
|
|
9
|
+
* Policy:
|
|
10
|
+
* - Only triggered after Paper C submission (examId ends in `-c` or
|
|
11
|
+
* starts with a country code + `-2026-c`).
|
|
12
|
+
* - Only triggered if student passed. Students who failed C should
|
|
13
|
+
* retry C before being pushed toward a harder paper.
|
|
14
|
+
* - Always points at https://icoa2026.au/selectionguide/ for detailed
|
|
15
|
+
* platform-specific install instructions, so the CLI copy stays
|
|
16
|
+
* concise and the canonical guide lives in one place.
|
|
17
|
+
* - Never prescribes a token — students must request one from their
|
|
18
|
+
* organizer. This prevents the CLI from implying self-service token
|
|
19
|
+
* issuance.
|
|
20
|
+
*/
|
|
21
|
+
import chalk from 'chalk';
|
|
22
|
+
import { isNativeWindowsCmd, isInWSL, hasPython } from './platform.js';
|
|
23
|
+
/**
|
|
24
|
+
* True when the just-submitted exam is a C paper (MCQ entry funnel).
|
|
25
|
+
* Matches `ua-2026-c`, `pe-2026-c`, `cn-2026-c`, etc. Case-insensitive.
|
|
26
|
+
*/
|
|
27
|
+
export function isCPaper(examId) {
|
|
28
|
+
if (!examId)
|
|
29
|
+
return false;
|
|
30
|
+
return /-2026-c$/i.test(examId.trim());
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Renders the C→B upgrade prompt to stdout. No-op if conditions aren't met.
|
|
34
|
+
*
|
|
35
|
+
* @param examId The submitted exam ID (from state.session.examId before clear)
|
|
36
|
+
* @param passed Whether the student met the passing bar
|
|
37
|
+
*/
|
|
38
|
+
export function showCToBUpgradePrompt(examId, passed) {
|
|
39
|
+
if (!isCPaper(examId))
|
|
40
|
+
return;
|
|
41
|
+
if (!passed)
|
|
42
|
+
return;
|
|
43
|
+
const onCmd = isNativeWindowsCmd();
|
|
44
|
+
const onWSL = isInWSL();
|
|
45
|
+
const pyReady = hasPython();
|
|
46
|
+
console.log(chalk.cyan(' ─────────────────────────────────────────────'));
|
|
47
|
+
console.log();
|
|
48
|
+
console.log(chalk.bold.white(' ★ Ready for more? Paper B — K-12 with AI'));
|
|
49
|
+
console.log();
|
|
50
|
+
console.log(chalk.gray(' Paper B adds ') + chalk.white('AI4CTF') + chalk.gray(' (chat with AI to find hidden flags) and ')
|
|
51
|
+
+ chalk.white('CTF4AI') + chalk.gray(' (break AI security).'));
|
|
52
|
+
console.log(chalk.gray(' 150 points total · 90 minutes · same command interface you already know.'));
|
|
53
|
+
console.log();
|
|
54
|
+
if (onCmd) {
|
|
55
|
+
// Windows cmd / PowerShell — needs WSL2 path for full Paper B
|
|
56
|
+
console.log(chalk.bold.white(' To take Paper B on Windows, install WSL2 + Ubuntu 22:'));
|
|
57
|
+
console.log();
|
|
58
|
+
console.log(chalk.white(' 1. Open PowerShell as Administrator'));
|
|
59
|
+
console.log(chalk.gray(' (right-click PowerShell → "Run as administrator")'));
|
|
60
|
+
console.log(chalk.white(' 2. Run: ') + chalk.cyan('wsl --install -d Ubuntu-22.04'));
|
|
61
|
+
console.log(chalk.white(' 3. Reboot when prompted, create a Linux username + password'));
|
|
62
|
+
console.log(chalk.white(' 4. Inside Ubuntu, install Node.js 22 and this CLI:'));
|
|
63
|
+
console.log(chalk.gray(' ') + chalk.cyan('curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -'));
|
|
64
|
+
console.log(chalk.gray(' ') + chalk.cyan('sudo apt install -y nodejs'));
|
|
65
|
+
console.log(chalk.gray(' ') + chalk.cyan('sudo npm install -g icoa-cli'));
|
|
66
|
+
console.log();
|
|
67
|
+
console.log(chalk.gray(' Setup takes 30-60 min the first time. You only do this once.'));
|
|
68
|
+
}
|
|
69
|
+
else if (onWSL) {
|
|
70
|
+
// Already on WSL2 — just need Python for practicals
|
|
71
|
+
console.log(chalk.bold.white(' You are on WSL2 — your setup is almost ready:'));
|
|
72
|
+
console.log();
|
|
73
|
+
if (!pyReady) {
|
|
74
|
+
console.log(chalk.white(' Install Python 3 (for Paper B practical questions):'));
|
|
75
|
+
console.log(chalk.gray(' ') + chalk.cyan('sudo apt install -y python3 python3-pip'));
|
|
76
|
+
console.log();
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(chalk.green(' ✓ Python 3 already installed. You are ready.'));
|
|
80
|
+
console.log();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (process.platform === 'darwin') {
|
|
84
|
+
// macOS
|
|
85
|
+
console.log(chalk.bold.white(' On macOS, install Python 3 for Paper B practicals:'));
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(chalk.white(' With Homebrew:'));
|
|
88
|
+
console.log(chalk.gray(' ') + chalk.cyan('brew install python@3.12'));
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(chalk.white(' Without Homebrew: download from ') + chalk.cyan.underline('https://python.org'));
|
|
91
|
+
if (pyReady) {
|
|
92
|
+
console.log();
|
|
93
|
+
console.log(chalk.green(' ✓ Python 3 already detected. You are ready.'));
|
|
94
|
+
}
|
|
95
|
+
console.log();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Linux native (not WSL)
|
|
99
|
+
console.log(chalk.bold.white(' On Linux, install Python 3 for Paper B practicals:'));
|
|
100
|
+
console.log();
|
|
101
|
+
console.log(chalk.white(' Ubuntu / Debian:'));
|
|
102
|
+
console.log(chalk.gray(' ') + chalk.cyan('sudo apt install -y python3 python3-pip'));
|
|
103
|
+
console.log(chalk.white(' Fedora / RHEL:'));
|
|
104
|
+
console.log(chalk.gray(' ') + chalk.cyan('sudo dnf install -y python3 python3-pip'));
|
|
105
|
+
if (pyReady) {
|
|
106
|
+
console.log();
|
|
107
|
+
console.log(chalk.green(' ✓ Python 3 already detected. You are ready.'));
|
|
108
|
+
}
|
|
109
|
+
console.log();
|
|
110
|
+
}
|
|
111
|
+
console.log(chalk.bold.white(' Then get a Paper B token from your organizer.'));
|
|
112
|
+
console.log(chalk.gray(' Each token is one-shot and bound to one device.'));
|
|
113
|
+
console.log();
|
|
114
|
+
console.log(chalk.gray(' Detailed step-by-step guide: ')
|
|
115
|
+
+ chalk.cyan.underline('https://icoa2026.au/selectionguide/'));
|
|
116
|
+
console.log();
|
|
117
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform detection for Windows cmd.exe K-12 entry path.
|
|
3
|
+
*
|
|
4
|
+
* Context: Windows middle-school students (12-14 y/o) are the expected largest
|
|
5
|
+
* cohort for ICOA 2026. Observed in-field: 4-5 students blocked by WSL install
|
|
6
|
+
* complexity. Their viable path is Windows native cmd + Node.js, which handles
|
|
7
|
+
* MCQ + AI chat perfectly but not Q31-36 (Unix grep / strings / pwntools).
|
|
8
|
+
*
|
|
9
|
+
* Decision (v2.19.97): C paper (ua-2026-c) is the cmd entry funnel — 30 MCQ
|
|
10
|
+
* only, 45 min, 70 pts, no tools. Non-cmd users get B paper (full 150 pts).
|
|
11
|
+
*
|
|
12
|
+
* This module is the single source of truth for "should we show exam setup?"
|
|
13
|
+
* and "should we route this user to C paper?". Used by src/repl.ts menu
|
|
14
|
+
* rendering and src/commands/exam.ts pre-token guards.
|
|
15
|
+
*/
|
|
16
|
+
export interface PlatformInfo {
|
|
17
|
+
platform: NodeJS.Platform;
|
|
18
|
+
isWindowsNativeCmd: boolean;
|
|
19
|
+
isInWSL: boolean;
|
|
20
|
+
hasPython: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Heuristic: true when running inside a WSL distribution. Node.js reports
|
|
24
|
+
* `process.platform === 'linux'` in that case, but WSL sets specific env vars.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isInWSL(): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* True when the user is on Windows native (cmd.exe or PowerShell), NOT a
|
|
29
|
+
* WSL distribution.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isNativeWindowsCmd(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Probe whether any Python 3 binary is reachable. Silent — no stderr leaks
|
|
34
|
+
* to the user. Used to decide whether Q31-35 practical questions are even
|
|
35
|
+
* theoretically doable on this machine.
|
|
36
|
+
*
|
|
37
|
+
* Uses execFileSync (no shell) so probe failures don't leak cmd.exe's
|
|
38
|
+
* "not recognized as internal or external command" messages to stdout.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hasPython(): boolean;
|
|
41
|
+
export declare function getPlatformInfo(): PlatformInfo;
|
|
42
|
+
/**
|
|
43
|
+
* Should the Selection-mode menu show the `exam setup` row?
|
|
44
|
+
*
|
|
45
|
+
* Windows native cmd: NO. Setup installs pip packages for Q31-35 practical
|
|
46
|
+
* which don't apply on the C-paper (cmd-recommended) path. Showing setup
|
|
47
|
+
* sends them chasing Python installs that may fail for 12-year-olds.
|
|
48
|
+
*
|
|
49
|
+
* Everyone else (macOS, Linux, WSL): YES. They can reach B-paper and setup
|
|
50
|
+
* is a legit pre-step.
|
|
51
|
+
*/
|
|
52
|
+
export declare function shouldShowExamSetup(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Is this user recommended to take the C paper (MCQ-only) rather than B?
|
|
55
|
+
* Currently: Windows native cmd users. Future: might expand based on age
|
|
56
|
+
* or partner-country policy.
|
|
57
|
+
*/
|
|
58
|
+
export declare function shouldRecommendCPaper(): boolean;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform detection for Windows cmd.exe K-12 entry path.
|
|
3
|
+
*
|
|
4
|
+
* Context: Windows middle-school students (12-14 y/o) are the expected largest
|
|
5
|
+
* cohort for ICOA 2026. Observed in-field: 4-5 students blocked by WSL install
|
|
6
|
+
* complexity. Their viable path is Windows native cmd + Node.js, which handles
|
|
7
|
+
* MCQ + AI chat perfectly but not Q31-36 (Unix grep / strings / pwntools).
|
|
8
|
+
*
|
|
9
|
+
* Decision (v2.19.97): C paper (ua-2026-c) is the cmd entry funnel — 30 MCQ
|
|
10
|
+
* only, 45 min, 70 pts, no tools. Non-cmd users get B paper (full 150 pts).
|
|
11
|
+
*
|
|
12
|
+
* This module is the single source of truth for "should we show exam setup?"
|
|
13
|
+
* and "should we route this user to C paper?". Used by src/repl.ts menu
|
|
14
|
+
* rendering and src/commands/exam.ts pre-token guards.
|
|
15
|
+
*/
|
|
16
|
+
import { execFileSync } from 'node:child_process';
|
|
17
|
+
/**
|
|
18
|
+
* Heuristic: true when running inside a WSL distribution. Node.js reports
|
|
19
|
+
* `process.platform === 'linux'` in that case, but WSL sets specific env vars.
|
|
20
|
+
*/
|
|
21
|
+
export function isInWSL() {
|
|
22
|
+
return !!(process.env.WSL_DISTRO_NAME || process.env.WSLENV);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* True when the user is on Windows native (cmd.exe or PowerShell), NOT a
|
|
26
|
+
* WSL distribution.
|
|
27
|
+
*/
|
|
28
|
+
export function isNativeWindowsCmd() {
|
|
29
|
+
return process.platform === 'win32' && !isInWSL();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Probe whether any Python 3 binary is reachable. Silent — no stderr leaks
|
|
33
|
+
* to the user. Used to decide whether Q31-35 practical questions are even
|
|
34
|
+
* theoretically doable on this machine.
|
|
35
|
+
*
|
|
36
|
+
* Uses execFileSync (no shell) so probe failures don't leak cmd.exe's
|
|
37
|
+
* "not recognized as internal or external command" messages to stdout.
|
|
38
|
+
*/
|
|
39
|
+
export function hasPython() {
|
|
40
|
+
const probes = [
|
|
41
|
+
['python3', ['--version']],
|
|
42
|
+
['python', ['--version']],
|
|
43
|
+
['py', ['-3', '--version']],
|
|
44
|
+
];
|
|
45
|
+
for (const [bin, args] of probes) {
|
|
46
|
+
try {
|
|
47
|
+
execFileSync(bin, args, {
|
|
48
|
+
encoding: 'utf-8',
|
|
49
|
+
timeout: 1500,
|
|
50
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
51
|
+
});
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch { /* try next */ }
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
export function getPlatformInfo() {
|
|
59
|
+
return {
|
|
60
|
+
platform: process.platform,
|
|
61
|
+
isWindowsNativeCmd: isNativeWindowsCmd(),
|
|
62
|
+
isInWSL: isInWSL(),
|
|
63
|
+
hasPython: hasPython(),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Should the Selection-mode menu show the `exam setup` row?
|
|
68
|
+
*
|
|
69
|
+
* Windows native cmd: NO. Setup installs pip packages for Q31-35 practical
|
|
70
|
+
* which don't apply on the C-paper (cmd-recommended) path. Showing setup
|
|
71
|
+
* sends them chasing Python installs that may fail for 12-year-olds.
|
|
72
|
+
*
|
|
73
|
+
* Everyone else (macOS, Linux, WSL): YES. They can reach B-paper and setup
|
|
74
|
+
* is a legit pre-step.
|
|
75
|
+
*/
|
|
76
|
+
export function shouldShowExamSetup() {
|
|
77
|
+
return !isNativeWindowsCmd();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Is this user recommended to take the C paper (MCQ-only) rather than B?
|
|
81
|
+
* Currently: Windows native cmd users. Future: might expand based on age
|
|
82
|
+
* or partner-country policy.
|
|
83
|
+
*/
|
|
84
|
+
export function shouldRecommendCPaper() {
|
|
85
|
+
return isNativeWindowsCmd();
|
|
86
|
+
}
|
package/dist/repl.js
CHANGED
|
@@ -10,6 +10,7 @@ import { getExamState, getRealExamState, getDemoState } from './lib/exam-state.j
|
|
|
10
10
|
import { getDemoStats } from './lib/demo-stats.js';
|
|
11
11
|
import { isExamSetupComplete } from './lib/exam-setup.js';
|
|
12
12
|
import { DEMO_PICK_SIZE, DEMO_POOL_SIZE } from './lib/demo-exam.js';
|
|
13
|
+
import { isNativeWindowsCmd } from './lib/platform.js';
|
|
13
14
|
import { resetTerminalTheme } from './lib/theme.js';
|
|
14
15
|
import { ensureSandbox, runInSandbox, isDockerAvailable } from './lib/sandbox.js';
|
|
15
16
|
import { logCommand } from './lib/logger.js';
|
|
@@ -149,13 +150,21 @@ function printSelectionMenu() {
|
|
|
149
150
|
const stats = getDemoStats();
|
|
150
151
|
const setupDone = isExamSetupComplete();
|
|
151
152
|
const demoLine = `Free practice — ${DEMO_PICK_SIZE} questions (from pool of ${DEMO_POOL_SIZE})`;
|
|
153
|
+
// v2.19.97 — Windows cmd K-12 entry path. Skip setup prompts + Python
|
|
154
|
+
// warnings for cmd users; they're routed to C paper (MCQ-only) which
|
|
155
|
+
// needs zero external tools.
|
|
156
|
+
const cmdMode = isNativeWindowsCmd();
|
|
152
157
|
console.log();
|
|
153
158
|
console.log(' ' + chalk.cyan.bold('[Selection Mode]'));
|
|
154
159
|
console.log();
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
if (cmdMode) {
|
|
161
|
+
console.log(chalk.gray(' Platform: ') + chalk.white('Windows cmd.exe') + chalk.gray(' — routed to Paper C (MCQ-only, 45 min, 70 pts, zero extra tools)'));
|
|
162
|
+
console.log();
|
|
163
|
+
}
|
|
164
|
+
else if (stats.attempts > 0) {
|
|
165
|
+
// Proactive Python check only matters for non-cmd users heading toward
|
|
166
|
+
// exam setup. Missing or version >= 3.13 shows yellow note pointing at
|
|
167
|
+
// `env python`. Quiet when 3.10-3.12 are installed.
|
|
159
168
|
const py = checkPython();
|
|
160
169
|
if (py.status === 'missing') {
|
|
161
170
|
console.log(chalk.yellow(' ⚠ Python not detected. For exam practical questions:'));
|
|
@@ -178,8 +187,9 @@ function printSelectionMenu() {
|
|
|
178
187
|
console.log(chalk.white(' lang es') + chalk.gray(' Switch language (e.g. lang es, lang zh, lang fr)'));
|
|
179
188
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
180
189
|
}
|
|
181
|
-
else if (!setupDone) {
|
|
190
|
+
else if (!setupDone && !cmdMode) {
|
|
182
191
|
// State 1: demo done at least once, but exam environment not installed.
|
|
192
|
+
// Windows cmd users skip this state entirely — they don't need setup.
|
|
183
193
|
const plural = stats.attempts === 1 ? 'attempt' : 'attempts';
|
|
184
194
|
console.log(chalk.green(' ✓ Demo completed ') + chalk.gray(`(${stats.attempts} ${plural}${stats.bestPercentage > 0 ? ` · best ${stats.bestPercentage}%` : ''})`));
|
|
185
195
|
console.log(chalk.yellow(' → Next: prepare your environment for the real exam.'));
|
|
@@ -192,13 +202,15 @@ function printSelectionMenu() {
|
|
|
192
202
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
193
203
|
}
|
|
194
204
|
else {
|
|
195
|
-
// State 2:
|
|
196
|
-
//
|
|
197
|
-
// already done demo; without a concrete example they stare at "<token>"
|
|
198
|
-
// and don't know what to type or where the token comes from.
|
|
205
|
+
// State 2: ready to enter exam. For cmd users this is reached immediately
|
|
206
|
+
// (no setup gate); for others it requires exam setup completion.
|
|
199
207
|
const plural = stats.attempts === 1 ? 'attempt' : 'attempts';
|
|
200
|
-
|
|
201
|
-
|
|
208
|
+
if (stats.attempts > 0) {
|
|
209
|
+
console.log(chalk.green(' ✓ Demo completed ') + chalk.gray(`(${stats.attempts} ${plural})`));
|
|
210
|
+
}
|
|
211
|
+
if (!cmdMode) {
|
|
212
|
+
console.log(chalk.green(' ✓ Environment ready'));
|
|
213
|
+
}
|
|
202
214
|
console.log(chalk.yellow(' → Enter your exam token to begin.'));
|
|
203
215
|
console.log(chalk.gray(' (10-char code from your organizer, starts with your country code like ') + chalk.cyan('UA') + chalk.gray(' — case-insensitive)'));
|
|
204
216
|
console.log();
|
|
@@ -208,7 +220,9 @@ function printSelectionMenu() {
|
|
|
208
220
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
209
221
|
console.log(chalk.gray(' Other commands:'));
|
|
210
222
|
console.log(chalk.white(' demo') + chalk.gray(` ${demoLine}`));
|
|
211
|
-
|
|
223
|
+
if (!cmdMode) {
|
|
224
|
+
console.log(chalk.white(' exam setup') + chalk.gray(' Re-verify tool environment'));
|
|
225
|
+
}
|
|
212
226
|
console.log(chalk.white(' lang') + chalk.gray(' List all supported languages'));
|
|
213
227
|
console.log(chalk.white(' lang es') + chalk.gray(' Switch language (e.g. lang es, lang zh, lang fr)'));
|
|
214
228
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|