icoa-cli 2.19.97 → 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.
@@ -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();
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.97",
3
+ "version": "2.19.98",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {