icoa-cli 2.19.100 โ†’ 2.19.102

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.
Files changed (45) hide show
  1. package/dist/commands/ai4ctf.js +1 -700
  2. package/dist/commands/connect.js +1 -66
  3. package/dist/commands/ctf.js +1 -620
  4. package/dist/commands/ctf4ai-demo.js +1 -525
  5. package/dist/commands/env.js +1 -738
  6. package/dist/commands/exam.js +1 -2353
  7. package/dist/commands/files.js +1 -52
  8. package/dist/commands/hint.js +1 -119
  9. package/dist/commands/lang.js +1 -155
  10. package/dist/commands/log.js +1 -165
  11. package/dist/commands/note.js +1 -40
  12. package/dist/commands/ref.js +1 -68
  13. package/dist/commands/setup.js +1 -122
  14. package/dist/commands/shell.js +1 -55
  15. package/dist/commands/theme.js +1 -50
  16. package/dist/index.js +1 -225
  17. package/dist/lib/access.js +1 -246
  18. package/dist/lib/budget.js +1 -42
  19. package/dist/lib/colors.js +1 -21
  20. package/dist/lib/config.js +1 -60
  21. package/dist/lib/ctfd-client.js +1 -274
  22. package/dist/lib/demo-exam.js +1 -249
  23. package/dist/lib/demo-flags.js +1 -27
  24. package/dist/lib/demo-stats.js +1 -65
  25. package/dist/lib/exam-client.js +1 -57
  26. package/dist/lib/exam-setup.js +1 -23
  27. package/dist/lib/exam-state.js +1 -112
  28. package/dist/lib/gemini.js +1 -235
  29. package/dist/lib/i18n.js +1 -273
  30. package/dist/lib/log-sync.js +1 -110
  31. package/dist/lib/logger.js +1 -59
  32. package/dist/lib/paper-upgrade.js +1 -117
  33. package/dist/lib/platform.js +1 -86
  34. package/dist/lib/sandbox.js +1 -93
  35. package/dist/lib/terminal.js +1 -49
  36. package/dist/lib/theme.js +1 -108
  37. package/dist/lib/translation.js +1 -66
  38. package/dist/lib/ui.js +1 -80
  39. package/dist/lib/update-check.js +1 -102
  40. package/dist/postinstall.js +1 -48
  41. package/dist/repl.js +1 -1281
  42. package/dist/types/index.d.ts +1 -1
  43. package/dist/types/index.js +1 -38
  44. package/package.json +6 -2
  45. package/translations/sw/i18n-snippet.ts +1 -0
@@ -1,2353 +1 @@
1
- import chalk from 'chalk';
2
- import { confirm } from '@inquirer/prompts';
3
- import { ExamClient } from '../lib/exam-client.js';
4
- import { getConfig, saveConfig } from '../lib/config.js';
5
- import { getExamState, saveExamState, clearExamState, getExamDeadline } from '../lib/exam-state.js';
6
- import { getDemoStats, recordDemoAttempt, saveRetryQueue, getRetryQueue, clearRetryQueue } from '../lib/demo-stats.js';
7
- import { logCommand } from '../lib/logger.js';
8
- import { printSuccess, printError, printWarning, printInfo, printTable, printHeader, printKeyValue, createSpinner, formatCountdown, } from '../lib/ui.js';
9
- import { t } from '../lib/i18n.js';
10
- import { getDeviceFingerprint } from '../lib/access.js';
11
- import { getExamSetup, saveExamSetup, isExamSetupComplete } from '../lib/exam-setup.js';
12
- import { execSync } from 'node:child_process';
13
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
14
- // Fire-and-forget event POST to /api/icoa/demo-stats. Used for demo-flow
15
- // choice points (demo enter, retry, back) where we want a server-side
16
- // timestamp of the user's decision but have nothing more to report.
17
- function reportDemoEvent(type, extra = {}) {
18
- const config = getConfig();
19
- fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
20
- method: 'POST',
21
- headers: { 'Content-Type': 'application/json' },
22
- body: JSON.stringify({
23
- type,
24
- lang: config.language || 'en',
25
- timestamp: new Date().toISOString(),
26
- ...extra,
27
- }),
28
- signal: AbortSignal.timeout(5000),
29
- }).catch(() => { });
30
- }
31
- /**
32
- * Skippable intro animation for first-time demo users.
33
- * Resolves immediately on any keypress. Max 30s auto-advance.
34
- */
35
- async function playDemoIntro() {
36
- let skipped = false;
37
- const stdin = process.stdin;
38
- const onKey = () => { skipped = true; };
39
- const rawSupported = stdin.isTTY && typeof stdin.setRawMode === 'function';
40
- if (rawSupported) {
41
- stdin.setRawMode(true);
42
- stdin.resume();
43
- stdin.once('data', onKey);
44
- }
45
- const waitOrSkip = async (ms) => {
46
- const step = 80;
47
- let waited = 0;
48
- while (waited < ms && !skipped) {
49
- await sleep(step);
50
- waited += step;
51
- }
52
- };
53
- const cleanup = () => {
54
- if (rawSupported) {
55
- stdin.removeListener('data', onKey);
56
- try {
57
- stdin.setRawMode(false);
58
- }
59
- catch { }
60
- stdin.pause();
61
- }
62
- };
63
- try {
64
- console.clear();
65
- console.log();
66
- console.log(chalk.gray(' ') + chalk.dim('(press any key to skip)'));
67
- console.log();
68
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—'));
69
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—'));
70
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘'));
71
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘'));
72
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘'));
73
- console.log(chalk.bold.white(' โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ•'));
74
- console.log();
75
- console.log(chalk.bold.yellow(' International Cyber Olympiad in AI'));
76
- console.log(chalk.gray(' Sydney, Australia ยท Jun 27 โ€“ Jul 2, 2026'));
77
- await waitOrSkip(2500);
78
- if (skipped)
79
- return;
80
- console.log();
81
- console.log(chalk.white(' What you\'ll do today:'));
82
- console.log();
83
- await waitOrSkip(600);
84
- if (skipped)
85
- return;
86
- console.log(chalk.cyan(' โ–ฃ ') + chalk.white('Answer 10 quick questions') + chalk.gray(' (now, ~5 min)'));
87
- await waitOrSkip(700);
88
- if (skipped)
89
- return;
90
- console.log(chalk.green(' โ–ฃ ') + chalk.white('Team up with AI to solve CTFs') + chalk.gray(' (ai4ctf)'));
91
- await waitOrSkip(700);
92
- if (skipped)
93
- return;
94
- console.log(chalk.red(' โ–ฃ ') + chalk.white('Hack the AI\'s guardrails') + chalk.gray(' (ctf4ai)'));
95
- await waitOrSkip(900);
96
- if (skipped)
97
- return;
98
- console.log();
99
- console.log(chalk.bold.white(' Your terminal ') + chalk.bold.yellow('IS') + chalk.bold.white(' the competition.'));
100
- console.log();
101
- await waitOrSkip(1500);
102
- if (skipped)
103
- return;
104
- console.log(chalk.gray(' ยท 110 CTF tools pre-configured'));
105
- await waitOrSkip(400);
106
- if (skipped)
107
- return;
108
- console.log(chalk.gray(' ยท 15 languages, real-time AI translation'));
109
- await waitOrSkip(400);
110
- if (skipped)
111
- return;
112
- console.log(chalk.gray(' ยท 15,000+ concurrent participants'));
113
- await waitOrSkip(800);
114
- if (skipped)
115
- return;
116
- console.log();
117
- console.log(chalk.bold.cyan(' Press any key to start demo...'));
118
- await waitOrSkip(4000);
119
- }
120
- finally {
121
- cleanup();
122
- console.clear();
123
- }
124
- }
125
- function drawProgress(percent, label) {
126
- const width = 30;
127
- const filled = Math.round((percent / 100) * width);
128
- const empty = width - filled;
129
- const bar = chalk.green('โ–ˆ'.repeat(filled)) + chalk.gray('โ–‘'.repeat(empty));
130
- const pct = String(percent).padStart(3) + '%';
131
- process.stdout.write(`\r\x1b[2K ${bar} ${pct} ${chalk.gray(label)}`);
132
- }
133
- function requireExamConnection() {
134
- const config = getConfig();
135
- if (!config.ctfdUrl || !config.token) {
136
- printError('Not connected. Run: join <url>');
137
- return null;
138
- }
139
- return new ExamClient(config.ctfdUrl, config.token);
140
- }
141
- function checkTimeRemaining() {
142
- const deadline = getExamDeadline();
143
- if (!deadline)
144
- return true;
145
- if (new Date() >= deadline) {
146
- const state = getExamState();
147
- if (state) {
148
- const answered = Object.keys(state.answers).length;
149
- if (state.session.examId === 'demo-free' || answered === 0) {
150
- // Demo or unanswered expired exam โ€” auto-clear to avoid deadlock
151
- clearExamState();
152
- printWarning('Exam expired and cleared.');
153
- printInfo('Type: demo to start again.');
154
- }
155
- else {
156
- printError('Time expired! Use "exam submit" to submit your answers.');
157
- }
158
- }
159
- return false;
160
- }
161
- return true;
162
- }
163
- function printTimeRemaining() {
164
- const deadline = getExamDeadline();
165
- if (deadline) {
166
- const now = new Date();
167
- if (now < deadline) {
168
- printKeyValue('Time Remaining', chalk.yellow.bold(formatCountdown(deadline)));
169
- }
170
- else {
171
- printKeyValue('Time', chalk.red.bold('EXPIRED'));
172
- }
173
- }
174
- }
175
- // One-shot urgent warnings: show the first time we cross a threshold, then
176
- // never again in the same session. Tracked on ExamState so it survives REPL
177
- // restarts and resume. Called from printQuestion so it fires naturally as the
178
- // user navigates (no background timer needed).
179
- function printTimeWarningIfNeeded(state) {
180
- const deadline = getExamDeadline();
181
- if (!deadline)
182
- return;
183
- const remainingMs = deadline.getTime() - Date.now();
184
- if (remainingMs <= 0)
185
- return;
186
- const minutes = remainingMs / 60000;
187
- const shown = new Set(state.shownWarnings || []);
188
- let warn = null;
189
- if (minutes <= 1 && !shown.has('t1')) {
190
- warn = {
191
- key: 't1',
192
- text: [
193
- chalk.red.bold(' โš  LESS THAN 1 MINUTE LEFT'),
194
- chalk.red(' Submit now with: ') + chalk.bold('exam submit'),
195
- ],
196
- };
197
- }
198
- else if (minutes <= 5 && !shown.has('t5')) {
199
- warn = {
200
- key: 't5',
201
- text: [
202
- chalk.red.bold(' โš  5 minutes remaining'),
203
- chalk.yellow(' Wrap up and submit: ') + chalk.bold('exam submit'),
204
- ],
205
- };
206
- }
207
- else if (minutes <= 10 && !shown.has('t10')) {
208
- warn = {
209
- key: 't10',
210
- text: [
211
- chalk.yellow.bold(' โฐ 10 minutes remaining'),
212
- chalk.gray(' Review unanswered questions: ') + chalk.white('exam review'),
213
- ],
214
- };
215
- }
216
- if (!warn)
217
- return;
218
- console.log();
219
- console.log(chalk.red(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
220
- for (const line of warn.text)
221
- console.log(line);
222
- console.log(chalk.red(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
223
- shown.add(warn.key);
224
- state.shownWarnings = Array.from(shown);
225
- saveExamState(state);
226
- }
227
- // Pacing hints at Q10 (time check) and Q30 (section complete). Real exam only.
228
- // Keyed by question number so they fire once per exam on any render.
229
- function printPacingHint(state, currentQ) {
230
- if (state.session.examId === 'demo-free')
231
- return;
232
- const shown = new Set(state.shownWarnings || []);
233
- const total = Number(state.session.questionCount || 40);
234
- const deadline = getExamDeadline();
235
- // Q10: gentle time check (only if exam is 40+ questions, not 10 demo)
236
- if (currentQ === 10 && total >= 40 && !shown.has('p10')) {
237
- const remainingMin = deadline ? Math.max(0, Math.round((deadline.getTime() - Date.now()) / 60000)) : null;
238
- console.log();
239
- console.log(chalk.gray(' โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„'));
240
- console.log(chalk.cyan(' โฑ Time check ') + chalk.gray(`โ€” you're 1/4 through the exam`));
241
- if (remainingMin !== null) {
242
- console.log(chalk.gray(` ${remainingMin} minutes left. Keep the pace steady.`));
243
- }
244
- console.log(chalk.gray(' โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„'));
245
- shown.add('p10');
246
- state.shownWarnings = Array.from(shown);
247
- saveExamState(state);
248
- }
249
- // Q30: MCQ section complete milestone
250
- if (currentQ === 30 && total >= 40 && !shown.has('p30')) {
251
- const answered = Object.keys(state.answers || {}).length;
252
- const mcqAnswered = Object.keys(state.answers || {}).filter((n) => Number(n) <= 30).length;
253
- console.log();
254
- console.log(chalk.green(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
255
- console.log(chalk.bold.green(' โœ“ MCQ section complete โ€” well done!'));
256
- console.log(chalk.gray(` ${mcqAnswered}/30 multiple-choice answered`));
257
- console.log(chalk.white(' โ†“ Practical section begins at Q31.'));
258
- console.log(chalk.gray(' Budget ~2 min per practical question. You can come back with: next / prev.'));
259
- console.log(chalk.green(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
260
- shown.add('p30');
261
- state.shownWarnings = Array.from(shown);
262
- saveExamState(state);
263
- }
264
- }
265
- // Bookmark / flag-for-review helpers
266
- function isBookmarked(state, n) {
267
- return Array.isArray(state.bookmarks) && state.bookmarks.includes(n);
268
- }
269
- function toggleBookmark(state, n) {
270
- const arr = Array.isArray(state.bookmarks) ? [...state.bookmarks] : [];
271
- const idx = arr.indexOf(n);
272
- if (idx >= 0) {
273
- arr.splice(idx, 1);
274
- state.bookmarks = arr;
275
- saveExamState(state);
276
- return false;
277
- }
278
- arr.push(n);
279
- arr.sort((a, b) => a - b);
280
- state.bookmarks = arr;
281
- saveExamState(state);
282
- return true;
283
- }
284
- function printHowToPlay() {
285
- console.log(chalk.gray(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
286
- console.log(chalk.white(` ${t('howToPlay')}`));
287
- console.log(chalk.yellow(' A/B/C/D') + chalk.gray(` ${t('htpAnswer')}`));
288
- console.log(chalk.yellow(' help') + chalk.gray(` ${t('htpHelp')}`));
289
- console.log(chalk.yellow(' next') + chalk.gray(' / ') + chalk.yellow('prev') + chalk.gray(` ${t('htpNav')}`));
290
- console.log(chalk.yellow(' more help') + chalk.gray(` ${t('htpMoreHelp')}`));
291
- console.log(chalk.yellow(' back') + chalk.gray(` ${t('htpBack')}`));
292
- console.log(chalk.yellow(' lang') + chalk.gray(` ${t('htpLang')}`));
293
- console.log(chalk.gray(' es ยท zh ยท ja ยท ko ยท ar ยท fr ยท pt'));
294
- console.log(chalk.gray(' ru ยท hi ยท de ยท id ยท th ยท vi ยท tr'));
295
- console.log(chalk.gray(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
296
- }
297
- // Australian easter eggs at percentage-based positions. The messages were
298
- // originally written for a 15-question exam; we now map them by percentage
299
- // so they fire at the right moment regardless of question count (10 or 15).
300
- function getEasterEgg(current, total) {
301
- const EGGS = [
302
- { pct: 0.20, emoji: '๐Ÿ›๏ธ', key: 'egg3' }, // Great start
303
- { pct: 0.33, emoji: '๐Ÿจ', key: 'egg5' }, // 1/3 done
304
- { pct: 0.50, emoji: '๐ŸŒ‰', key: 'egg7' }, // Keep going (halfway)
305
- { pct: 0.60, emoji: '๐Ÿฆ˜', key: 'egg9' }, // Past halfway
306
- { pct: 0.80, emoji: '๐Ÿ–๏ธ', key: 'egg11' }, // Almost there
307
- { pct: 0.87, emoji: '๐Ÿฆˆ', key: 'egg13' }, // 2 more to go
308
- { pct: 1.00, emoji: '๐ŸŽ‰', key: 'egg15' }, // All done
309
- ];
310
- const seen = new Set();
311
- for (const egg of EGGS) {
312
- const targetQ = Math.round(egg.pct * total);
313
- if (targetQ < 1 || seen.has(targetQ))
314
- continue;
315
- seen.add(targetQ);
316
- if (current === targetQ)
317
- return { emoji: egg.emoji, text: t(egg.key) };
318
- }
319
- return undefined;
320
- }
321
- function printQuestionProgress(current, total, answered) {
322
- const width = 30;
323
- const filled = Math.round((current / total) * width);
324
- const empty = width - filled;
325
- const bar = chalk.cyan('โ”'.repeat(filled)) + chalk.gray('โ”€'.repeat(empty));
326
- const pct = Math.round((current / total) * 100);
327
- console.log();
328
- console.log(` ${bar} ${chalk.white.bold(`${current}`)}${chalk.gray(`/${total}`)} ${chalk.gray(`(${answered} answered)`)} ${chalk.gray(`${pct}%`)}`);
329
- // Time remaining โ€” colour-coded countdown so contestants always see where
330
- // they stand. Authoritative clock lives on the server (confirmedAt +
331
- // duration enforced at submit time); this is a display helper.
332
- const deadline = getExamDeadline();
333
- if (deadline) {
334
- const remainingSec = Math.max(0, Math.round((deadline.getTime() - Date.now()) / 1000));
335
- const mm = Math.floor(remainingSec / 60);
336
- const ss = remainingSec % 60;
337
- const timeStr = `${mm}:${String(ss).padStart(2, '0')}`;
338
- const color = remainingSec <= 60 ? chalk.red.bold
339
- : remainingSec <= 300 ? chalk.red
340
- : remainingSec <= 600 ? chalk.yellow
341
- : chalk.gray;
342
- console.log(` ${chalk.gray('โฑ Time remaining:')} ${color(timeStr)} ${chalk.gray('(server-authoritative)')}`);
343
- }
344
- }
345
- // Help budget per exam.md ยง3: 20 base + 10 hidden bonus (unlocked via `more help`).
346
- // Demo still uses the lighter 5 + 3 set via _helpMax overrides at demo start.
347
- const HELP_BASE = 20;
348
- const HELP_WITH_BONUS = 30;
349
- function getHelpState(state) {
350
- return {
351
- used: state._helpUsed || 0,
352
- max: state._helpMax || HELP_BASE,
353
- perQ: state._helpPerQ || {},
354
- eliminated: state._eliminated || {},
355
- };
356
- }
357
- // Section intros fire once per REPL session, first time the user enters
358
- // that section. State-persisted via shownWarnings keys 's_ai4ctf' / 's_ctf4ai'
359
- // so jumping back and forth doesn't re-fire them.
360
- function printSectionIntro(state, currentQ) {
361
- if (state.session.examId === 'demo-free')
362
- return;
363
- const total = Number(state.session.questionCount || 40);
364
- // Only 40-question exams have the three-section structure
365
- if (total < 40)
366
- return;
367
- const shown = new Set(state.shownWarnings || []);
368
- // โ”€โ”€โ”€ AI4CTF section intro (Q31 = first practical, AI-as-teammate) โ”€โ”€โ”€
369
- if (currentQ >= 31 && currentQ <= 38 && !shown.has('s_ai4ctf')) {
370
- console.log();
371
- console.log(chalk.green(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
372
- console.log(chalk.bold.green(' ๐Ÿš€ Section 2: AI4CTF โ€” AI is your teammate'));
373
- console.log(chalk.green(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
374
- console.log();
375
- console.log(chalk.white(' Welcome to the practical CTF section. Real'));
376
- console.log(chalk.white(' security tasks โ€” crypto, web, forensics, pwn.'));
377
- console.log();
378
- console.log(chalk.bold.white(' Q31โ€“38 (8 questions ยท 6 pts each)'));
379
- console.log(chalk.gray(' Solve ') + chalk.bold('with') + chalk.gray(' AI by your side. AI is your teammate.'));
380
- console.log();
381
- console.log(chalk.bold.white(' How to work'));
382
- console.log(chalk.gray(' Enter chat: ') + chalk.bold.green('ai4ctf') + chalk.gray(' โ†’ ') + chalk.magenta('ai4ctf>') + chalk.gray(' prompt, just like the demo'));
383
- console.log(chalk.gray(' Inside chat: ') + chalk.cyan('hint a / b / c') + chalk.gray(' ยท ') + chalk.cyan('submit ICOA{...}') + chalk.gray(' ยท ') + chalk.cyan('!python3 ...'));
384
- console.log(chalk.gray(' Free chat: any message โ†’ AI teammate'));
385
- console.log(chalk.gray(' Exit chat: ') + chalk.cyan('exit') + chalk.gray(' โ†’ back to exam, navigate with ') + chalk.cyan('next / prev'));
386
- console.log();
387
- console.log(chalk.bold.white(' Budget (shared across Q31โ€“38)'));
388
- console.log(chalk.gray(' AI tokens: ') + chalk.white('25,000') + chalk.gray(' for this section'));
389
- console.log(chalk.gray(' Hints A/B/C: ') + chalk.white('pre-written per question'));
390
- console.log();
391
- console.log(chalk.yellow(' Time still counting down. Budget ~2 min per question.'));
392
- console.log(chalk.green(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
393
- console.log();
394
- shown.add('s_ai4ctf');
395
- state.shownWarnings = Array.from(shown);
396
- saveExamState(state);
397
- return;
398
- }
399
- // โ”€โ”€โ”€ CTF4AI section intro (Q39 = adversarial AI, highest-value questions) โ”€โ”€โ”€
400
- if (currentQ >= 39 && !shown.has('s_ctf4ai')) {
401
- console.log();
402
- console.log(chalk.red(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
403
- console.log(chalk.bold.red(' ๐ŸŽฏ Section 3: CTF4AI โ€” Challenge the AI'));
404
- console.log(chalk.red(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
405
- console.log();
406
- console.log(chalk.white(' The final section flips the script: instead of'));
407
- console.log(chalk.white(' using AI, you\'re ') + chalk.bold('attacking') + chalk.white(' AI systems.'));
408
- console.log();
409
- console.log(chalk.bold.white(' Q39โ€“40 (2 questions ยท 16 pts each โ€” highest value!)'));
410
- console.log(chalk.gray(' Prompt injection ยท adversarial analysis ยท AI auditing'));
411
- console.log();
412
- console.log(chalk.bold.white(' How to attack'));
413
- console.log(chalk.gray(' Enter chat: ') + chalk.bold.red('ctf4ai') + chalk.gray(' โ†’ ') + chalk.red('ctf4ai>') + chalk.gray(' prompt, same shape as demo'));
414
- console.log(chalk.gray(' Inside chat: ') + chalk.cyan('hint a / b / c') + chalk.gray(' ยท ') + chalk.cyan('submit ICOA{...}') + chalk.gray(' ยท ') + chalk.cyan('!python3 ...'));
415
- console.log(chalk.gray(' Messages โ†’ AI target โ€” craft prompts to break its rules.'));
416
- console.log();
417
- console.log(chalk.bold.white(' Key differences from AI4CTF'));
418
- console.log(chalk.gray(' ยท AI is your ') + chalk.red('target') + chalk.gray(', not your teammate'));
419
- console.log(chalk.gray(' ยท Separate budget: ') + chalk.white('25,000 tokens') + chalk.gray(' shared across Q39โ€“40'));
420
- console.log();
421
- console.log(chalk.yellow(' These are the hardest questions. Worth 21% of total score.'));
422
- console.log(chalk.yellow(' If time is tight, skim Q39/40 first to decide attack order.'));
423
- console.log(chalk.red(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
424
- console.log();
425
- shown.add('s_ctf4ai');
426
- state.shownWarnings = Array.from(shown);
427
- saveExamState(state);
428
- return;
429
- }
430
- }
431
- function printQuestion(q, answer) {
432
- const state = getExamState();
433
- const total = Number(state?.session.questionCount || 30);
434
- const answered = Object.keys(state?.answers || {}).length;
435
- const help = getHelpState(state);
436
- const eliminated = help.eliminated[q.number] || [];
437
- const isPractical = q.type === 'ai4ctf' || q.type === 'ctf4ai' || (q.options && !q.options.A && !q.options.B);
438
- // Pacing hints (Q10 time check, Q30 section complete) โ€” real exam only
439
- if (state)
440
- printPacingHint(state, q.number);
441
- // Section intros (AI4CTF at Q31, CTF4AI at Q39) โ€” real exam only
442
- if (state && isPractical)
443
- printSectionIntro(state, q.number);
444
- // Urgent countdown warnings (10 / 5 / 1 minutes remaining)
445
- if (state)
446
- printTimeWarningIfNeeded(state);
447
- // Progress bar
448
- printQuestionProgress(q.number, total, answered);
449
- // Bookmark indicator โ€” yellow โ˜… after the progress line if current q is marked
450
- if (state && isBookmarked(state, q.number)) {
451
- console.log(chalk.yellow(' โ˜… Marked for review'));
452
- }
453
- // Easter egg (position calculated by percentage of total)
454
- const egg = getEasterEgg(q.number, total);
455
- if (egg) {
456
- console.log(chalk.yellow(` ${egg.emoji} ${egg.text}`));
457
- }
458
- console.log();
459
- if (q.category)
460
- console.log(chalk.cyan(` [${q.category}]`));
461
- if (q.points && q.points > 2) {
462
- console.log(chalk.cyan(` [${q.points} points]`));
463
- }
464
- if (isPractical) {
465
- // Practical question display
466
- const displayText = q.description || q.text;
467
- console.log(chalk.bold.white(` Q${q.number}. `) + chalk.white(displayText.split('\n')[0]));
468
- // Show remaining lines with proper indentation
469
- const lines = displayText.split('\n').slice(1);
470
- for (const line of lines) {
471
- console.log(chalk.white(` ${line}`));
472
- }
473
- console.log();
474
- if (answer) {
475
- console.log(chalk.green(` Your answer: ${answer}`));
476
- console.log();
477
- }
478
- }
479
- else {
480
- // MCQ display
481
- console.log(chalk.bold.white(` Q${q.number}. `) + chalk.white(q.text));
482
- console.log();
483
- for (const key of ['A', 'B', 'C', 'D']) {
484
- const isEliminated = eliminated.includes(key);
485
- const selected = answer === key;
486
- if (isEliminated) {
487
- console.log(chalk.gray.strikethrough(` ${key}. ${q.options[key]}`) + chalk.red(` (${t('wrong')})`));
488
- }
489
- else if (selected) {
490
- console.log(chalk.green.bold(` โ–ธ ${key}. ${q.options[key]}`));
491
- }
492
- else {
493
- console.log(chalk.gray(` ${key}.`) + ' ' + chalk.white(q.options[key]));
494
- }
495
- }
496
- console.log();
497
- }
498
- // Q2 tutorial hint
499
- const isDemo = state?.session.examId === 'demo-free';
500
- const q2Helps = help.perQ[2] || 0;
501
- if (isDemo && q.number === 2 && q2Helps === 0) {
502
- console.log(chalk.yellow.bold(` ${t('qTutorial')}`));
503
- console.log();
504
- }
505
- // Full menu
506
- const remaining = help.max - help.used;
507
- console.log(chalk.gray(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
508
- if (isPractical) {
509
- const isCtf4aiQ = state && state.session.examId !== 'demo-free' && q.number >= 39;
510
- const isAi4ctfExamQ = state && state.session.examId !== 'demo-free' && q.number >= 31 && q.number <= 38;
511
- if (isAi4ctfExamQ) {
512
- console.log(chalk.bold.green(' ai4ctf') + chalk.gray(' enter AI4CTF chat for this question (recommended)'));
513
- }
514
- else if (isCtf4aiQ) {
515
- console.log(chalk.bold.red(' ctf4ai') + chalk.gray(' enter CTF4AI chat โ€” attack the AI target (recommended)'));
516
- }
517
- console.log(chalk.yellow(' exam answer <n> ICOA{...}') + chalk.gray(' submit flag directly'));
518
- console.log(chalk.yellow(' !python3') + chalk.gray(' start Python shell'));
519
- }
520
- else {
521
- // "used up" prompt shows until the bonus tier is reached; after that
522
- // (already unlocked), show the full-used final state.
523
- const bonusUnlocked = help.max >= HELP_WITH_BONUS;
524
- const helpLabel = remaining > 0
525
- ? chalk.gray(`${t('helpRemove')} `) + chalk.yellow(`(${remaining}/${help.max})`)
526
- : (!bonusUnlocked ? chalk.gray(`${t('helpUsedUp')}`) : chalk.gray(`${t('helpAllUsed')} (${help.used}/${help.max})`));
527
- console.log(chalk.yellow(' A/B/C/D') + chalk.gray(` ${t('answerThis')}`));
528
- console.log(chalk.yellow(' help') + ' ' + helpLabel);
529
- }
530
- console.log(chalk.yellow(' next') + chalk.gray(' / ') + chalk.yellow('prev') + chalk.gray(` ${t('htpNav')}`));
531
- console.log(chalk.yellow(` exam q 1..${total}`) + chalk.gray(` ${t('htpJump')}`));
532
- console.log(chalk.yellow(' mark') + chalk.gray(` ${t('htpMark')}`));
533
- console.log(chalk.yellow(' exam review') + chalk.gray(` ${t('htpReview')}`));
534
- console.log(chalk.bold.yellow(' exam submit') + chalk.gray(` ${t('htpSubmit')}`));
535
- console.log(chalk.yellow(' back') + chalk.gray(` ${t('htpBack')}`));
536
- console.log(chalk.yellow(' lang') + chalk.gray(` ${t('htpLang')}`));
537
- console.log(chalk.gray(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
538
- }
539
- export function registerExamCommand(program) {
540
- const exam = program.command('exam').description('National selection exam');
541
- // โ”€โ”€โ”€ exam nations โ”€โ”€โ”€
542
- exam
543
- .command('nations')
544
- .description('View all participating countries')
545
- .action(() => {
546
- logCommand('exam nations');
547
- console.log();
548
- printHeader('ICOA Participating Countries and Regions');
549
- console.log();
550
- const nations = [
551
- ['AU', 'Australia'], ['BR', 'Brazil'], ['CN', 'China'],
552
- ['DE', 'Germany'], ['EG', 'Egypt'], ['FR', 'France'],
553
- ['ID', 'Indonesia'], ['IN', 'India'], ['JP', 'Japan'],
554
- ['KR', 'South Korea'], ['MY', 'Malaysia'], ['PE', 'Peru'],
555
- ['PH', 'Philippines'], ['RU', 'Russia'], ['SA', 'Saudi Arabia'],
556
- ['SG', 'Singapore'], ['TH', 'Thailand'], ['TR', 'Turkey'],
557
- ['VN', 'Vietnam'], ['ZA', 'South Africa'],
558
- ];
559
- for (let i = 0; i < nations.length; i += 3) {
560
- const row = nations.slice(i, i + 3)
561
- .map(([code, name]) => ` ${chalk.cyan(code)} ${chalk.gray(name.padEnd(16))}`)
562
- .join('');
563
- console.log(row);
564
- }
565
- console.log();
566
- console.log(chalk.gray(' 40+ countries and regions represented'));
567
- console.log(chalk.gray(' New member? Contact: accreditation@icoa2026.au'));
568
- console.log();
569
- console.log(chalk.white(' Check exams for your country:'));
570
- console.log(chalk.cyan(' exam list AU') + chalk.gray(' exam list PE') + chalk.gray(' exam list CN'));
571
- console.log();
572
- });
573
- // โ”€โ”€โ”€ exam list [country] โ”€โ”€โ”€
574
- exam
575
- .command('list [country]')
576
- .description('List available exams (optional: 2-letter country code)')
577
- .action(async (country) => {
578
- logCommand(`exam list ${country || ''}`);
579
- const client = requireExamConnection();
580
- if (!client)
581
- return;
582
- const spinner = createSpinner('Loading exams...');
583
- spinner.start();
584
- try {
585
- let exams = await client.getExams();
586
- spinner.stop();
587
- // Filter by country if specified
588
- if (country) {
589
- const code = country.toUpperCase();
590
- exams = exams.filter((e) => e.country.toUpperCase() === code || e.country === 'ALL');
591
- }
592
- if (!exams || exams.length === 0) {
593
- console.log();
594
- if (country) {
595
- printInfo(`No exams available for ${country.toUpperCase()} yet.`);
596
- }
597
- else {
598
- printInfo('No exams available for your country yet.');
599
- }
600
- console.log();
601
- console.log(chalk.white(' New exams are coming soon!'));
602
- console.log(chalk.gray(' National Round 1 Selection exams are being prepared.'));
603
- console.log();
604
- console.log(chalk.gray(' In the meantime, try: ') + chalk.bold.cyan('demo'));
605
- console.log();
606
- const langHint = {
607
- PE: 'es', CN: 'zh', JP: 'ja', KR: 'ko', BR: 'pt',
608
- SA: 'ar', FR: 'fr', DE: 'de', IN: 'hi', ID: 'id',
609
- TH: 'th', VN: 'vi', TR: 'tr', RU: 'ru', UA: 'uk', HT: 'ht',
610
- };
611
- const code = (country || '').toUpperCase();
612
- if (langHint[code]) {
613
- console.log(chalk.gray(` Tip: switch to your language with: lang ${langHint[code]}`));
614
- console.log();
615
- }
616
- return;
617
- }
618
- const rows = exams.map((e) => {
619
- const statusColor = e.status === 'submitted' ? chalk.green
620
- : e.status === 'in_progress' ? chalk.yellow
621
- : chalk.white;
622
- return [
623
- chalk.white(e.id),
624
- e.name,
625
- chalk.cyan(e.country),
626
- String(e.questionCount),
627
- `${e.durationMinutes} min`,
628
- statusColor(e.status),
629
- ];
630
- });
631
- const countries = [...new Set(exams.map((e) => e.country))];
632
- printHeader(country ? `Exams โ€” ${country.toUpperCase()}` : `Available Exams (${countries.length} regions)`);
633
- printTable(['ID', 'Name', 'Country', 'Questions', 'Duration', 'Status'], rows);
634
- console.log();
635
- console.log(chalk.gray(' Start: exam start <id>'));
636
- if (countries.length > 1) {
637
- console.log(chalk.gray(` Filter by country: exam list PE, exam list CN, ...`));
638
- }
639
- }
640
- catch (err) {
641
- spinner.fail('Failed to load exams');
642
- printError(err.message);
643
- }
644
- });
645
- // โ”€โ”€โ”€ exam start <id> โ”€โ”€โ”€
646
- exam
647
- .command('start <id>')
648
- .description('Start an exam (begins timer)')
649
- .action(async (examId) => {
650
- logCommand(`exam start ${examId}`);
651
- // Check if already in an exam
652
- const existing = getExamState();
653
- if (existing) {
654
- printWarning(`Exam "${existing.session.examName}" is already in progress.`);
655
- printInfo('Use "exam review" to check progress or "exam submit" to submit.');
656
- return;
657
- }
658
- const client = requireExamConnection();
659
- if (!client)
660
- return;
661
- const proceed = await confirm({
662
- message: chalk.white('Start the exam? The timer will begin immediately.'),
663
- default: true,
664
- theme: { prefix: '', style: { message: (t) => t, defaultAnswer: (t) => chalk.green(t) } },
665
- });
666
- if (!proceed)
667
- return;
668
- console.log();
669
- try {
670
- // Step 1: Connect
671
- drawProgress(0, 'Connecting to exam server...');
672
- const { session, questions } = await client.startExam(examId);
673
- // Step 2-4: Visual loading steps
674
- drawProgress(30, 'Loading questions...');
675
- await sleep(200);
676
- drawProgress(60, 'Preparing exam environment...');
677
- await sleep(200);
678
- drawProgress(90, 'Starting timer...');
679
- await sleep(150);
680
- saveExamState({ session, questions, answers: {} });
681
- drawProgress(100, 'Ready!');
682
- console.log();
683
- console.log();
684
- printHeader(session.examName);
685
- printKeyValue('Questions', String(session.questionCount));
686
- printKeyValue('Duration', `${session.durationMinutes} minutes`);
687
- printKeyValue('Country', session.country);
688
- printTimeRemaining();
689
- // Show first question
690
- if (questions.length > 0) {
691
- printQuestion(questions[0]);
692
- }
693
- }
694
- catch (err) {
695
- console.log();
696
- printError(err.message);
697
- }
698
- });
699
- // โ”€โ”€โ”€ exam q [n] โ”€โ”€โ”€
700
- exam
701
- .command('q [n]')
702
- .description('View question (or all questions)')
703
- .action((n) => {
704
- logCommand(`exam q ${n || 'all'}`);
705
- const state = getExamState();
706
- if (!state) {
707
- printError('No exam in progress. Use "exam start <id>" to begin.');
708
- return;
709
- }
710
- printTimeRemaining();
711
- if (n) {
712
- const num = parseInt(n);
713
- const q = state.questions.find((q) => q.number === num);
714
- if (!q) {
715
- printError(`Question ${n} not found (1-${state.questions.length}).`);
716
- return;
717
- }
718
- state._lastQ = num;
719
- saveExamState(state);
720
- printQuestion(q, state.answers[num]);
721
- }
722
- else {
723
- // Show all questions summary
724
- printHeader(`${state.session.examName}`);
725
- const answered = Object.keys(state.answers).length;
726
- printKeyValue('Progress', `${answered}/${state.session.questionCount} answered`);
727
- console.log();
728
- for (const q of state.questions) {
729
- const ans = state.answers[q.number];
730
- const status = ans ? chalk.green(`[${ans}]`) : chalk.gray('[ ]');
731
- const cat = q.category ? chalk.gray(` [${q.category}]`) : '';
732
- console.log(` ${status} ${chalk.white(`Q${q.number}`)}${cat} ${chalk.gray(q.text.substring(0, 60))}${q.text.length > 60 ? '...' : ''}`);
733
- }
734
- console.log();
735
- }
736
- });
737
- // โ”€โ”€โ”€ exam mark [n] โ”€โ”€โ”€
738
- // Flag the current (or given) question for review later. Toggles on/off.
739
- exam
740
- .command('mark [n]')
741
- .description('Flag a question to review later (toggles)')
742
- .action((n) => {
743
- logCommand(`exam mark ${n || ''}`);
744
- const state = getExamState();
745
- if (!state) {
746
- printError('No exam in progress.');
747
- return;
748
- }
749
- const total = state.questions.length;
750
- const targetN = n ? parseInt(n, 10) : (state._lastQ || 1);
751
- if (isNaN(targetN) || targetN < 1 || targetN > total) {
752
- printError(`Invalid question number. Use 1..${total}.`);
753
- return;
754
- }
755
- const nowMarked = toggleBookmark(state, targetN);
756
- console.log();
757
- if (nowMarked) {
758
- console.log(chalk.yellow(` โ˜… Q${targetN} flagged for review.`));
759
- }
760
- else {
761
- console.log(chalk.gray(` โ˜† Q${targetN} unflagged.`));
762
- }
763
- const all = Array.isArray(state.bookmarks) ? state.bookmarks : [];
764
- if (all.length > 0) {
765
- console.log(chalk.gray(` Currently flagged: ${all.map((x) => `Q${x}`).join(', ')}`));
766
- }
767
- console.log(chalk.gray(' See all flagged questions in: ') + chalk.white('exam review'));
768
- console.log();
769
- });
770
- // โ”€โ”€โ”€ exam unmark [n] โ”€โ”€โ”€
771
- exam
772
- .command('unmark [n]')
773
- .description('Remove review flag from a question')
774
- .action((n) => {
775
- logCommand(`exam unmark ${n || ''}`);
776
- const state = getExamState();
777
- if (!state) {
778
- printError('No exam in progress.');
779
- return;
780
- }
781
- const total = state.questions.length;
782
- const targetN = n ? parseInt(n, 10) : (state._lastQ || 1);
783
- if (isNaN(targetN) || targetN < 1 || targetN > total) {
784
- printError(`Invalid question number. Use 1..${total}.`);
785
- return;
786
- }
787
- if (!isBookmarked(state, targetN)) {
788
- console.log(chalk.gray(` Q${targetN} was not flagged.`));
789
- return;
790
- }
791
- toggleBookmark(state, targetN);
792
- console.log();
793
- console.log(chalk.gray(` โ˜† Q${targetN} unflagged.`));
794
- console.log();
795
- });
796
- // โ”€โ”€โ”€ exam next โ”€โ”€โ”€
797
- exam
798
- .command('next')
799
- .description('Go to next question')
800
- .action(() => {
801
- logCommand('exam next');
802
- const state = getExamState();
803
- if (!state) {
804
- printError('No exam in progress.');
805
- return;
806
- }
807
- const current = state.questions.findIndex((q) => !state.answers[q.number]);
808
- const lastViewed = state._lastQ || 1;
809
- const next = Math.min(lastViewed + 1, state.questions.length);
810
- state._lastQ = next;
811
- saveExamState(state);
812
- const q = state.questions[next - 1];
813
- printQuestion(q, state.answers[q.number]);
814
- });
815
- // โ”€โ”€โ”€ exam prev โ”€โ”€โ”€
816
- exam
817
- .command('prev')
818
- .description('Go to previous question')
819
- .action(() => {
820
- logCommand('exam prev');
821
- const state = getExamState();
822
- if (!state) {
823
- printError('No exam in progress.');
824
- return;
825
- }
826
- const lastViewed = state._lastQ || 1;
827
- const prev = Math.max(lastViewed - 1, 1);
828
- state._lastQ = prev;
829
- saveExamState(state);
830
- const q = state.questions[prev - 1];
831
- printQuestion(q, state.answers[q.number]);
832
- });
833
- // โ”€โ”€โ”€ exam help โ”€โ”€โ”€
834
- exam
835
- .command('help')
836
- .description('Eliminate one wrong option (limited uses)')
837
- .action(async () => {
838
- logCommand('exam help');
839
- const state = getExamState();
840
- if (!state) {
841
- printError('No exam in progress.');
842
- return;
843
- }
844
- const help = getHelpState(state);
845
- const currentQ = state._lastQ || 1;
846
- const qHelps = help.perQ[currentQ] || 0;
847
- const eliminated = help.eliminated[currentQ] || [];
848
- const q = state.questions.find((qq) => qq.number === currentQ);
849
- if (!q)
850
- return;
851
- // Check: already used 2 helps on this question
852
- if (qHelps >= 2) {
853
- console.log();
854
- console.log(chalk.yellow(' ๐Ÿ™ˆ Max 2 helps per question โ€” you already have a 50/50!'));
855
- console.log(chalk.gray(' Trust your instinct and pick one!'));
856
- console.log();
857
- printQuestion(q, state.answers[q.number]);
858
- return;
859
- }
860
- // Check: only 1 option left (shouldn't happen with max 2 helps, but safety)
861
- const remaining = ['A', 'B', 'C', 'D'].filter((k) => !eliminated.includes(k));
862
- if (remaining.length <= 2) {
863
- console.log();
864
- console.log(chalk.yellow(' ๐Ÿ˜„ Only 2 options left โ€” you got this!'));
865
- console.log();
866
- printQuestion(q, state.answers[q.number]);
867
- return;
868
- }
869
- // Check budget
870
- if (help.used >= help.max) {
871
- const bonusUnlocked = help.max >= HELP_WITH_BONUS;
872
- const bonusRemaining = HELP_WITH_BONUS - help.max;
873
- if (!bonusUnlocked) {
874
- console.log();
875
- console.log(chalk.yellow(` Help used: ${help.used}/${help.max}`));
876
- console.log(chalk.white(' Need more? Type: ') + chalk.bold.yellow('more help') + chalk.gray(` (+${bonusRemaining} bonus uses)`));
877
- console.log();
878
- }
879
- else {
880
- console.log();
881
- console.log(chalk.gray(` All help used (${help.used}/${help.max}). You\'re on your own! ๐Ÿ’ช`));
882
- console.log();
883
- }
884
- return;
885
- }
886
- // Apply elimination of a specific option
887
- const applyEliminate = (toRemove) => {
888
- eliminated.push(toRemove);
889
- if (!help.eliminated[currentQ])
890
- help.eliminated[currentQ] = [];
891
- help.eliminated[currentQ] = eliminated;
892
- help.perQ[currentQ] = qHelps + 1;
893
- help.used++;
894
- state._helpUsed = help.used;
895
- state._helpMax = help.max;
896
- state._helpPerQ = help.perQ;
897
- state._eliminated = help.eliminated;
898
- if (!state.interactions)
899
- state.interactions = [];
900
- state.interactions.push({ ts: new Date().toISOString(), q: currentQ, type: 'help_used', result: `eliminated ${toRemove}` });
901
- saveExamState(state);
902
- console.log();
903
- console.log(chalk.yellow(` ๐Ÿ’ก Option ${toRemove} eliminated!`) + chalk.gray(` (help ${help.used}/${help.max})`));
904
- printQuestion(q, state.answers[q.number]);
905
- };
906
- // Pick a random wrong option to eliminate (needs correct answer)
907
- const doEliminate = (correct) => {
908
- const wrongRemaining = remaining.filter((k) => k !== correct && !eliminated.includes(k));
909
- if (wrongRemaining.length === 0)
910
- return;
911
- applyEliminate(wrongRemaining[Math.floor(Math.random() * wrongRemaining.length)]);
912
- };
913
- if (state.session.examId === 'demo-free' && q.answer) {
914
- doEliminate(q.answer);
915
- }
916
- else {
917
- // Server-authoritative: ask server which wrong option to eliminate
918
- const config = getConfig();
919
- const serverUrl = config.ctfdUrl || 'https://practice.icoa2026.au';
920
- const token = state.session?.token || '';
921
- try {
922
- const res = await fetch(`${serverUrl}/api/icoa/exams/${state.session.examId}/help`, {
923
- method: 'POST',
924
- headers: { 'Content-Type': 'application/json', 'User-Agent': 'icoa-cli' },
925
- body: JSON.stringify({ token, question: currentQ, eliminated }),
926
- signal: AbortSignal.timeout(5000),
927
- });
928
- const json = await res.json();
929
- const toRemove = json?.data?.eliminate;
930
- if (toRemove && ['A', 'B', 'C', 'D'].includes(toRemove)) {
931
- applyEliminate(toRemove);
932
- }
933
- else {
934
- console.log();
935
- console.log(chalk.gray(' Server did not return a valid option. Try again.'));
936
- }
937
- }
938
- catch {
939
- console.log();
940
- console.log(chalk.gray(' Could not reach server for help. Check your connection.'));
941
- }
942
- }
943
- });
944
- // โ”€โ”€โ”€ exam more-help โ”€โ”€โ”€
945
- exam
946
- .command('more-help')
947
- .description('Unlock hidden bonus help uses')
948
- .action(() => {
949
- logCommand('exam more-help');
950
- const state = getExamState();
951
- if (!state) {
952
- printError('No exam in progress.');
953
- return;
954
- }
955
- const help = getHelpState(state);
956
- // Demo (5โ†’8) and real exam (10โ†’15) each add their own bonus tier. Pick
957
- // the target based on what's already in state: demo-free exam keeps the
958
- // smaller step, real exams get the full 15.
959
- const isDemo = state.session.examId === 'demo-free';
960
- const bonusTarget = isDemo ? 8 : HELP_WITH_BONUS;
961
- if (help.max >= bonusTarget) {
962
- console.log(chalk.gray(' Bonus help already unlocked.'));
963
- return;
964
- }
965
- if (help.used < help.max) {
966
- console.log(chalk.gray(` You still have ${help.max - help.used} helps remaining. Use them first!`));
967
- return;
968
- }
969
- const bonusDelta = bonusTarget - help.max;
970
- state._helpMax = bonusTarget;
971
- saveExamState(state);
972
- console.log();
973
- console.log(chalk.green(` ๐ŸŽ +${bonusDelta} bonus help unlocked! (${bonusDelta}/${bonusTarget} remaining)`));
974
- console.log(chalk.gray(' Type "help" on any question to use.'));
975
- console.log();
976
- });
977
- // โ”€โ”€โ”€ exam answer <n> <choice> โ”€โ”€โ”€
978
- exam
979
- .command('answer <n> <choice...>')
980
- .description('Answer question N (A/B/C/D for MCQ, ICOA{flag} for practical)')
981
- .action(async (n, choiceParts) => {
982
- const choice = choiceParts.join(' ');
983
- logCommand(`exam answer ${n} ${choice}`);
984
- const state = getExamState();
985
- if (!state) {
986
- printError('No exam in progress. Use "exam start <id>" to begin.');
987
- return;
988
- }
989
- if (!checkTimeRemaining())
990
- return;
991
- const num = parseInt(n);
992
- const q = state.questions.find((q) => q.number === num);
993
- if (!q) {
994
- printError(`Question ${n} not found (1-${state.questions.length}).`);
995
- return;
996
- }
997
- // Detect question type: practical (ai4ctf/ctf4ai) or MCQ
998
- const isPractical = q.type === 'ai4ctf' || q.type === 'ctf4ai' || (q.options && !q.options.A && !q.options.B);
999
- let c;
1000
- if (isPractical) {
1001
- c = choice.trim();
1002
- if (!c) {
1003
- printError('Please provide your flag: exam answer <n> ICOA{your_flag}');
1004
- return;
1005
- }
1006
- // Reject letter-only answers on practical questions โ€” almost certainly
1007
- // a user who typed `A` thinking MCQ was still in play. Submitting 'A'
1008
- // as the flag is a footgun that would waste an attempt silently.
1009
- if (/^[A-Da-d]$/.test(c)) {
1010
- printError(`Q${num} is a practical question โ€” answer with a flag, not a letter.`);
1011
- console.log(chalk.gray(' Example: ') + chalk.green(`exam answer ${num} ICOA{your_flag}`));
1012
- return;
1013
- }
1014
- }
1015
- else {
1016
- c = choice.toUpperCase();
1017
- if (!['A', 'B', 'C', 'D'].includes(c)) {
1018
- printError('Choice must be A, B, C, or D.');
1019
- return;
1020
- }
1021
- }
1022
- // Q2 tutorial: must use help first before answering
1023
- if (state.session.examId === 'demo-free' && num === 2) {
1024
- const helpState = getHelpState(state);
1025
- const q2Helps = helpState.perQ[2] || 0;
1026
- if (q2Helps === 0) {
1027
- console.log();
1028
- console.log(chalk.yellow(' ๐Ÿ’ก Try typing ') + chalk.bold.yellow('help') + chalk.yellow(' first to see what it does!'));
1029
- console.log(chalk.gray(' This question requires you to use help before answering.'));
1030
- console.log();
1031
- return;
1032
- }
1033
- }
1034
- // Log interaction
1035
- const prevAnswer = state.answers[num];
1036
- if (!state.interactions)
1037
- state.interactions = [];
1038
- state.interactions.push({
1039
- ts: new Date().toISOString(),
1040
- q: num,
1041
- type: prevAnswer ? 'answer_changed' : 'answer_submitted',
1042
- input: c,
1043
- result: prevAnswer ? `changed from ${prevAnswer}` : 'new',
1044
- });
1045
- state.answers[num] = c;
1046
- state._lastQ = num;
1047
- saveExamState(state);
1048
- // UX: visible "saved" indicator reassures beginners their answer is safe
1049
- // even if they Ctrl+C or lose network. Neutral โ€” doesn't reveal correctness.
1050
- console.log(chalk.gray(' โœ“ saved'));
1051
- // Per-question sync to server (real exam only). Best-effort, fire-and-forget.
1052
- // If timer expires or network drops before final submit, the server still
1053
- // has the answer. Final submit is authoritative and overwrites this.
1054
- const tokenForSync = state.session.token;
1055
- if (tokenForSync && state.session.examId !== 'demo-free') {
1056
- const cfg = getConfig();
1057
- const url = `${cfg.ctfdUrl || 'https://practice.icoa2026.au'}/api/icoa/exams/${state.session.examId}/answer`;
1058
- fetch(url, {
1059
- method: 'POST',
1060
- headers: { 'Content-Type': 'application/json' },
1061
- body: JSON.stringify({ token: tokenForSync, question: num, answer: c }),
1062
- signal: AbortSignal.timeout(5000),
1063
- }).catch(() => { }); // silent failure โ€” local state still saved
1064
- }
1065
- const answered = Object.keys(state.answers).length;
1066
- const total = state.session.questionCount;
1067
- printSuccess(`Q${num}: ${c} โœ“ (${answered}/${total} answered)`);
1068
- // Demo-only: gentle per-answer feedback (grey, non-intrusive)
1069
- if (state.session.examId === 'demo-free' && q.answer) {
1070
- const NICE_WORDS = ['Nice one.', 'Solid pick.', 'On the money.', 'Exactly.', 'Spot on.'];
1071
- const CLOSE_WORDS = ['Close โ€” we\'ll revisit this one.', 'Hmm, worth a second look at the end.', 'Keep it in mind for review.'];
1072
- const pool = c === q.answer ? NICE_WORDS : CLOSE_WORDS;
1073
- const word = pool[Math.floor(Math.random() * pool.length)];
1074
- console.log(chalk.gray(` ยท ${word}`));
1075
- }
1076
- // Auto-show next question and update _lastQ
1077
- if (num < state.questions.length) {
1078
- const nextQ = state.questions[num]; // 0-indexed: questions[num] = question num+1
1079
- state._lastQ = nextQ.number;
1080
- saveExamState(state);
1081
- printQuestion(nextQ, state.answers[nextQ.number]);
1082
- }
1083
- else if (answered >= state.questions.length) {
1084
- console.log();
1085
- console.log(chalk.green.bold(' ๐ŸŽ‰ All questions answered!'));
1086
- console.log();
1087
- if (state.session.examId === 'demo-free') {
1088
- console.log(chalk.white(' Type ') + chalk.bold.cyan('exam submit') + chalk.white(' to see your results!'));
1089
- console.log();
1090
- }
1091
- else {
1092
- console.log(chalk.white(' Use: exam review ยท exam submit'));
1093
- }
1094
- }
1095
- });
1096
- // โ”€โ”€โ”€ exam review โ”€โ”€โ”€
1097
- exam
1098
- .command('review')
1099
- .description('Review all answers before submitting')
1100
- .action(() => {
1101
- logCommand('exam review');
1102
- const state = getExamState();
1103
- if (!state) {
1104
- printError('No exam in progress.');
1105
- return;
1106
- }
1107
- printHeader(`Review: ${state.session.examName}`);
1108
- printTimeRemaining();
1109
- console.log();
1110
- const cols = 6;
1111
- let line = ' ';
1112
- const marks = Array.isArray(state.bookmarks) ? state.bookmarks : [];
1113
- for (const q of state.questions) {
1114
- const ans = state.answers[q.number];
1115
- const isMarked = marks.includes(q.number);
1116
- const star = isMarked ? 'โ˜…' : ' ';
1117
- if (ans) {
1118
- // Answered + marked โ†’ bold yellow; answered only โ†’ green; unanswered + marked โ†’ yellow; unanswered โ†’ dim yellow
1119
- if (isMarked) {
1120
- line += chalk.bold.yellow(`Q${String(q.number).padStart(2)}${star}[${ans}] `);
1121
- }
1122
- else {
1123
- line += chalk.green(`Q${String(q.number).padStart(2)} [${ans}] `);
1124
- }
1125
- }
1126
- else {
1127
- if (isMarked) {
1128
- line += chalk.bold.yellow(`Q${String(q.number).padStart(2)}${star}[ ] `);
1129
- }
1130
- else {
1131
- line += chalk.yellow(`Q${String(q.number).padStart(2)} [ ] `);
1132
- }
1133
- }
1134
- if (q.number % cols === 0) {
1135
- console.log(line);
1136
- line = ' ';
1137
- }
1138
- }
1139
- if (line.trim())
1140
- console.log(line);
1141
- const answered = Object.keys(state.answers).length;
1142
- const total = state.session.questionCount;
1143
- const unanswered = total - answered;
1144
- console.log();
1145
- printKeyValue('Answered', chalk.green.bold(`${answered}/${total}`));
1146
- if (unanswered > 0) {
1147
- const missing = state.questions
1148
- .filter((q) => !state.answers[q.number])
1149
- .map((q) => q.number)
1150
- .join(', ');
1151
- printKeyValue('Unanswered', chalk.yellow(`${unanswered} (Q${missing})`));
1152
- }
1153
- if (marks.length > 0) {
1154
- printKeyValue('Flagged for review', chalk.bold.yellow(`โ˜… ${marks.length}`) + chalk.gray(` (${marks.map((x) => `Q${x}`).join(', ')})`));
1155
- }
1156
- console.log();
1157
- if (marks.length > 0) {
1158
- console.log(chalk.gray(' Jump to a flagged question: ') + chalk.white(`exam q ${marks[0]}`));
1159
- }
1160
- console.log(chalk.gray(' Ready? Use ') + chalk.bold.cyan('exam submit') + chalk.gray(' to submit for grading.'));
1161
- });
1162
- // โ”€โ”€โ”€ exam submit [confirm] โ”€โ”€โ”€
1163
- exam
1164
- .command('submit [confirmArg]')
1165
- .description('Submit exam for grading (use: "exam submit confirm" for real exams)')
1166
- .action(async (confirmArg) => {
1167
- logCommand(`exam submit ${confirmArg || ''}`);
1168
- const state = getExamState();
1169
- if (state && confirmArg === 'confirm') {
1170
- state._submitConfirmed = true;
1171
- saveExamState(state);
1172
- }
1173
- if (!state) {
1174
- printError('No exam in progress.');
1175
- return;
1176
- }
1177
- const answered = Object.keys(state.answers).length;
1178
- const total = state.session.questionCount;
1179
- const unanswered = total - answered;
1180
- if (answered === 0) {
1181
- const deadline = getExamDeadline();
1182
- const expired = deadline && new Date() >= deadline;
1183
- if (state.session.examId === 'demo-free' || expired) {
1184
- clearExamState();
1185
- printWarning('No answers to submit. Exam cleared.');
1186
- printInfo('Type: demo to start again.');
1187
- }
1188
- else {
1189
- printWarning('No answers to submit.');
1190
- printInfo('Answer some questions first: exam q 1');
1191
- }
1192
- return;
1193
- }
1194
- // Demo: submit directly. Real exam: require typed confirmation.
1195
- if (state.session.examId !== 'demo-free') {
1196
- const confirmedAlready = state._submitConfirmed === true;
1197
- const deadline = getExamDeadline();
1198
- const expired = !!(deadline && new Date() >= deadline);
1199
- if (!confirmedAlready && !expired) {
1200
- // First press of `exam submit` โ€” show a clear preflight summary and
1201
- // require the user to run `exam submit confirm` to proceed. Typing
1202
- // out "confirm" is the deliberate action that prevents accidental
1203
- // submissions while an exam is still in progress.
1204
- console.log();
1205
- console.log(chalk.red(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
1206
- console.log(chalk.bold.red(' โš  FINAL SUBMISSION โ€” please confirm'));
1207
- console.log(chalk.red(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
1208
- console.log();
1209
- console.log(chalk.white(` You will submit ${chalk.bold.green(`${answered}/${total}`)} answers.`));
1210
- if (unanswered > 0) {
1211
- const missingList = state.questions
1212
- .filter((q) => !state.answers[q.number])
1213
- .map((q) => `Q${q.number}`)
1214
- .join(', ');
1215
- console.log(chalk.yellow(` ${unanswered} unanswered: ${missingList}`));
1216
- }
1217
- const marks = Array.isArray(state.bookmarks) ? state.bookmarks : [];
1218
- if (marks.length > 0) {
1219
- console.log(chalk.yellow(` โ˜… ${marks.length} still flagged for review: ${marks.map((x) => `Q${x}`).join(', ')}`));
1220
- }
1221
- console.log();
1222
- console.log(chalk.white(' Answers are ') + chalk.bold.red('final') + chalk.white(' โ€” you cannot change them after submit.'));
1223
- console.log();
1224
- console.log(chalk.bold.white(' To confirm: ') + chalk.bold.cyan('exam submit confirm'));
1225
- console.log(chalk.white(' To go back: ') + chalk.cyan('exam review') + chalk.gray(' or ') + chalk.cyan('back'));
1226
- console.log();
1227
- return;
1228
- }
1229
- // Clear the flag so a subsequent re-entry requires re-confirmation.
1230
- if (confirmedAlready) {
1231
- delete state._submitConfirmed;
1232
- saveExamState(state);
1233
- }
1234
- }
1235
- console.log();
1236
- // Demo exam: grade locally
1237
- if (state.session.examId === 'demo-free') {
1238
- try {
1239
- drawProgress(0, t('grading'));
1240
- await sleep(300);
1241
- let score = 0;
1242
- const wrongQuestions = [];
1243
- const categoryStats = {};
1244
- for (const q of state.questions) {
1245
- const cat = q.category || 'Other';
1246
- if (!categoryStats[cat])
1247
- categoryStats[cat] = { correct: 0, total: 0 };
1248
- categoryStats[cat].total++;
1249
- const userAns = state.answers[q.number];
1250
- if (userAns && q.answer && userAns === q.answer) {
1251
- score++;
1252
- categoryStats[cat].correct++;
1253
- }
1254
- else {
1255
- wrongQuestions.push(q.number);
1256
- }
1257
- }
1258
- wrongQuestions.sort((a, b) => a - b);
1259
- drawProgress(100, t('complete'));
1260
- console.log();
1261
- // Elapsed time
1262
- const startedMs = new Date(state.session.startedAt).getTime();
1263
- const elapsedSec = Math.max(0, Math.round((Date.now() - startedMs) / 1000));
1264
- const elapsedMin = Math.floor(elapsedSec / 60);
1265
- const elapsedS = elapsedSec % 60;
1266
- const elapsedLabel = `${elapsedMin}m ${elapsedS}s`;
1267
- const questionsSnapshot = [...state.questions];
1268
- const answersSnapshot = { ...state.answers };
1269
- const helpState = getHelpState(state);
1270
- clearExamState();
1271
- const percentage = Math.round(score / total * 100);
1272
- // Report demo stats to server (includes full interaction audit
1273
- // trail so admin can see hint usage patterns, time per question,
1274
- // answer changes โ€” same data we collect for real exams).
1275
- const config = getConfig();
1276
- fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
1277
- method: 'POST',
1278
- headers: { 'Content-Type': 'application/json' },
1279
- body: JSON.stringify({
1280
- type: 'demo-exam',
1281
- score,
1282
- total,
1283
- lang: config.language || 'en',
1284
- help_used: helpState.used,
1285
- help_max: helpState.max,
1286
- tokens_used: 0,
1287
- solved: percentage >= 60 ? 1 : 0,
1288
- timestamp: new Date().toISOString(),
1289
- interactions: state.interactions || [],
1290
- }),
1291
- signal: AbortSignal.timeout(5000),
1292
- }).catch(() => { });
1293
- console.log();
1294
- console.log(chalk.cyan(' โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1295
- console.log();
1296
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—'));
1297
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—'));
1298
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘'));
1299
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘'));
1300
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘'));
1301
- console.log(chalk.bold.white(' โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ•'));
1302
- console.log();
1303
- console.log(chalk.bold(` ${t('score')}: ${score}/${total} (${percentage}%)`));
1304
- console.log(chalk.bold(` ${percentage >= 60 ? chalk.green(t('passed')) : chalk.red(t('notPassed'))}`));
1305
- console.log(chalk.gray(` Elapsed: ${elapsedLabel}`));
1306
- console.log();
1307
- console.log(chalk.yellow(' International Cyber Olympiad in AI 2026'));
1308
- console.log(chalk.gray(' Sydney, Australia ยท Jun 27 - Jul 2, 2026'));
1309
- console.log();
1310
- console.log(chalk.cyan(' โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1311
- // Paced reveal: give the user time to absorb each section.
1312
- await sleep(3000);
1313
- // Per-category breakdown with ASCII progress bars
1314
- const catEntries = Object.entries(categoryStats);
1315
- if (catEntries.length > 0) {
1316
- console.log();
1317
- console.log(chalk.bold.white(' By category'));
1318
- const BAR_WIDTH = 10;
1319
- for (const [cat, s] of catEntries) {
1320
- const pct = s.total > 0 ? Math.round(s.correct / s.total * 100) : 0;
1321
- const filled = s.total > 0 ? Math.round((s.correct / s.total) * BAR_WIDTH) : 0;
1322
- const empty = BAR_WIDTH - filled;
1323
- const color = pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.red;
1324
- const bar = color('โ–ˆ'.repeat(filled)) + chalk.gray('โ–‘'.repeat(empty));
1325
- console.log(' ' + chalk.white(cat.padEnd(20)) + ' ' + bar + ' ' + color(`${s.correct}/${s.total}`) + chalk.gray(` (${pct}%)`));
1326
- }
1327
- console.log();
1328
- console.log(chalk.cyan(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
1329
- }
1330
- // Record stats locally + save retry queue
1331
- const updatedStats = recordDemoAttempt(score, total);
1332
- const isNewBest = score === updatedStats.best && updatedStats.attempts > 1 && score > 0;
1333
- if (isNewBest) {
1334
- console.log();
1335
- console.log(chalk.bold.green(' โ˜… New personal best!'));
1336
- }
1337
- if (wrongQuestions.length > 0) {
1338
- const wrongSnapshots = questionsSnapshot
1339
- .filter((q) => wrongQuestions.includes(q.number))
1340
- .map((q) => ({ ...q }));
1341
- saveRetryQueue(wrongSnapshots);
1342
- }
1343
- else {
1344
- clearRetryQueue();
1345
- }
1346
- await sleep(3000);
1347
- // Show wrong answers with explanations
1348
- if (wrongQuestions.length > 0) {
1349
- console.log();
1350
- console.log(chalk.yellow(` ${wrongQuestions.length} ${t('incorrectIntro')}`));
1351
- console.log();
1352
- for (const qn of wrongQuestions) {
1353
- const q = questionsSnapshot.find((qq) => qq.number === qn);
1354
- if (!q)
1355
- continue;
1356
- const userAns = answersSnapshot[qn];
1357
- const correctAns = q.answer;
1358
- console.log(chalk.white(` Q${qn}. ${q.text}`));
1359
- if (userAns) {
1360
- console.log(chalk.red(` ${t('yourAnswer')}: ${userAns}. ${q.options[userAns]}`));
1361
- }
1362
- else {
1363
- console.log(chalk.yellow(` ${t('yourAnswer')}: โ€”`));
1364
- }
1365
- if (correctAns) {
1366
- console.log(chalk.green(` ${t('correct')}: ${correctAns}. ${q.options[correctAns]}`));
1367
- }
1368
- if (q.explanation) {
1369
- console.log(chalk.gray(` โ†’ ${q.explanation}`));
1370
- }
1371
- console.log();
1372
- }
1373
- }
1374
- else {
1375
- console.log();
1376
- console.log(chalk.green(` ${t('perfectScore')}`));
1377
- }
1378
- // Stage 1 ends here. Retry hint, CTF intro, and dual-track overview
1379
- // are deferred to the final report printed after ctf4ai so the flow
1380
- // feels: theory โ†’ ai4ctf โ†’ ctf4ai โ†’ one combined report + retry/back.
1381
- // The retry queue was already persisted above, so the final report
1382
- // can surface it later via getRetryQueue().
1383
- await sleep(2000);
1384
- console.log();
1385
- console.log(chalk.cyan(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
1386
- console.log(chalk.white(` ${t('nextLabel')} `) + chalk.bold.green('ai4ctf') + chalk.gray(` โ€” ${t('ai4ctfDesc')}`));
1387
- console.log(chalk.gray(` ${t('ai4ctfSub')}`));
1388
- console.log(chalk.cyan(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
1389
- console.log();
1390
- }
1391
- catch (err) {
1392
- console.log();
1393
- printError(err.message);
1394
- }
1395
- return;
1396
- }
1397
- // Real exam: submit to server
1398
- // Token-based exam: use direct fetch with exam token
1399
- // CTFd-based exam: use ExamClient
1400
- const examToken = state.session.token;
1401
- try {
1402
- drawProgress(0, 'Uploading answers...');
1403
- let result;
1404
- if (examToken) {
1405
- // Submit via exam token
1406
- const config = getConfig();
1407
- const serverUrl = config.ctfdUrl || 'https://practice.icoa2026.au';
1408
- const res = await fetch(`${serverUrl}/api/icoa/exams/${state.session.examId}/submit`, {
1409
- method: 'POST',
1410
- headers: { 'Content-Type': 'application/json' },
1411
- body: JSON.stringify({
1412
- token: examToken,
1413
- answers: state.answers,
1414
- interactions: state.interactions || [],
1415
- aiUsage: state.aiUsage || { ai4ctf: 0, ctf4ai: 0 },
1416
- // Flagged-for-review questions (display numbers). Server maps
1417
- // these to original pool numbers for cross-session aggregation
1418
- // so the exam center can spot high-flag-rate questions.
1419
- bookmarks: state.bookmarks || [],
1420
- }),
1421
- signal: AbortSignal.timeout(15000),
1422
- });
1423
- if (!res.ok) {
1424
- const err = await res.json().catch(() => ({ message: 'Submit failed' }));
1425
- throw new Error(err.message || 'Submit failed');
1426
- }
1427
- const json = await res.json();
1428
- result = json.data;
1429
- }
1430
- else {
1431
- const client = requireExamConnection();
1432
- if (!client)
1433
- return;
1434
- result = await client.submitExam(state.session.examId, state.answers);
1435
- }
1436
- drawProgress(50, 'Grading...');
1437
- await sleep(300);
1438
- drawProgress(100, 'Complete!');
1439
- console.log();
1440
- // Capture stats from state BEFORE we clear it for the debrief display.
1441
- const preSubmit = {
1442
- answered: Object.keys(state.answers).length,
1443
- total: state.session.questionCount,
1444
- bookmarks: Array.isArray(state.bookmarks) ? state.bookmarks.length : 0,
1445
- help: getHelpState(state),
1446
- aiUsage: state.aiUsage || { ai4ctf: 0, ctf4ai: 0 },
1447
- interactions: (state.interactions || []).length,
1448
- durationMin: state.session.durationMinutes || 0,
1449
- confirmedAt: state.session.confirmedAt || state.session.startedAt,
1450
- examId: state.session.examId,
1451
- };
1452
- clearExamState();
1453
- const elapsedSec = Math.max(0, Math.round((Date.now() - new Date(preSubmit.confirmedAt).getTime()) / 1000));
1454
- const elapsedMin = Math.floor(elapsedSec / 60);
1455
- const elapsedS = elapsedSec % 60;
1456
- // โ”€โ”€โ”€ Student debrief โ€” qualitative only โ”€โ”€โ”€
1457
- // National Selection: student sees pass/fail and directional feedback.
1458
- // Numeric scores go to the country's exam center who decides the real
1459
- // selection bar. This respects national autonomy โ€” each country picks
1460
- // who advances based on their own criteria (top N, percentile, etc.).
1461
- console.log();
1462
- console.log(chalk.cyan(' โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1463
- console.log();
1464
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—'));
1465
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—'));
1466
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘'));
1467
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘'));
1468
- console.log(chalk.bold.white(' โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘'));
1469
- console.log(chalk.bold.white(' โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ•'));
1470
- console.log();
1471
- const statusLine = result.passed
1472
- ? chalk.green.bold(' โœ“ Submission accepted โ€” baseline met')
1473
- : chalk.yellow.bold(' โ—‹ Submission accepted');
1474
- console.log(statusLine);
1475
- console.log();
1476
- console.log(chalk.white(' Your answers have been recorded and sent to your national'));
1477
- console.log(chalk.white(' exam center. Final scoring and selection are decided by'));
1478
- console.log(chalk.white(' the organizer in your country.'));
1479
- console.log();
1480
- console.log(chalk.cyan(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
1481
- console.log();
1482
- // Your run โ€” personal stats only, no score
1483
- console.log(chalk.bold.white(' Your run'));
1484
- const unanswered = preSubmit.total - preSubmit.answered;
1485
- console.log(' ' + chalk.gray('Answered: ') + chalk.white(`${preSubmit.answered}/${preSubmit.total}`)
1486
- + (unanswered > 0 ? chalk.gray(` ยท ${unanswered} skipped`) : ''));
1487
- console.log(' ' + chalk.gray('Time used: ') + chalk.white(`${elapsedMin}m ${elapsedS}s`)
1488
- + chalk.gray(` of ${preSubmit.durationMin}m total`));
1489
- if (preSubmit.bookmarks > 0) {
1490
- console.log(' ' + chalk.gray('Flagged: ') + chalk.yellow(`โ˜… ${preSubmit.bookmarks}`));
1491
- }
1492
- console.log();
1493
- // Qualitative category feedback: "stronger" vs "area to grow" based on
1494
- // relative rank, not absolute percentages. Never reveals numbers.
1495
- const catStats = result.categoryStats;
1496
- if (catStats && Object.keys(catStats).length >= 2) {
1497
- const ranked = Object.entries(catStats)
1498
- .filter(([, s]) => s.total > 0)
1499
- .map(([cat, s]) => ({ cat, pct: s.correct / s.total }))
1500
- .sort((a, b) => b.pct - a.pct);
1501
- const topCount = Math.min(2, Math.ceil(ranked.length / 3));
1502
- const bottomCount = Math.min(2, Math.ceil(ranked.length / 3));
1503
- const strong = ranked.slice(0, topCount).map((r) => r.cat);
1504
- const grow = ranked.slice(-bottomCount).map((r) => r.cat);
1505
- // Only show if there's actual variation (avoid "strong: X, grow: X")
1506
- const overlap = strong.some((s) => grow.includes(s));
1507
- if (!overlap) {
1508
- console.log(chalk.bold.white(' Directional feedback'));
1509
- console.log(' ' + chalk.green('Stronger here: ') + chalk.white(strong.join(', ')));
1510
- console.log(' ' + chalk.yellow('Room to grow: ') + chalk.white(grow.join(', ')));
1511
- console.log(chalk.gray(' (relative to other categories โ€” numeric scores go to your exam center)'));
1512
- console.log();
1513
- }
1514
- }
1515
- // Assistance usage โ€” personal, fine to show
1516
- console.log(chalk.bold.white(' Assistance used'));
1517
- console.log(' ' + chalk.gray('Help: ') + chalk.white(`${preSubmit.help.used}/${preSubmit.help.max}`));
1518
- console.log(' ' + chalk.gray('AI tokens: ') + chalk.white(`${preSubmit.aiUsage.ai4ctf} AI4CTF`) + chalk.gray(' ยท ') + chalk.white(`${preSubmit.aiUsage.ctf4ai} CTF4AI`));
1519
- console.log();
1520
- console.log(chalk.cyan(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
1521
- console.log();
1522
- // Next steps
1523
- console.log(chalk.bold.white(' What next'));
1524
- console.log(chalk.gray(' ยท Your national organizer will announce selection results.'));
1525
- console.log(chalk.gray(' ยท Keep sharpening: ') + chalk.white('demo') + chalk.gray(' is free, unlimited practice.'));
1526
- console.log(chalk.gray(' ยท Reference guides for 38 tools: ') + chalk.white('ref'));
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);
1533
- console.log(chalk.yellow(' ICOA 2026 ยท Sydney, Australia ยท Jun 27 โ€“ Jul 2'));
1534
- console.log(chalk.cyan.underline(' https://icoa2026.au'));
1535
- console.log();
1536
- console.log(chalk.cyan(' โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1537
- console.log();
1538
- }
1539
- catch (err) {
1540
- console.log();
1541
- printError(err.message);
1542
- }
1543
- });
1544
- // โ”€โ”€โ”€ exam result โ”€โ”€โ”€
1545
- exam
1546
- .command('result [id]')
1547
- .description('View exam result')
1548
- .action(async (examId) => {
1549
- logCommand(`exam result ${examId || ''}`);
1550
- const client = requireExamConnection();
1551
- if (!client)
1552
- return;
1553
- if (!examId) {
1554
- const state = getExamState();
1555
- if (state) {
1556
- examId = state.session.examId;
1557
- }
1558
- else {
1559
- printError('Specify exam ID: exam result <id>');
1560
- return;
1561
- }
1562
- }
1563
- const spinner = createSpinner('Loading result...');
1564
- spinner.start();
1565
- try {
1566
- const result = await client.getResult(examId);
1567
- spinner.succeed('Result loaded');
1568
- console.log();
1569
- printHeader(result.examName);
1570
- printKeyValue('Score', chalk.bold(`${result.score}/${result.total}`));
1571
- printKeyValue('Percentage', chalk.bold(`${result.percentage}%`));
1572
- printKeyValue('Status', result.passed ? chalk.green.bold('PASSED') : chalk.red.bold('NOT PASSED'));
1573
- printKeyValue('Submitted', result.submittedAt);
1574
- console.log();
1575
- }
1576
- catch (err) {
1577
- spinner.fail('Failed to load result');
1578
- printError(err.message);
1579
- }
1580
- });
1581
- // โ”€โ”€โ”€ exam token <code> (Selection mode: no login needed) โ”€โ”€โ”€
1582
- exam
1583
- .command('token <code>')
1584
- .description('Enter exam with access token (no login needed)')
1585
- .action(async (code) => {
1586
- logCommand(`exam token ${code}`);
1587
- const { getRealExamState, saveExamState: saveExamStateFn, clearExamState: clearState } = await import('../lib/exam-state.js');
1588
- const existing = getRealExamState();
1589
- if (existing) {
1590
- const existingToken = existing.session.token;
1591
- // Auto-recover from abandoned/expired sessions. If the stored state's
1592
- // deadline is >30 min in the past, the session is unusable (past the
1593
- // server's submit grace window). If user types a DIFFERENT token in
1594
- // this state, they clearly mean "start fresh". Clear silently rather
1595
- // than locking them out forever. Prevents the stale-A-paper-content +
1596
- // "Could not reload questions in new language" lockout.
1597
- const deadlineNow = getExamDeadline();
1598
- const typedDiffers = !!existingToken && existingToken.trim().toUpperCase() !== code.trim().toUpperCase();
1599
- const expiredPast30min = deadlineNow ? (deadlineNow.getTime() + 30 * 60 * 1000) < Date.now() : false;
1600
- if (typedDiffers && expiredPast30min) {
1601
- clearState();
1602
- console.log();
1603
- console.log(chalk.gray(' (previous exam session expired โ€” cleared state, starting fresh with your new token)'));
1604
- console.log();
1605
- // Fall through to the fresh-start flow below (skip the "already in progress" guard)
1606
- // Re-run this action: easiest to just continue out of the `if (existing)` block
1607
- // by setting a flag. We do that by falling through โ€” but since we're inside
1608
- // `if (existing)`, we need to proceed. Easiest: re-invoke the inner start flow.
1609
- // Actually: since we cleared state, the subsequent code paths will treat it
1610
- // as a fresh session. But we're inside `if (existing)`. Jump to the fresh path
1611
- // by executing a goto-equivalent via function call. Simplest: return here
1612
- // after instructing the user to retype the command.
1613
- console.log(chalk.white(' Please re-type: ') + chalk.bold.cyan(`exam ${code}`));
1614
- console.log();
1615
- return;
1616
- }
1617
- // Migration path: pre-v2.19.55 sessions didn't persist the token on
1618
- // state. Those sessions can't use `lang` or submit via the new token
1619
- // path. If the user re-types the matching token, attach it so the
1620
- // session can reach the server again. Device binding on the server
1621
- // means only the original token holder can do this.
1622
- if (!existingToken && code) {
1623
- existing.session.token = code.trim();
1624
- saveExamStateFn(existing);
1625
- console.log();
1626
- console.log(chalk.green(' โœ“ Token re-attached to your in-progress exam.'));
1627
- console.log(chalk.gray(' You can now use: ') + chalk.white('lang <code>') + chalk.gray(' ยท ') + chalk.white('exam submit'));
1628
- console.log();
1629
- console.log(chalk.bold.white(' Continue this exam:'));
1630
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam q 1') + chalk.gray(' resume at any question'));
1631
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam review') + chalk.gray(' see progress + flagged items'));
1632
- console.log();
1633
- return;
1634
- }
1635
- const answered = Object.keys(existing.answers).length;
1636
- const total = existing.session.questionCount;
1637
- const deadline = getExamDeadline();
1638
- const remainingMin = deadline ? Math.max(0, Math.round((deadline.getTime() - Date.now()) / 60000)) : null;
1639
- console.log();
1640
- console.log(chalk.yellow(` โš  An exam is already in progress on this device.`));
1641
- console.log();
1642
- console.log(chalk.gray(' Exam: ') + chalk.white(existing.session.examName));
1643
- if (existingToken) {
1644
- console.log(chalk.gray(' Token: ') + chalk.white(existingToken) + (existingToken === code ? chalk.green(' (same as you just typed)') : chalk.yellow(' (different from the one you typed)')));
1645
- }
1646
- console.log(chalk.gray(' Answers: ') + chalk.white(`${answered}/${total}`));
1647
- if (remainingMin !== null) {
1648
- console.log(chalk.gray(' Time: ') + chalk.white(`${remainingMin} min remaining`));
1649
- }
1650
- console.log();
1651
- console.log(chalk.bold.white(' To continue this exam:'));
1652
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam q 1') + chalk.gray(' resume at any question'));
1653
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam review') + chalk.gray(' see progress + flagged items'));
1654
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam submit') + chalk.gray(' finish and submit'));
1655
- if (existingToken && existingToken !== code) {
1656
- console.log();
1657
- console.log(chalk.yellow(' Note: you typed a different token. Each exam token is bound to'));
1658
- console.log(chalk.yellow(' one device + one session. You cannot switch tokens mid-exam.'));
1659
- console.log();
1660
- console.log(chalk.gray(' If you need to abandon this session (e.g. expired / wrong paper),'));
1661
- console.log(chalk.gray(' run ') + chalk.bold.cyan('exam reset') + chalk.gray(' to wipe local state, then re-enter the new token.'));
1662
- }
1663
- console.log();
1664
- return;
1665
- }
1666
- // Gate: require exam setup
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()) {
1677
- console.log();
1678
- printWarning('Pre-exam setup required before entering a token.');
1679
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam setup'));
1680
- console.log(chalk.gray(' Or type ') + chalk.bold.cyan('back') + chalk.gray(' to return to the main menu.'));
1681
- console.log();
1682
- return;
1683
- }
1684
- // Auto-set language from token country prefix
1685
- const COUNTRY_LANG = {
1686
- UA: 'uk', PE: 'es', CN: 'zh', AU: 'en', JP: 'ja', KR: 'ko',
1687
- BR: 'pt', SA: 'ar', FR: 'fr', DE: 'de', IN: 'hi', ID: 'id',
1688
- TH: 'th', VN: 'vi', TR: 'tr', RU: 'ru', EG: 'ar', HT: 'ht',
1689
- PH: 'en', MY: 'en', SG: 'en', ZA: 'en',
1690
- };
1691
- const countryPrefix = code.trim().substring(0, 2).toUpperCase();
1692
- const detectedLang = COUNTRY_LANG[countryPrefix];
1693
- const config = getConfig();
1694
- const serverUrl = config.ctfdUrl || 'https://practice.icoa2026.au';
1695
- const lang = detectedLang || config.language || 'en';
1696
- if (detectedLang && detectedLang !== (config.language || 'en')) {
1697
- saveConfig({ language: detectedLang });
1698
- }
1699
- console.log();
1700
- drawProgress(0, 'Validating token...');
1701
- try {
1702
- const res = await fetch(`${serverUrl}/api/icoa/exam-token`, {
1703
- method: 'POST',
1704
- headers: { 'Content-Type': 'application/json' },
1705
- body: JSON.stringify({
1706
- token: code.trim(),
1707
- deviceHash: getDeviceFingerprint(),
1708
- lang,
1709
- }),
1710
- signal: AbortSignal.timeout(10000),
1711
- });
1712
- if (!res.ok) {
1713
- drawProgress(0, '');
1714
- console.log();
1715
- const err = await res.json().catch(() => ({ message: 'Invalid token' }));
1716
- printError(err.message || 'Invalid exam token');
1717
- printInfo('Check your token or contact your proctor.');
1718
- return;
1719
- }
1720
- drawProgress(30, 'Loading exam...');
1721
- const json = await res.json();
1722
- const { session, questions, languages } = json.data;
1723
- drawProgress(60, 'Preparing...');
1724
- await sleep(200);
1725
- drawProgress(100, 'Ready!');
1726
- console.log();
1727
- console.log();
1728
- // โ”€โ”€ Exam info page (timer NOT started yet) โ”€โ”€
1729
- printHeader(session.examName);
1730
- console.log();
1731
- printKeyValue('Questions', `${session.questionCount} (30 MCQ + 10 practical)`);
1732
- printKeyValue('Duration', `${session.durationMinutes} minutes`);
1733
- printKeyValue('Total', `${session.totalScore || 150} points`);
1734
- printKeyValue('Pass', `${session.passingScore || 75} points (50%)`);
1735
- console.log();
1736
- console.log(chalk.white(' Assistance:'));
1737
- console.log(chalk.gray(' Help: 10 uses + 5 hidden bonus (MCQ only)'));
1738
- console.log(chalk.gray(' Hints: A(5) B(3) C(1) โ€” practical only'));
1739
- console.log(chalk.gray(' AI: 25K AI4CTF + 25K CTF4AI tokens'));
1740
- if (languages && languages.length > 1) {
1741
- console.log(chalk.gray(` Lang: ${languages.join(', ')} โ€” switch with: lang <code>`));
1742
- }
1743
- console.log();
1744
- console.log(chalk.yellow(' Rules:'));
1745
- console.log(chalk.gray(' โ€ข All interactions are recorded for audit'));
1746
- console.log(chalk.gray(' โ€ข Timer auto-submits when time expires'));
1747
- console.log(chalk.gray(' โ€ข You may exit and resume with the same token'));
1748
- console.log();
1749
- // โ”€โ”€ Wait for confirmation โ”€โ”€
1750
- // Raw stdin read (not readline.createInterface) โ€” a second readline on
1751
- // process.stdin fights the parent REPL's readline and leaves stdin in
1752
- // a half-detached state, so subsequent commands silently get no input.
1753
- await new Promise((resolve) => {
1754
- process.stdout.write(chalk.bold.yellow(' Press Enter to start the exam timer... '));
1755
- const wasRaw = process.stdin.isTTY ? process.stdin.isRaw : false;
1756
- if (process.stdin.isTTY && process.stdin.setRawMode) {
1757
- process.stdin.setRawMode(false);
1758
- }
1759
- const onData = (chunk) => {
1760
- const s = chunk.toString();
1761
- if (s.includes('\n') || s.includes('\r')) {
1762
- process.stdin.removeListener('data', onData);
1763
- if (process.stdin.isTTY && process.stdin.setRawMode) {
1764
- process.stdin.setRawMode(wasRaw);
1765
- }
1766
- process.stdout.write('\n');
1767
- resolve();
1768
- }
1769
- };
1770
- process.stdin.on('data', onData);
1771
- process.stdin.resume();
1772
- });
1773
- // โ”€โ”€ Timer starts NOW โ”€โ”€
1774
- const confirmedAt = new Date().toISOString();
1775
- session.confirmedAt = confirmedAt;
1776
- // Store the token on session so lang switching, exam submit, and
1777
- // resume can reach the server without re-prompting. Device binding
1778
- // means the token alone is useless on another machine, so keeping
1779
- // it in exam-state.json is safe.
1780
- session.token = code.trim();
1781
- // Best-effort server-time sync. Corrects client clock drift (NTP-less
1782
- // Kali live-boots, timezone misconfigs) so the displayed countdown
1783
- // matches the server's authoritative deadline. Silent on failure โ€”
1784
- // old servers (no endpoint) or offline clients fall back to the
1785
- // local-clock-only deadline (see getExamDeadline in exam-state.ts).
1786
- try {
1787
- const t0 = Date.now();
1788
- const tRes = await fetch(`${serverUrl}/api/icoa/server-time`, {
1789
- method: 'GET',
1790
- signal: AbortSignal.timeout(3000),
1791
- });
1792
- if (tRes.ok) {
1793
- const tJson = await tRes.json();
1794
- const serverMs = tJson?.data?.server_time_ms;
1795
- if (typeof serverMs === 'number' && serverMs > 0) {
1796
- // Offset is measured at the midpoint of the request window to
1797
- // halve the latency bias (serverMs reflects when the server
1798
- // read its clock; midpoint of our send/receive window is the
1799
- // best single-sample approximation of when that reading aligns
1800
- // with our clock).
1801
- const clientMs = (t0 + Date.now()) / 2;
1802
- session.clockOffsetMs = Math.round(serverMs - clientMs);
1803
- session.deadlineServerMs = serverMs + session.durationMinutes * 60 * 1000;
1804
- }
1805
- }
1806
- }
1807
- catch {
1808
- // Offline or old server โ€” keep fallback (confirmedAt + duration local).
1809
- }
1810
- saveExamState({ session, questions, answers: {}, interactions: [], aiUsage: { ai4ctf: 0, ctf4ai: 0 } });
1811
- console.log();
1812
- printSuccess('Exam started! Timer is running.');
1813
- printKeyValue('Time Remaining', `${session.durationMinutes}:00`);
1814
- if (questions.length > 0) {
1815
- printQuestion(questions[0]);
1816
- }
1817
- }
1818
- catch (err) {
1819
- console.log();
1820
- if (err.name === 'TimeoutError') {
1821
- printError('Server not reachable. Check your internet connection.');
1822
- }
1823
- else {
1824
- printError(err.message || 'Failed to start exam');
1825
- }
1826
- }
1827
- });
1828
- // โ”€โ”€โ”€ exam demo โ”€โ”€โ”€
1829
- exam
1830
- .command('reset', { hidden: true })
1831
- .description('Abandon current exam session and wipe local state (recovery only)')
1832
- .action(async () => {
1833
- logCommand('exam reset');
1834
- const { getRealExamState, clearExamState: clearState } = await import('../lib/exam-state.js');
1835
- const existing = getRealExamState();
1836
- if (!existing) {
1837
- console.log();
1838
- console.log(chalk.gray(' No active exam session to reset.'));
1839
- console.log();
1840
- return;
1841
- }
1842
- const tok = existing.session.token;
1843
- console.log();
1844
- console.log(chalk.yellow(' โš  Reset will abandon your current exam session on this device.'));
1845
- console.log(chalk.gray(' Exam: ') + chalk.white(existing.session.examName));
1846
- if (tok)
1847
- console.log(chalk.gray(' Token: ') + chalk.white(tok));
1848
- console.log(chalk.gray(' The token itself is NOT revoked server-side. If your session is'));
1849
- console.log(chalk.gray(' still active and not submitted, re-entering the same token resumes it.'));
1850
- console.log();
1851
- console.log(chalk.gray(' Type ') + chalk.bold.cyan('reset') + chalk.gray(' to confirm, or anything else to cancel:'));
1852
- // Typed-word confirmation โ€” matches existing exam submit confirm pattern
1853
- const confirmWord = await new Promise((resolve) => {
1854
- process.stdout.write(' > ');
1855
- const onData = (chunk) => {
1856
- const s = chunk.toString().trim();
1857
- if (s.includes('\n') || s.includes('\r') || s.length > 0) {
1858
- process.stdin.removeListener('data', onData);
1859
- resolve(s.replace(/[\r\n]/g, ''));
1860
- }
1861
- };
1862
- process.stdin.on('data', onData);
1863
- process.stdin.resume();
1864
- });
1865
- if (confirmWord.toLowerCase() !== 'reset') {
1866
- console.log(chalk.gray(' Cancelled.'));
1867
- console.log();
1868
- return;
1869
- }
1870
- clearState();
1871
- console.log();
1872
- console.log(chalk.green(' โœ“ Exam state cleared.'));
1873
- console.log(chalk.gray(' You can now enter a new token with ') + chalk.bold.cyan('exam <token>'));
1874
- console.log();
1875
- });
1876
- exam
1877
- .command('demo')
1878
- .description('Try a free practice exam (no account needed)')
1879
- .action(async () => {
1880
- logCommand('exam demo');
1881
- reportDemoEvent('demo-enter');
1882
- const { pickDemoQuestions, getLocalizedDemoSession, DEMO_PICK_SIZE, DEMO_POOL_SIZE } = await import('../lib/demo-exam.js');
1883
- // First-time intro animation (skippable)
1884
- const cfg = getConfig();
1885
- if (!cfg.demoIntroSeen) {
1886
- await playDemoIntro();
1887
- saveConfig({ demoIntroSeen: true });
1888
- }
1889
- // T2-6: Pre-start confirmation. Prevents accidental launches (user typed
1890
- // `demo` instead of exploring the menu). No timer pressure, but exam
1891
- // state + AI budget get allocated the moment the first question lands,
1892
- // so a 1-line "are you ready?" gate is cheap insurance. Ctrl+C here
1893
- // cleanly exits via the REPL's SIGINT handler.
1894
- console.log();
1895
- console.log(chalk.white(' Demo: ') + chalk.gray(`${DEMO_PICK_SIZE} questions drawn from a pool of ${DEMO_POOL_SIZE}. No timer. Free practice.`));
1896
- console.log(chalk.gray(' You can pause with ') + chalk.cyan('Ctrl+C') + chalk.gray(' or leave with ') + chalk.cyan('back') + chalk.gray(' at any time.'));
1897
- await new Promise((resolve) => {
1898
- process.stdout.write(chalk.bold.yellow(' Press Enter to begin... '));
1899
- const wasRaw = process.stdin.isTTY ? process.stdin.isRaw : false;
1900
- if (process.stdin.isTTY && process.stdin.setRawMode) {
1901
- process.stdin.setRawMode(false);
1902
- }
1903
- const onData = (chunk) => {
1904
- const s = chunk.toString();
1905
- if (s.includes('\n') || s.includes('\r')) {
1906
- process.stdin.removeListener('data', onData);
1907
- if (process.stdin.isTTY && process.stdin.setRawMode) {
1908
- process.stdin.setRawMode(wasRaw);
1909
- }
1910
- process.stdout.write('\n');
1911
- resolve();
1912
- }
1913
- };
1914
- process.stdin.on('data', onData);
1915
- process.stdin.resume();
1916
- });
1917
- const DEMO_QUESTIONS = pickDemoQuestions(DEMO_PICK_SIZE);
1918
- const DEMO_SESSION = getLocalizedDemoSession();
1919
- // Demo uses separate state file โ€” doesn't conflict with real exam
1920
- const { getDemoState, clearExamState: clearState } = await import('../lib/exam-state.js');
1921
- const existingDemo = getDemoState();
1922
- if (existingDemo) {
1923
- clearState('demo-free');
1924
- }
1925
- console.log();
1926
- printHeader('ICOA Demo Exam โ€” Free Practice');
1927
- console.log();
1928
- console.log(chalk.white(' Free practice ยท No account needed ยท No time limit'));
1929
- console.log(chalk.white(` ${DEMO_PICK_SIZE} random questions from a pool of ${DEMO_POOL_SIZE} ยท Pick one answer per question`));
1930
- // Best score tracker
1931
- const stats = getDemoStats();
1932
- if (stats.attempts > 0) {
1933
- const bestPct = stats.bestPercentage;
1934
- const bestColor = bestPct >= 80 ? chalk.green : bestPct >= 60 ? chalk.yellow : chalk.white;
1935
- console.log();
1936
- console.log(chalk.gray(' Your best: ') + bestColor(`${stats.best}/${DEMO_PICK_SIZE} (${bestPct}%)`) + chalk.gray(` ยท ${stats.attempts} attempts`));
1937
- }
1938
- console.log();
1939
- printHowToPlay();
1940
- console.log();
1941
- // Show language info
1942
- const { getConfig: gc } = await import('../lib/config.js');
1943
- const currentLang = gc().language || 'en';
1944
- if (currentLang === 'en') {
1945
- console.log(chalk.gray(' Questions in English. To switch language first:'));
1946
- console.log(chalk.gray(' lang es (Espaรฑol) ยท lang zh (ไธญๆ–‡) ยท lang ko (ํ•œ๊ตญ์–ด) ยท lang ja (ๆ—ฅๆœฌ่ชž)'));
1947
- console.log(chalk.gray(' lang fr ยท lang ar ยท lang pt ยท 15 languages supported'));
1948
- }
1949
- else {
1950
- console.log(chalk.green(` Language: ${currentLang}`));
1951
- }
1952
- console.log();
1953
- console.log(chalk.white(' Starting exam...'));
1954
- const session = { ...DEMO_SESSION, startedAt: new Date().toISOString() };
1955
- console.log();
1956
- drawProgress(0, 'Preparing questions...');
1957
- await sleep(200);
1958
- drawProgress(40, 'Shuffling options...');
1959
- await sleep(200);
1960
- drawProgress(80, 'Almost ready...');
1961
- await sleep(150);
1962
- drawProgress(100, 'Ready!');
1963
- console.log();
1964
- console.log();
1965
- // Demo keeps the lighter 5 + 3 help budget (10 questions, so 5 base =
1966
- // half the questions; plenty without making the exam trivial).
1967
- saveExamState({ session, questions: DEMO_QUESTIONS, answers: {}, _helpMax: 5 });
1968
- printKeyValue('Questions', String(DEMO_PICK_SIZE));
1969
- printKeyValue('Duration', 'No time limit');
1970
- // Show first question
1971
- printQuestion(DEMO_QUESTIONS[0]);
1972
- });
1973
- // โ”€โ”€โ”€ exam demo-retry โ”€โ”€โ”€ (runs demo with the wrong questions from last attempt)
1974
- exam
1975
- .command('demo-retry')
1976
- .description('Retry only the questions you got wrong last demo attempt')
1977
- .action(async () => {
1978
- logCommand('exam demo-retry');
1979
- reportDemoEvent('post-report-retry');
1980
- const { getLocalizedDemoSession } = await import('../lib/demo-exam.js');
1981
- const retryQueue = getRetryQueue();
1982
- if (!retryQueue || retryQueue.length === 0) {
1983
- console.log();
1984
- console.log(chalk.yellow(' No wrong questions to retry. Run ') + chalk.bold.cyan('demo') + chalk.yellow(' first.'));
1985
- console.log();
1986
- return;
1987
- }
1988
- const existing = getExamState();
1989
- if (existing) {
1990
- if (existing.session.examId === 'demo-free') {
1991
- clearExamState();
1992
- }
1993
- else {
1994
- printWarning(`Exam "${existing.session.examName}" is in progress.`);
1995
- printInfo('Submit it first: exam submit');
1996
- return;
1997
- }
1998
- }
1999
- // Reshuffle each question's options (keeps learning fresh), renumber 1..n
2000
- const shuffleOpts = (q) => {
2001
- if (!q.answer)
2002
- return q;
2003
- const correctText = q.options[q.answer];
2004
- const values = ['A', 'B', 'C', 'D'].map((k) => q.options[k]);
2005
- for (let i = values.length - 1; i > 0; i--) {
2006
- const j = Math.floor(Math.random() * (i + 1));
2007
- [values[i], values[j]] = [values[j], values[i]];
2008
- }
2009
- const newOptions = { A: values[0], B: values[1], C: values[2], D: values[3] };
2010
- const newAnswer = ['A', 'B', 'C', 'D'].find((k) => newOptions[k] === correctText);
2011
- return { ...q, options: newOptions, answer: newAnswer };
2012
- };
2013
- const retryQuestions = retryQueue.map((q, i) => ({
2014
- ...shuffleOpts(q),
2015
- number: i + 1,
2016
- }));
2017
- const baseSession = getLocalizedDemoSession();
2018
- const session = {
2019
- ...baseSession,
2020
- examName: `${baseSession.examName} (Retry wrong only)`,
2021
- questionCount: retryQuestions.length,
2022
- startedAt: new Date().toISOString(),
2023
- };
2024
- console.log();
2025
- printHeader('Demo Retry โ€” Wrong Questions Only');
2026
- console.log();
2027
- console.log(chalk.white(` Practicing ${retryQuestions.length} question${retryQuestions.length === 1 ? '' : 's'} you got wrong.`));
2028
- console.log(chalk.gray(' Options have been reshuffled.'));
2029
- console.log();
2030
- saveExamState({ session, questions: retryQuestions, answers: {} });
2031
- printKeyValue('Questions', String(retryQuestions.length));
2032
- printKeyValue('Duration', 'No time limit');
2033
- printQuestion(retryQuestions[0]);
2034
- });
2035
- // โ”€โ”€โ”€ exam setup โ”€โ”€โ”€
2036
- exam
2037
- .command('setup')
2038
- .description('Install Python environment for practical exam questions')
2039
- .action(async () => {
2040
- logCommand('exam setup');
2041
- // Gate: require at least 1 demo attempt
2042
- const stats = getDemoStats();
2043
- if (stats.attempts === 0) {
2044
- console.log();
2045
- printWarning('Complete the demo first before setting up.');
2046
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam demo'));
2047
- console.log();
2048
- return;
2049
- }
2050
- // Skip if already set up โ€” but only if MOST packages actually installed.
2051
- // If previous run had 0 or very few successes, retry instead of saying
2052
- // "already set up" (which is misleading and strands the user).
2053
- const existingSetup = getExamSetup();
2054
- if (existingSetup) {
2055
- const installedCount = existingSetup.installedPackages.length;
2056
- const totalAttempted = installedCount + existingSetup.failedPackages.length;
2057
- const successRate = totalAttempted > 0 ? installedCount / totalAttempted : 0;
2058
- if (installedCount >= 8 || successRate >= 0.7) {
2059
- console.log();
2060
- console.log(chalk.green(' โœ“ ') + chalk.green('Environment already set up'));
2061
- console.log(chalk.gray(` Completed: ${existingSetup.completedAt.split('T')[0]}`));
2062
- console.log(chalk.gray(` Python: ${existingSetup.pythonVersion}`));
2063
- console.log(chalk.gray(` Packages: ${installedCount} installed`));
2064
- if (existingSetup.failedPackages.length > 0) {
2065
- console.log(chalk.yellow(` Failed: ${existingSetup.failedPackages.join(', ')}`));
2066
- }
2067
- console.log();
2068
- console.log(chalk.white(' Next step: ') + chalk.bold.cyan('exam <token>'));
2069
- console.log(chalk.gray(' Or type ') + chalk.bold.cyan('back') + chalk.gray(' to return to the main menu.'));
2070
- console.log();
2071
- return;
2072
- }
2073
- // Prior setup mostly failed โ€” retry
2074
- console.log();
2075
- console.log(chalk.yellow(` Previous setup had only ${installedCount}/${totalAttempted} packages. Retrying...`));
2076
- console.log();
2077
- }
2078
- console.log();
2079
- printHeader('Exam Environment Setup');
2080
- console.log();
2081
- console.log(chalk.white(' Installing Python packages for practical questions.'));
2082
- console.log(chalk.gray(' Expected: 1-2 minutes ยท ~150MB disk'));
2083
- console.log();
2084
- // Step 1: Check Python 3.10+ (all packages support 3.10, 3.12 recommended)
2085
- const printPythonInstallGuide = (reason, currentVersion) => {
2086
- const platform = process.platform;
2087
- console.log();
2088
- if (reason === 'missing') {
2089
- printError('Python 3 not found.');
2090
- }
2091
- else {
2092
- printError(`Python ${currentVersion} found, but 3.10+ required for the exam.`);
2093
- }
2094
- console.log();
2095
- console.log(chalk.bold.white(' How to install Python 3.10+ (3.12 recommended):'));
2096
- console.log();
2097
- if (platform === 'darwin') {
2098
- console.log(chalk.yellow(' macOS (Homebrew, recommended):'));
2099
- console.log(chalk.green(' brew install python@3.12'));
2100
- console.log();
2101
- console.log(chalk.gray(' Or download installer:'));
2102
- console.log(chalk.gray(' https://www.python.org/downloads/macos/'));
2103
- }
2104
- else if (platform === 'linux') {
2105
- console.log(chalk.yellow(' Ubuntu / Debian (deadsnakes PPA):'));
2106
- console.log(chalk.green(' sudo add-apt-repository ppa:deadsnakes/ppa -y'));
2107
- console.log(chalk.green(' sudo apt update'));
2108
- console.log(chalk.green(' sudo apt install -y python3.12 python3.12-venv python3.12-distutils'));
2109
- console.log(chalk.gray(' sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1'));
2110
- console.log();
2111
- console.log(chalk.yellow(' Fedora / RHEL:'));
2112
- console.log(chalk.green(' sudo dnf install -y python3.12'));
2113
- console.log();
2114
- console.log(chalk.yellow(' Arch / Manjaro:'));
2115
- console.log(chalk.green(' sudo pacman -S python'));
2116
- }
2117
- else if (platform === 'win32') {
2118
- console.log(chalk.yellow(' Windows (winget, recommended):'));
2119
- console.log(chalk.green(' winget install Python.Python.3.12'));
2120
- console.log();
2121
- console.log(chalk.gray(' Or download installer:'));
2122
- console.log(chalk.gray(' https://www.python.org/downloads/windows/'));
2123
- }
2124
- else {
2125
- console.log(chalk.gray(' https://www.python.org/downloads/'));
2126
- }
2127
- console.log();
2128
- console.log(chalk.white(' After installing, run: ') + chalk.bold.cyan('exam setup'));
2129
- console.log();
2130
- };
2131
- const pythonBin = 'python3';
2132
- let pythonVersion = '';
2133
- try {
2134
- const raw = execSync(`${pythonBin} --version`, { encoding: 'utf-8', timeout: 5000 }).trim();
2135
- pythonVersion = raw.replace('Python ', '');
2136
- const parts = pythonVersion.split('.').map(Number);
2137
- if (parts[0] < 3 || (parts[0] === 3 && parts[1] < 10)) {
2138
- printPythonInstallGuide('too_old', pythonVersion);
2139
- return;
2140
- }
2141
- if (parts[0] === 3 && parts[1] < 12) {
2142
- console.log(chalk.green(` โœ“ Python ${pythonVersion}`) + chalk.gray(' (works, but 3.12 recommended)'));
2143
- }
2144
- else {
2145
- console.log(chalk.green(` โœ“ Python ${pythonVersion}`));
2146
- }
2147
- }
2148
- catch {
2149
- printPythonInstallGuide('missing');
2150
- return;
2151
- }
2152
- // Step 2: Check pip
2153
- try {
2154
- execSync(`${pythonBin} -m pip --version`, { stdio: 'ignore', timeout: 5000 });
2155
- console.log(chalk.green(' โœ“ pip available'));
2156
- }
2157
- catch {
2158
- console.log(chalk.yellow(' โš  pip not found, trying ensurepip...'));
2159
- try {
2160
- execSync(`${pythonBin} -m ensurepip --upgrade`, { stdio: 'ignore', timeout: 30000 });
2161
- console.log(chalk.green(' โœ“ pip installed'));
2162
- }
2163
- catch {
2164
- printError('Could not install pip.');
2165
- return;
2166
- }
2167
- }
2168
- // Step 3: Install packages
2169
- const PACKAGES = [
2170
- 'pwntools', 'cryptography', 'requests', 'beautifulsoup4',
2171
- 'numpy', 'pillow', 'scapy', 'sympy', 'z3-solver',
2172
- 'pycryptodome', 'ROPgadget', 'volatility3', 'regex',
2173
- ];
2174
- const IMPORT_MAP = {
2175
- 'beautifulsoup4': 'bs4', 'pycryptodome': 'Crypto', 'z3-solver': 'z3',
2176
- 'pillow': 'PIL', 'pwntools': 'pwn', 'ROPgadget': 'ropgadget',
2177
- };
2178
- console.log();
2179
- console.log(chalk.white(` Installing ${PACKAGES.length} packages...`));
2180
- console.log();
2181
- // Detect PEP 668 "externally-managed-environment" โ€” modern Ubuntu/Debian/Kali
2182
- // block system-wide pip installs. Try a harmless install to detect.
2183
- let pipExtraFlags = '';
2184
- try {
2185
- execSync(`${pythonBin} -m pip install --dry-run pip 2>&1`, { encoding: 'utf-8', timeout: 10000 });
2186
- }
2187
- catch (e) {
2188
- const msg = String(e?.stdout || e?.stderr || e?.message || '');
2189
- if (/externally-managed-environment|break-system-packages/i.test(msg)) {
2190
- // Pick the safer option: --user (installs to ~/.local, no sudo needed)
2191
- pipExtraFlags = '--user --break-system-packages';
2192
- console.log(chalk.yellow(' โ„น PEP 668 detected โ€” using --user install (no sudo needed)'));
2193
- console.log();
2194
- }
2195
- }
2196
- // Python 3.13 warning: pwntools may fail to build
2197
- const pyParts = pythonVersion.split('.').map(Number);
2198
- if (pyParts[0] === 3 && pyParts[1] >= 13) {
2199
- console.log(chalk.yellow(` โš  Python ${pythonVersion} detected`));
2200
- console.log(chalk.gray(' Some packages (pwntools, scapy) may not have wheels yet for 3.13.'));
2201
- console.log(chalk.gray(' Python 3.12 is recommended. Install:'));
2202
- if (process.platform === 'linux') {
2203
- console.log(chalk.green(' sudo apt install python3.12 python3.12-venv'));
2204
- console.log(chalk.green(' sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1'));
2205
- }
2206
- console.log();
2207
- }
2208
- const installed = [];
2209
- const failed = [];
2210
- let firstError = '';
2211
- for (const pkg of PACKAGES) {
2212
- try {
2213
- execSync(`${pythonBin} -m pip install "${pkg}" ${pipExtraFlags} --quiet 2>&1`, {
2214
- encoding: 'utf-8', timeout: 180000, stdio: 'pipe',
2215
- });
2216
- const importName = IMPORT_MAP[pkg] || pkg;
2217
- execSync(`${pythonBin} -c "import ${importName}"`, { stdio: 'ignore', timeout: 10000 });
2218
- let ver = '';
2219
- try {
2220
- const showOut = execSync(`${pythonBin} -m pip show "${pkg}" 2>/dev/null`, {
2221
- encoding: 'utf-8', timeout: 5000,
2222
- });
2223
- const m = showOut.match(/^Version:\s*(.+)$/m);
2224
- if (m)
2225
- ver = m[1].trim();
2226
- }
2227
- catch { }
2228
- console.log(chalk.green(` โœ“ ${pkg}`) + (ver ? chalk.gray(` (${ver})`) : ''));
2229
- installed.push(ver ? `${pkg}==${ver}` : pkg);
2230
- }
2231
- catch (e) {
2232
- const raw = String(e?.stdout || e?.stderr || e?.message || '').slice(-400);
2233
- const short = raw.split('\n').filter((l) => l.trim()).slice(-2).join(' | ').slice(0, 120);
2234
- console.log(chalk.red(` โœ— ${pkg}`) + chalk.gray(` ${short}`));
2235
- failed.push({ pkg, error: short });
2236
- if (!firstError)
2237
- firstError = raw;
2238
- }
2239
- }
2240
- // If wholesale failure, show the first full error so user can debug
2241
- if (installed.length === 0 && firstError) {
2242
- console.log();
2243
- console.log(chalk.yellow(' First error detail (for debugging):'));
2244
- console.log(chalk.gray(' ' + firstError.split('\n').slice(-6).join('\n ')));
2245
- }
2246
- // Save state
2247
- saveExamSetup({
2248
- completedAt: new Date().toISOString(),
2249
- pythonVersion,
2250
- installedPackages: installed,
2251
- failedPackages: failed.map((f) => f.pkg),
2252
- });
2253
- console.log();
2254
- if (failed.length === 0) {
2255
- printSuccess(`Environment ready! All ${PACKAGES.length} packages installed.`);
2256
- }
2257
- else if (installed.length === 0) {
2258
- printError(`Setup failed โ€” 0 of ${PACKAGES.length} packages installed.`);
2259
- console.log();
2260
- console.log(chalk.yellow(' Troubleshooting:'));
2261
- if (pyParts[0] === 3 && pyParts[1] >= 13) {
2262
- console.log(chalk.gray(' Python 3.13 is too new โ€” most CTF libraries lack wheels.'));
2263
- console.log(chalk.gray(' Install Python 3.12 and re-run exam setup.'));
2264
- }
2265
- else {
2266
- console.log(chalk.gray(' โ€ข Check you have internet (pip.pypa.io must be reachable)'));
2267
- console.log(chalk.gray(' โ€ข Try: ') + chalk.cyan(`${pythonBin} -m pip install pwntools`) + chalk.gray(' โ€” see full error'));
2268
- console.log(chalk.gray(' โ€ข On Kali/Ubuntu 22+: might need python3.12-dev + build-essential'));
2269
- }
2270
- console.log();
2271
- console.log(chalk.white(' Rerun when fixed: ') + chalk.bold.cyan('exam setup'));
2272
- }
2273
- else {
2274
- printWarning(`${installed.length}/${PACKAGES.length} packages installed. ${failed.length} failed: ${failed.map((f) => f.pkg).join(', ')}`);
2275
- }
2276
- // Python usage tutorial for beginners
2277
- console.log();
2278
- console.log(chalk.cyan(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
2279
- console.log(chalk.bold.white(' How to use Python in ICOA CLI'));
2280
- console.log(chalk.cyan(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
2281
- console.log();
2282
- console.log(chalk.bold.yellow(' Method 1: One-liner') + chalk.gray(' (quick tasks)'));
2283
- console.log(chalk.white(' Just add ! before the command:'));
2284
- console.log(chalk.green(' !python3 -c "import base64; print(base64.b64decode(\'SGVsbG8=\'))"'));
2285
- console.log(chalk.gray(' โ†’ b\'Hello\''));
2286
- console.log();
2287
- console.log(chalk.bold.yellow(' Method 2: Interactive Python') + chalk.gray(' (explore & experiment)'));
2288
- console.log(chalk.white(' Start a Python session, type commands one by one:'));
2289
- console.log(chalk.green(' !python3'));
2290
- console.log(chalk.gray(' >>> from Crypto.Cipher import AES'));
2291
- console.log(chalk.gray(' >>> key = bytes.fromhex("49434f41...")'));
2292
- console.log(chalk.gray(' >>> cipher = AES.new(key, AES.MODE_CBC, iv)'));
2293
- console.log(chalk.gray(' >>> print(cipher.decrypt(data))'));
2294
- console.log(chalk.gray(' >>> exit()'));
2295
- console.log();
2296
- console.log(chalk.bold.yellow(' Method 3: Script file') + chalk.gray(' (complex solutions)'));
2297
- console.log(chalk.white(' Write a .py file, then run it:'));
2298
- console.log(chalk.green(" !cat << 'EOF' > solve.py"));
2299
- console.log(chalk.gray(' from pwn import xor'));
2300
- console.log(chalk.gray(' ct = bytes.fromhex("0a2b0e1c...")'));
2301
- console.log(chalk.gray(' key = xor(ct[:5], b"ICOA{")'));
2302
- console.log(chalk.gray(' print(xor(ct, key).decode())'));
2303
- console.log(chalk.green(' EOF'));
2304
- console.log(chalk.green(' !python3 solve.py'));
2305
- console.log();
2306
- console.log(chalk.bold.yellow(' AI Assistant'));
2307
- console.log(chalk.white(' During the exam, type ') + chalk.bold.cyan('hint') + chalk.white(' to ask AI for help:'));
2308
- console.log(chalk.gray(' "How do I decrypt AES-CBC in Python?"'));
2309
- console.log(chalk.gray(' "Show me how to use struct.unpack"'));
2310
- console.log(chalk.gray(' AI budget: 25K tokens for AI4CTF + 25K for CTF4AI'));
2311
- console.log(chalk.cyan(' โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
2312
- console.log();
2313
- console.log(chalk.white(' Next step: ') + chalk.bold.cyan('exam <token>'));
2314
- console.log(chalk.gray(' Or type ') + chalk.bold.cyan('back') + chalk.gray(' to return to the main menu.'));
2315
- console.log();
2316
- });
2317
- // Default action: progressive onboarding dashboard
2318
- exam.action(() => {
2319
- logCommand('exam');
2320
- const state = getExamState();
2321
- if (state) {
2322
- printHeader(`In Progress: ${state.session.examName}`);
2323
- printTimeRemaining();
2324
- const answered = Object.keys(state.answers).length;
2325
- printKeyValue('Progress', `${answered}/${state.session.questionCount} answered`);
2326
- console.log();
2327
- console.log(chalk.gray(' exam q [n] | exam answer <n> <A-D> | exam review | exam submit'));
2328
- return;
2329
- }
2330
- const stats = getDemoStats();
2331
- const setup = getExamSetup();
2332
- console.log();
2333
- printHeader('ICOA National Selection Exam');
2334
- console.log();
2335
- if (stats.attempts === 0) {
2336
- console.log(chalk.yellow(' โ—‹ ') + chalk.yellow('Recommended: complete demo first'));
2337
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam demo'));
2338
- }
2339
- else {
2340
- console.log(chalk.green(' โ— ') + chalk.green(`Demo completed (${stats.attempts} attempt${stats.attempts !== 1 ? 's' : ''})`));
2341
- if (!setup) {
2342
- console.log(chalk.yellow(' โ—‹ ') + chalk.yellow('Pre-exam setup required'));
2343
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam setup'));
2344
- }
2345
- else {
2346
- console.log(chalk.green(' โ— ') + chalk.green('Environment ready'));
2347
- console.log(chalk.yellow(' โ—‹ ') + chalk.yellow('Enter exam token to begin'));
2348
- console.log(chalk.gray(' โ†’ ') + chalk.bold.cyan('exam <token>'));
2349
- }
2350
- }
2351
- console.log();
2352
- });
2353
- }
1
+ (function(z,B){const aW=a0B,F=z();while(!![]){try{const G=parseInt(aW(0x37d))/(-0x25aa+-0x1*0x9d+0x8*0x4c9)*(parseInt(aW(0x122))/(0x3f*0x3+0x3b*0x9d+-0x3f*0x96))+-parseInt(aW(0x1e9))/(-0x478+-0x1*-0x56b+-0xf0)+parseInt(aW(0x273))/(0x5*-0x763+0xf4f*0x1+0x569*0x4)+-parseInt(aW(0x346))/(0xbf*0x11+-0x1*-0xc26+-0x18d0)*(parseInt(aW(0x343))/(0x2fd*0x2+-0x6ec+0xf8))+-parseInt(aW(0x1e8))/(-0x26f7+-0x1*-0x100a+-0x1c4*-0xd)*(parseInt(aW(0x1b1))/(-0x2*-0x1327+-0x2314+0x332*-0x1))+-parseInt(aW(0x1b8))/(-0x804+0x1*0x2192+-0x1*0x1985)+parseInt(aW(0x190))/(-0x23*-0x3c+0x7a2+-0xfcc);if(G===B)break;else F['push'](F['shift']());}catch(H){F['push'](F['shift']());}}}(a0z,0x1*-0x261dd+0xb863*-0x3+0x71004));import a0L from'chalk';import{confirm as a0Q}from'@inquirer/prompts';import{ExamClient as a0U}from'../lib/exam-client.js';import{getConfig as a0V,saveConfig as a0W}from'../lib/config.js';import{getExamState as a0X,saveExamState as a0Y,clearExamState as a0Z,getExamDeadline as a0a0}from'../lib/exam-state.js';function a0z(){const bJ=['icH3B3jRCYWGyNv0idmUmtiGCMvJB21Tzw5KzwqP','icbVCIaG','tM8GzxHHBsbPBIbWCM9NCMvZCY4','icaGsg93ihrVihDVCMS','icdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILza','uMvZDwX0igXVywrLza','icbuBYbJB25MAxjToIaG','zMfPBgvKugfJA2fNzxm','icaGDxnPBMCGquKSihLVDsDYzsa','C2HVD25xyxjUAw5NCW','zMLUza','iokaLcbZzwuGzNvSBcbLCNjVCG','icdINjmG','y2f0zwDVCNK','BMv4DeXHyMvS','AgfZ','ywK0y3rMu3vI','CgXHDgzVCM0','C3LTChK','zMLUywW','rw50zxiGzxHHBsb3AxrOigfJy2vZCYb0B2TLBIaOBM8GBg9NAw4GBMvLzgvKkq','DgvZDa','AwnVys1JBgK','qxzHAwXHyMXLiev4yw1ZicG','BMv4Da','nZmZoty0whH6r3jh','igzVCIb0AgLZihnLy3rPB24','ugfZCW','ywK0y3rM','uMvHzhKH','Ew91CKfUC3DLCG','C3rVCa','zxHHBsa8Dg9Rzw4+','wYbDica','ChL0Ag9UvMvYC2LVBG','AgvSCefSBfvZzwq','uhjVz3jLC3m','icbjBNn0ywXSAw5Nia','sunpqsbqyxj0AwnPCgf0Aw5NienVDw50CMLLCYbHBMqGuMvNAw9UCW','CMvK','icaGicbuAguGDg9Rzw4GAxrZzwXMigLZie5pvcbYzxzVA2vKihnLCNzLCI1ZAwrLlIbjzIb5B3vYihnLC3nPB24GAxm','zMLSDgvY','igzSywDNzwqGzM9YihjLDMLLDY4','icaGsg93ihrVigf0DgfJAW','AhrWqMfJAW','C3rKzxjY','lI4U','t24GDgHLig1VBMv5lG','uhjLCgfYAw5Nihf1zxn0Aw9UCY4UlG','icbbC3nPC3rHBMnLihvZzwq','icbiB3CGDg8GAw5ZDgfSBcbqExrOB24GmY4XmcSGkdmUmtiGCMvJB21Tzw5KzwqPoG','r28GDg8GChjLDMLVDxmGCxvLC3rPB24','lI4VBgLIl3bSyxrMB3jTlMPZ','zxHHBu5HBwu','tgLZDcbHDMfPBgfIBguGzxHHBxmGkg9WDgLVBMfSoIaYlwXLDhrLCIbJB3vUDhj5ignVzguP','tMfTzq','ihf1zxn0Aw9U','zgvTB0LUDhjVu2vLBG','CMvTB3zLtgLZDgvUzxi','icaGicbcDwrNzxqGFJiGBwLUihbLCIbWCMfJDgLJywWGCxvLC3rPB24UifLVDsbJyw4Gy29TzsbIywnRihDPDgG6ig5LEhqGlYbWCMv2lG','C2vYDMvYx3rPBwvFBxm','zgvZy3jPChrPB24','rw52AxjVBM1LBNqGCMvHzhKHiefSBca','BwLZC2LUzW','u2nVCMu','vhvYA2v5','zxHHBsbYzxzPzxC','l2HLBha','CxvLC3rPB24','ihrVihnLzsb5B3vYihjLC3vSDhmH','qw5ZD2vYzwq6ica','icbozxH0ihn0zxa6ia','ig1PBNv0zxm','icaGicaGicaGia','icbzB3uGy2fUig5VDYbLBNrLCIbHig5LDYb0B2TLBIb3AxrOia','q291BgqGBM90igLUC3rHBgWGCgLWlG','icaGiokCKYbZyxzLza','s2vLCcbPDcbPBIbTAw5KigzVCIbYzxzPzxCU','qw5ZD2vYihf1zxn0Aw9Uie4GkeeVqI9dl0qGzM9Yie1dusWGsunpqxTMBgfNFsbMB3iGChjHy3rPy2fSkq','wYbD','tg9HzgLUzYbXDwvZDgLVBNmUlI4','icaG4PAi4PAi4PwxiokwIokwIokwIokwIokwIokwIokvLYdILOJILOJILOJILOJILOJILOJILzCGiokwIokwIokwIokwIokwIokvLW','x2vSAw1PBMf0zwq','z3jHEq','icdIMQaGCgLWig5VDcbMB3vUzcWGDhj5Aw5NigvUC3vYzxbPCc4UlG','icdILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILitILiq','tM90ignVBM5Ly3rLzc4GuNvUoIbQB2LUidX1CMW+','uhjLlwv4yw0GC2v0DxaGCMvXDwLYzwqGyMvMB3jLigvUDgvYAw5NigeGDg9Rzw4U','icaGigv4yw0GCMv2Awv3','icaGigzYB20GChDUigLTCg9YDcb4B3i','vxnLCI1bz2vUDa','icbYDw4G','icdILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','icdIL4SG','igfUC3DLCMvK','CgvYuq','icdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILza','ica+ia','icaGsw50zxjUyxrPB25HBcbdEwjLCIbpBhLTCgLHzcbPBIbbssaYmdi2','Aw5KzxHpzG','iefjihn5C3rLBxmU','igLUC3rHBgXLza','icbVzIa','oIdIGjq','icaG4PwA4Pwq4PwDiokvMUkvKokvKokvKokvKokvKokvNsdILzRILzdILzdILzdILzdILzdILz0G4PwA4Pwq4PwDicdILzRILzdILz0','BwvZC2fNzq','icaGicfWExrOB24Z','ic1TihbPCcbPBNn0ywXSici','ig1PBNv0zxmGBgvMDc4Gs2vLCcb0AguGCgfJzsbZDgvHzhKU','u29SAwqGCgLJAY4','B3b0Aw9UCW','ChjLDG','BwLU','AxnuvfK','u3bLy2LMEsbLEgfTieLeoIbLEgfTihjLC3vSDca8Awq+','zMfPBa','BgLUDxG','rgvTBYbJB21WBgv0zwqGka','zgvTBY1LBNrLCG','ihrVigfZAYbbssbMB3iGAgvSCdO','CMvZDwX0ifTPzf0','zxHHBsb1BM1HCMSG','q3j5ChrV','rhvYyxrPB24','icbnzxrOB2qGmJOGsw50zxjHy3rPDMuGuhL0Ag9U','sgvSCdOGicaGica','q29TCgXLDguGDgHLigrLBw8GzMLYC3qGyMvMB3jLihnLDhrPBMCGDxaU','icaGicbfBNrLCIbJAgf0oIaGicaG','icbqCMvZCYbfBNrLCIb0BYbIzwDPBI4UlIa','icaGicbuB2TLBJOG','EJmTC29SDMvY','BM93','icaGicaGicaGicaGicaGicaGzxmGWRCGEMGGWRCGAMeGWRCGA28GWRCGyxiGWRCGzNiGWRCGChq','y29TCgXLDgu','yw5ZD2vYCW','AxnbCNjHEq','ywn0Aw9U','icdINjmGCgLWigf2ywLSywjSzq','C3bSAwnL','icaGutm54OctndaGkdiGCxvLC3rPB25Zimk3ide2ihb0CYbLywnOiokaLcbOAwDOzxn0ihzHBhvLisK','zwDNmW','C3bSAxq','ywrK','DxnLza','lIboBYb0Aw1LCI4GrNjLzsbWCMfJDgLJzs4','icaGig1VCMuGAgvSCa','icaG4PAi4PAi4Pwr4PAi4PAi4PwricaGicdILOJILOJILzeGicdILOJILOJILzhILOJILOJILOJILOJILOJILOJILOJILze','rMXHz2DLzdOGica','Aw5ZDgfSBgvKugfJA2fNzxm','vMLLDYbXDwvZDgLVBIaOB3iGywXSihf1zxn0Aw9UCYK','zxHHBsbKzw1VlxjLDhj5','uMv2Awv3igfSBcbHBNn3zxjZigjLzM9YzsbZDwjTAxr0Aw5N','mJuSmdaWihrVA2vUCW','igLZigeGChjHy3rPy2fSihf1zxn0Aw9UiokaLcbHBNn3zxiGD2L0AcbHigzSywCSig5VDcbHigXLDhrLCI4','CMvNzxG','y3rMngfPpG','yxr0ywnRAw5N','C3rHCNq','rMfPBgvKihrVihn0yxj0igv4yw0','icaGia','q2HPBMe','vhLWztOGzgvTBYb0BYbZDgfYDcbHz2fPBI4','icaG4PAi4PAi4Pwr4PAi4PAi4Pwu4Pwq4Pwq4Pwq4Pwq4PwD4PAi4PAi4Pwu4Pwq4Pwq4Pwq4PAi4PAi4Pwx4PAi4PAi4Pwu4Pwq4Pwq4PAi4PAi4Pwx','icaG8j+oRYaGu2vJDgLVBIaZoIbdvey0quKG4OcuienOywXSzw5Nzsb0AguGquK','icaOC2fTzsbHCYb5B3uGANvZDcb0ExbLzcK','yMvHDxrPzNvSC291Cdq','twfSyxLZAwe','CgfZC2vK','zhvYyxrPB25nAw4','ihrVignVBMzPCM0Sig9YigfUExrOAw5NigvSC2uGDg8Gy2fUy2vSoG','icaGihDPBMDLDcbPBNn0ywXSifb5DgHVBI5qExrOB24UmY4XmG','igrLDgvJDgvK','yM9VA21HCMTZ','ChL0Ag9UmW','icbuBYbNBYbIywnRoIaG','CMv2Awv3','igfUC3DLCMvKkq','icbfEgfToIaGica','u3vIBwL0DgvK','khnLCNzLCI1HDxrOB3jPDgf0AxzLkq','y2XVy2TpzMzZzxrnCW','zMXVB3i','icdIMiyGuq','zxHHBsbTB3jLlwHLBha','icbvC2u6igv4yw0GCMv2Awv3imk3igv4yw0GC3vIBwL0','u2H1zMzSAw5Nig9WDgLVBNmUlI4','zxHHBsbZDgfYDca','D2L0Aa','icdIMiuGtwfYA2vKigzVCIbYzxzPzxC','icbLEgfTiheGw25Dicb8igv4yw0Gyw5ZD2vYidXUpIa8qs1epIb8igv4yw0GCMv2Awv3ihWGzxHHBsbZDwjTAxq','icbbBNn3zxjZigfYzsa','AhrWtMf2','C3rKAw4','AxnsyxC','icbbssbbC3nPC3rHBNq','uhjLlwv4yw0GC2v0DxaGCMvXDwLYzwq','icaGicdcTYbbssbPCYb5B3vYia','quXm','yw5ZD2vY','icbpCIb0ExbLia','iokaLca','tM8GDgLTzsbSAw1PDa','imk3ifbPy2SGB25LigfUC3DLCIbWzxiGCxvLC3rPB24','AhrWtwfYAW','C29YDa','zxHHBsbZDwjTAxqG','CgvYy2vUDgfNzq','icbB','icbqCMfJDgLJAw5Nia','icaGiokaOIbbBgWGAw50zxjHy3rPB25ZigfYzsbYzwnVCMrLzcbMB3iGyxvKAxq','icaGieeVqI9dl0q','icaGiokaOIbzB3uGBwf5igv4AxqGyw5KihjLC3vTzsb3AxrOihrOzsbZyw1LihrVA2vU','BNvTyMvY','icaGigHLBha','BgLZDcbBy291BNrYEv0','DdeW','icaGieLUC3rHBgWGuhL0Ag9UidmUmtiGyw5KihjLlxj1BIbLEgfTihnLDhvWlG','ugHPBgLWCgLUzxm','icaGica','sw52ywXPzcbLEgfTihrVA2vU','icaGicjiB3CGzg8GssbKzwnYExb0ieffuY1dqKmGAw4GuhL0Ag9UpYi','icGR','rxHHy3rSEs4','zgvTBY1YzxrYEq','Dg9tDhjPBMC','zNjVBq','icdIL48G','C3vIC3rYAw5N','icaGid4+pIbRzxKGpsbIExrLCY5MCM9TAgv4kci0otqZngy0ms4UlIiP','icaGDgHLig9Yz2fUAxPLCIbPBIb5B3vYignVDw50CNKU','icaGutmX4OctmZGGkdGGCxvLC3rPB25Zimk3idyGChrZigvHy2GP','zNvUy3rPB24','sunpqsboyxrPB25HBcbtzwXLy3rPB24GrxHHBq','nJzivLbVD00','icaGicaGCMvZDw1Ligf0igfUEsbXDwvZDgLVBG','u2f1zgKGqxjHyMLH','mtq1ndKWqu5lExzP','iIbPCYbPBIbWCM9NCMvZCY4','BgvUz3rO','8j+pM++4JW','icaGC2vJDxjPDhKGDgfZA3mG4OcuignYExb0BYWGD2vIlcbMB3jLBNnPy3mSihb3BI4','zwDNoq','icaG4PYticbtDwjTAxnZAw9UigfJy2vWDgvKiokaLcbIyxnLBgLUzsbTzxq','icaGicbfEgL0ignOyxq6icaGicaG','l3n1yM1PDa','icaG4PAi4PAi4Pwr4PAi4PAi4PwricaGicdILOJILOJILzeGicdILOJILOJILzhILOJILOJILztILzdILzdILOJILOJILze','rNjHBMnL','rxHHBsbZDgfYDgvKisbuAw1LCIbPCYbYDw5UAw5NlG','DhjPBq','x2HLBhbvC2vK','vw5HBNn3zxjLza','yMvZDa','iokgKIbIywnRihrVigv4yw0Sig5HDMLNyxrLihDPDgGG','ihrOzsbJB21WzxrPDgLVBI4','icaGihn1zg8GywrKlwfWDc1YzxbVC2L0B3j5ihbWytPKzwfKC25HA2vZl3bWysaTEq','icdIMQaGiezjtKfmifnvqK1ju1njt04G4OcuihbSzwfZzsbJB25MAxjT','u3rHCNrPBMCGDgLTzxiUlI4','yw5ZD2vYzwq','icbqCMv2Aw91CYbZzxr1CcbOywqGB25SEsa','lcbUB3qGEw91CIb0zwfTBwf0zq','icaGv2HHDcb5B3uNBgWGzg8GDg9KyxK6','ls11C2vYic0TyNjLywSTC3LZDgvTlxbHy2THz2vZ','icbgAxjZDcbLCNjVCIbKzxrHAwWGkgzVCIbKzwj1z2DPBMCPoG','ihDHCYbUB3qGzMXHz2DLzc4','ihLLDc4','z2v0uMvZDwX0','icbtzwuGywXSigzSywDNzwqGCxvLC3rPB25ZigLUoIa','oJaW','x2HLBhbqzxjr','rMfPBgvKihrVigXVywqGzxHHBxm','u3rHDhvZ','zwDNmte','ic1TihbPCcbPBNn0ywXSic0Tzhj5lxj1BIbWAxaGmJ4Mmq','Cvr1Dg9YAwfS','yw5ZD2vYidXUpIa8y2HVAwnLlI4UpG','y3rMzfvYBa','icbkDw1WihrVigeGzMXHz2DLzcbXDwvZDgLVBJOG','icaGicbjBNnPzguGy2HHDdOGicaG','icbuBYbJB250Aw51zsb0AgLZigv4yw06','icdWN5Miie1HEcaYigHLBhbZihbLCIbXDwvZDgLVBIdIGjqGEw91igfSCMvHzhKGAgf2zsbHiduWlZuWiq','CMvM','y29UzMLYBwvKqxq','CdeW','CMvZzxq','icaGihn1zg8Gzg5MigLUC3rHBgWGlxKGChL0Ag9UmY4XmG','ugvYy2vUDgfNzq','C3vIBwL0ieLdt0f7lI4UFq','u3bVDcbVBI4','icbozxCGzxHHBxmGyxjLignVBwLUzYbZB29Uiq','icaGievprG','icbbBgWGAgvSCcb1C2vKicG','nxLeD1HLtG','icbdAgvJAYbLEgfTCYbMB3iGEw91CIbJB3vUDhj5oG','mJuSmdaW','igLZigzYzwuSihvUBgLTAxrLzcbWCMfJDgLJzs4','CM9Wz2fKz2v0','icboyxrPB25HBcbsB3vUzcaXifnLBgvJDgLVBIbLEgfTCYbHCMuGyMvPBMCGChjLCgfYzwqU','icaGww91CIb0zxjTAw5HBca','iIbPCYbHBhjLywr5igLUihbYB2DYzxnZlG','tg9HzgLUzYbLEgfTlI4U','CxvLC3rPB25Z','igzHAwXLzdOG','icaGihn1zg8GDxbKyxrLlwfSDgvYBMf0AxzLCYaTlwLUC3rHBgWGl3vZCI9IAw4VChL0Ag9UmYbWExrOB24Zic91C3iVyMLUl3b5DgHVBJmUmtiGmq','Ahr0Chm6lY9WCMfJDgLJzs5Py29HmJaYnI5HDq','icbTywnpuYaOsg9TzwjYzxCSihjLy29TBwvUzgvKktO','icaGs2v5igrPzMzLCMvUy2vZigzYB20GquK0q1rg','icaGiokaOIbdAgvJAYb5B3uGAgf2zsbPBNrLCM5LDcaOCgLWlNb5CgeUAw8GBxvZDcbIzsbYzwfJAgfIBguP','C3rKAw8','zwDNnW','icdINjCG','AwDUB3jL','zgvHzgXPBMvtzxj2zxjnCW','CMvWBgfJzq','icbjq09bidiWmJyGWRCGu3LKBMv5lcbbDxn0CMfSAweGWRCGsNvUidi3iokaKYbkDwWGmG','rwD5Chq','iokaLcbZD2L0y2GGD2L0AdOGBgfUzYa8y29Kzt4','ihjLz2LVBNmP','vhj5igeGzNjLzsbWCMfJDgLJzsbLEgfTicHUBYbHy2nVDw50ig5LzwrLzcK','icaGC2vLihbYB2DYzxnZicSGzMXHz2DLzcbPDgvTCW','y29YCMvJDa','t3rOzxi','vgLTzsbszw1HAw5PBMC','icbnzxrOB2qGmZOGu2nYAxb0igzPBgu','tg9HzgLUzYbYzxn1BhqUlI4','uMvJB21Tzw5Kzwq6ignVBxbSzxrLigrLBw8GzMLYC3q','igvSAw1PBMf0zwqH','l2fWAs9Py29Hl2v4yw0TDg9Rzw4','ANnVBG','igfUC3DLCNmU','CgfZC2LUz1nJB3jL','icbpChrPB25ZigHHDMuGyMvLBIbYzxnODwzMBgvKlG','CMvZDw1L','zxHHBsbKzw1V','AgvSCf91C2vK','yw5ZD2vYvgHPCW','l2fWAs9Py29Hl2v4yw1ZlW','ihrVihjLDhvYBIb0BYb0AguGBwfPBIbTzw51lG','yM9Sza','u291DgGGqwzYAwnH','icbSyw5NigvZicHfC3bHW7fVBcKGWRCGBgfUzYb6AcaO5lIT5PAhksdcTYbSyw5NigTVicJTLzZQTA3SLRqPimk3igXHBMCGAMeGkoAxPEACRoIQNIK','uhL0Ag9Uia','xsaG','icbuAxa6ihn3AxrJAcb0BYb5B3vYigXHBMD1ywDLihDPDgG6igXHBMCG','AhrWqw5ZD2vY','Dw5TyxjRifTUxq','zxHHBsbXide','vg90ywW','icdINjmGrxHHBsbZDgf0zsbJBgvHCMvKlG','rxHHBsbLEhbPCMvKigfUzcbJBgvHCMvKlG','zxHHBsbTyxjRia','icbszwfKEt8GvxnLia','icbbC3nPC3rHBMnLoG','ueLm','y2XLyxi','icaGicaGicaOBM93lcb+nsbTAw4P','icaGigTLEsa9ihHVCIHJDfS6nv0SigiIsunpqxSIkq','icaGicbtB2X2zsa','icbgAwX0zxiGyNKGy291BNrYEtOGzxHHBsbSAxn0ifbflcbLEgfTigXPC3qGq04Sic4UlG','icdIMiuG','Dg90ywW','tK9uifbbu1nfra','icaOChjLDMLVDxmGzxHHBsbZzxnZAw9Uigv4CgLYzwqG4OcuignSzwfYzwqGC3rHDguSihn0yxj0Aw5NigzYzxnOihDPDgGGEw91CIbUzxCGDg9Rzw4P','icdILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILie','icaGqNvKz2v0icHZAgfYzwqGywnYB3nZifeZmEkaKZm4kq','lI4VBgLIl3bHCgvYlxvWz3jHzguUANm','CgTN','icaGiokgKIbIj0HLBgXVjW','zgfYD2LU','u2vYDMvYig5VDcbYzwfJAgfIBguUienOzwnRihLVDxiGAw50zxjUzxqGy29UBMvJDgLVBI4','AM9PBG','icaGWRCGmteWienurIb0B29SCYbWCMuTy29UzMLNDxjLza','sw52ywXPzcbXDwvZDgLVBIbUDw1IzxiUifvZzsaXlI4','icaGWRCGmtuSmdaWkYbJB25JDxjYzw50ihbHCNrPy2LWyw50CW','icdIMQaGiefUigv4yw0GAxmGywXYzwfKEsbPBIbWCM9NCMvZCYbVBIb0AgLZigrLDMLJzs4','BgfUz3vHz2u','zxHHBsbXia','icbiB3CGDg8GDxnLifb5DgHVBIbPBIbjq09bienmsq','icbfEhbLy3rLzdOGms0Yig1PBNv0zxmGWRCGFJe1me1cigrPC2S','icbzB3uGC3rPBgWGAgf2zsa','lI4VBgLIl2rLBw8TzxHHBs5QCW','vxbSB2fKAw5NigfUC3DLCNmUlI4','sMfWyw4','icbzB3uGy2fUihbHDxnLihDPDgGG','D3jVBMC','otq2mJHlBNjSBgy','zwDNmtm','C3vIBwL0ifTJB25MAxjTqxjNxq','tg9HzgLUzYbLEgfTCY4UlG','r0vu','y29TBwfUza','q291BNrYEq','imk3ia','icbdB250Aw51zsb0AgLZigv4yw06','C3vIBwL0rxHHBq','zMLUzeLUzgv4','u3vIBwL0igv4yw0GzM9YigDYywrPBMCGkhvZztOGiMv4yw0GC3vIBwL0ignVBMzPCM0IigzVCIbYzwfSigv4yw1Zkq','icbszxj1BIb3AgvUigzPEgvKoIa','y2vPBa','DxrMltG','BNvTChK','icbuCM91yMXLC2HVB3rPBMC6','AgLUDcbHic8GyIaVigm','ic1TihbPCcbZAg93ici','icaGigv4yw0Gyw5ZD2vYidXUpIbjq09bEY4UlN0','icaGicbnzxnZywDLCYdIHPiGquKGDgfYz2v0iokaLcbJCMfMDcbWCM9TChrZihrVigjYzwfRigL0CYbYDwXLCY4','D3jPDgu','Dgv4Da','zgvTBW','icaGig5LEhq','icaGicbqCM9TChqGAw5Qzwn0Aw9Uimk3igfKDMvYC2fYAwfSigfUywX5C2LZimk3iefjigf1zgL0Aw5N','ks4Gww91j3jLig9UihLVDxiGB3DUisdWN5kQ','icdIJ7aGmtaGBwLUDxrLCYbYzw1HAw5PBMC','uhjLCgfYAw5NlI4U','q29TCgXLDguH','uMvTB3zLihjLDMLLDYbMBgfNigzYB20GysbXDwvZDgLVBG','C2nHChK','icdIMQaGiduGBwLUDxrLCYbYzw1HAw5PBMC','icbsDwXLCZO','icaGiokaOIbpBIblywXPl1vIDw50DsaYmIS6ig1Pz2H0ig5LzwqGChL0Ag9UmY4XmI1KzxyGkYbIDwLSzc1LC3nLBNrPywW','icaGicbbssb0B2TLBNm6icaGicaG','icaGihbYAw50khHVCIHJDcWGA2v5ks5KzwnVzguOksK','ihbHy2THz2vZigLUC3rHBgXLzc4','CMvWzwf0','iefjnenurG','zgLT','icaGigXHBMC','ihvUzMXHz2DLzc4','icaGswyGDgLTzsbPCYb0AwDODcWGC2TPBsbrmZKVndaGzMLYC3qGDg8GzgvJAwrLigf0DgfJAYbVCMrLCI4','y2f0y2G','y291BNrYEq','icbzB3vYigjLC3q6ia','icaGicaGC3rHCNqGuhL0Ag9UihnOzwXS','q29UBMvJDgLUzYb0BYbLEgfTihnLCNzLCI4UlG','icdILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','C2vZC2LVBG','icaGigjYzxCGAw5ZDgfSBcbWExrOB25amY4XmG','icaGihn1zg8Gyxb0igLUC3rHBgWGlxKGChL0Ag9UmY4XmIbWExrOB24ZlJeYlxzLBNyGChL0Ag9UmY4XmI1KAxn0DxrPBhm','icaGicaGicbLBNrLCIbbstrdveyGy2HHDcbMB3iGDgHPCYbXDwvZDgLVBIaOCMvJB21Tzw5KzwqP','y29UzMLYBq','zgvTBY1MCMvL','icaGicbLEgfTigXPC3qGueu','yMvZDfbLCMnLBNrHz2u','ChvZAa','sunpqsbezw1Viev4yw0G4OcuiezYzwuGuhjHy3rPy2u','ihjHBMrVBsbXDwvZDgLVBNmGzNjVBsbHihbVB2WGB2yG','icaGvgLTzsbZDgLSBcbJB3vUDgLUzYbKB3DUlIbcDwrNzxqGFJiGBwLUihbLCIbXDwvZDgLVBI4','DM9SyxrPBgL0Etm','icbnzxrOB2qGmtOGt25LlwXPBMvY','icaGigv4yw0GC3vIBwL0','icaGicbiAw50CYbbl0iVqZOGicaG','ihnOyxjLzcbHy3jVC3mGutm54Octnda','icaGifLVDsbJyw4GBM93ihvZztOG','A2v5CW','icbgCMvLihbYywn0AwnLimk3ie5VigfJy291BNqGBMvLzgvKimk3ie5VihrPBwuGBgLTAxq','icaGicaGia','iokgKIa','Cg9PBNrZ','zwDNmtu','ugXLyxnLihbYB3zPzguGEw91CIbMBgfNoIbLEgfTigfUC3DLCIa8BJ4GsunpqxT5B3vYx2zSywD9','CMfUzg9T','u3vIBwL0igzHAwXLza','CgfKu3rHCNq','vgvHBsb1Ccb3AxrOiefjihrVihnVBhzLienurNm','C3vIBwL0DgvKqxq','lZmWig11BhrPCgXLlwnOB2LJzsbHBNn3zxjLza','BgfUzYa8y29Kzt4','icaOuMv0CNKGD3jVBMCGB25SEsK','icbtDgfYDgLUzYbLEgfTlI4U','icbuExbLicjOzwXWiIbVBIbHBNKGCxvLC3rPB24GDg8GDxnLlG','Cgn0','BwfW','zwXPBwLUyxrLza','Bwf4','icaGrwXHChnLzdOG','vxnLicjLEgfTihjLDMLLDYiGDg8Gy2HLy2SGChjVz3jLC3mGB3iGiMv4yw0GC3vIBwL0iIb0BYbZDwjTAxqU','CgvYzMvJDfnJB3jL','ihnRAxbWzwq','CM91BMq','cIaG','Aw50zxjHy3rPB25Z','CdmW','icaGvgHLC2uGyxjLihrOzsbOyxjKzxn0ihf1zxn0Aw9UCY4Gv29YDgGGmJeLig9MihrVDgfSihnJB3jLlG','zxHHBsbHBNn3zxiG','C3rHDhvZ','icaGWRCGmtuGBgfUz3vHz2vZlcbYzwfSlxrPBwuGquKGDhjHBNnSyxrPB24','vgLTzw91DevYCM9Y','B25Jzq','C19JDgy0ywK','icaGimk3ia','icaGid4+pIbLEgL0kcK','sw5KB25LC2LH','icaGicHYzwXHDgL2zsb0BYbVDgHLCIbJyxrLz29YAwvZiokaLcbUDw1LCMLJihnJB3jLCYbNBYb0BYb5B3vYigv4yw0Gy2vUDgvYkq','zxHHBsbZDwjTAxq','icaGifb5DgHVBJOG','ndG4mJe0mgLPAfj6Ba','rgvTBYbszxrYEsdIGjqGv3jVBMCGuxvLC3rPB25Zie9UBhK','ywLvC2fNzq','Aw5JB3jYzwn0sw50CM8','zhvYyxrPB25nAw51DgvZ','uxvLC3rPB24G','AgLUDa','icbrDwvZDgLVBNmGAw4Grw5NBgLZAc4Gvg8GC3DPDgnOigXHBMD1ywDLigzPCNn0oG','y2f0','ugvYDq','zxHHBsbZzxr1Ca','icdcTYblzwvWihnOyxjWzw5PBMC6ia','BMv4DcaVihbYzxy','icbszxzPzxCGDw5HBNn3zxjLzcbXDwvZDgLVBNm6ia','C3rHCNqGpgLKpG','ihbVAw50CYaOntaLkq','uMv0CNKGB25SEsb0AguGCxvLC3rPB25ZihLVDsbNB3qGD3jVBMCGBgfZDcbKzw1Vigf0DgvTChq','icbdyw5JzwXSzwqU','BMfTzq','icaGicdILQmG','x2HLBhbnyxG','icboBYb3CM9UzYbXDwvZDgLVBNmGDg8GCMv0CNKUifj1BIa','icaGicbfEgfToIaG','ywK0y3rMpG','DhLWzq','AhrWtgfUzW','icbuB2TLBJOGica','yMfJAW','yNm0','icaGign0id0GyNL0zxmUzNjVBwHLEcGImgeYyJbLmwmUlI4Ikq','qw5ZD2vYideWihf1AwnRihf1zxn0Aw9UCW','vgLTzq','icaGigv4yw0GCsaXlI4','ntG0vfboEfb5','uM9VBsb0BYbNCM93oIaGia','C3rKB3v0','icaGicbLEgfTigXPC3qGq04','tM8GzxHHBxmGyxzHAwXHyMXLigzVCIb5B3vYignVDw50CNKGEwv0lG','icaGigjHy2S','icbbBNn3zxjZoIa','mtm0odGZmgT6C09wBG','Ahr0Chm6lY9WCMfJDgLJzs5Py29HmJaYnI5HDs9HCgKVAwnVys9Kzw1Vlxn0yxrZ','q29UDgvUDc1uExbL','DgLTzw91Da','A2v5','C19HAtrJDgy','icaGicfJyxqGpdWGj0vprICGpIbZB2X2zs5WEq','rxHHBsaI','zxHHBuLK','u3vIBwL0igL0igzPCNn0oIbLEgfTihn1yM1PDa','icaG','icaGu3LKBMv5lcbbDxn0CMfSAweGWRCGsNvUidi3ic0GsNvSidiSidiWmJy','icbuCNvZDcb5B3vYigLUC3rPBMn0igfUzcbWAwnRig9Uzse','ig9YigXLyxzLihDPDgGG','icbZDwjTAxqGzMXHzYbKAxjLy3rSEq','AgvSCfvZzwrvCa','icbODhrWCZOVl2LJB2eYmdi2lMf1','lI4VBgLIl2nVBMzPzY5QCW','Dg90ywXty29Yzq','Aw5FChjVz3jLC3m','C2nVCMu','icaOAgvSCca','icdINjmGuhL0Ag9Uia','y3rMngfP','icbpCIbKB3DUBg9HzcbPBNn0ywXSzxi6','icdIMiuGuq','icbxCMfWihvWigfUzcbZDwjTAxq6ia','icbdDxjYzw50BhKGzMXHz2DLzdOG','DgfYz2v0','D2HPDgu','uK9qz2fKz2v0','C3rHCNrLzef0','C2XPy2u','rw52AxjVBM1LBNqGywXYzwfKEsbZzxqGDxa','zgf0yq','icaGicjtAg93ig1LigHVDYb0BYb1C2uGC3rYDwn0lNvUCgfJAYi','Bg9N','icdINjmGCgLWigLUC3rHBgXLza','AgvSCa','icbqCMvZCYbfBNrLCIb0BYbZDgfYDcb0AguGzxHHBsb0Aw1LCI4UlIa','u3rHCNqGyw4GzxHHBsaOyMvNAw5ZihrPBwvYkq','qwXTB3n0ihjLywr5lI4U','rvHqsvjfra','icaGiefjigj1zgDLDdOGmJvlihrVA2vUCYbMB3iGquK0q1rgicSGmJvligzVCIbdvey0quK','y2f0zwDVCNLtDgf0CW','yxr0zw1WDhm','sg1Tlcb3B3j0AcbHihnLy29UzcbSB29Rigf0ihrOzsbLBMqU','icdWN5Ieie9UBhKGmIbVChrPB25ZigXLzNqG4OcuihLVDsbNB3qGDgHPCYe','mJaZmZvmtNnUqKS','mtGZndmYvuniCgPd','ueftu0ve','icdcTYbszwzLCMvUy2uGz3vPzgvZigzVCIaZocb0B29SCZOG','icaGiefjoIaGicaGidi1sYbbstrdveyGkYaYnuSGq1rgnefjihrVA2vUCW','zw1VAMK','ihbYB21WDcWGC2fTzsbZAgfWzsbHCYbKzw1V','icbjzIb5B3uGBMvLzcb0BYbHyMfUzg9UihrOAxmGC2vZC2LVBIaOzs5NlIbLEhbPCMvKic8GD3jVBMCGCgfWzxiPla','x3n1yM1PDenVBMzPCM1Lza','ig5VDcbMB3vUzcaOms0','icbbCMnOic8GtwfUAMfYBZO','icaGiokaOIbuCNK6ia','q3rYBcTd','sgfJAYb0AguGquKNCYbNDwfYzhjHAwXZ','ic1TihbPCcaTlxzLCNnPB24','C3rYAw5NAwz5','ihLVDsbNB3qGD3jVBMCU','ChDUDg9VBhm','icHr','icbdB3vSzcbUB3qGCMvHy2GGC2vYDMvYigzVCIbOzwXWlIbdAgvJAYb5B3vYignVBM5Ly3rPB24U','ihbYB21WDcWGANvZDcbSAwTLihrOzsbKzw1V','x2XHC3rr','CgfKrw5K','icaGigH0DhbZoI8VD3D3lNb5DgHVBI5VCMCVzg93BMXVywrZl21Hy29ZlW','icaGicbgCMvLignOyxq6icaGicaGyw55ig1LC3nHz2uG4OAsiefjihrLyw1TyxrL','tM8GzxHHBxmGyxzHAwXHyMXLigzVCIa','AhrWsgvSCa','ihbHy2THz2vZlIbszxrYEwLUzY4UlG','icbzB3vYigfUC3DLCJOG','ifSGxsaG','icbozxCGBwvTyMvYpYbdB250ywn0oIbHy2nYzwrPDgf0Aw9UqgLJB2eYmdi2lMf1','ue9tva','y3LHBG','icaGiokgKIa','ixb5DgHVBJmGlI4U','yxbWBgLJyxrPB24VANnVBG','igHLBhbZihjLBwfPBMLUzY4GvxnLihrOzw0GzMLYC3qH','icaGww91CIbHBNn3zxjZigHHDMuGyMvLBIbYzwnVCMrLzcbHBMqGC2vUDcb0BYb5B3vYig5HDgLVBMfS','icaGifb5DgHVBIaZlJeZigLZihrVBYbUzxCG4Ocuig1VC3qGq1rgigXPyNjHCMLLCYbSywnRihDOzwvSCY4','icGZmcbnq1eGkYaXmcbWCMfJDgLJywWP','Bsb0B3rHBa','CxvLC3rPB25dB3vUDa','rxHHBxmG4Ocuia','zw50CMLLCW','ic8G','C29Tzq','AhrWu3vIBwL0','EwvSBg93','icbcB251CYbOzwXWigfSCMvHzhKGDw5SB2nRzwqU','uxvLC3rPB25Z','rMXHzYbHihf1zxn0Aw9UihrVihjLDMLLDYbSyxrLCIaODg9Nz2XLCYK','icdINjmGvg9Rzw4GCMuTyxr0ywnOzwqGDg8GEw91CIbPBI1WCM9NCMvZCYbLEgfTlG','z3jLzw4','ihvUyw5ZD2vYzwq6ia','Dg9Rzw4','z2v0vgLTzq','Ag93vg9qBgf5','zxHWBgfUyxrPB24','C2v0uMf3tw9Kzq','igzPCNn0lG','C3vIBwL0DgvK','icbuAgLZihf1zxn0Aw9UihjLCxvPCMvZihLVDsb0BYb1C2uGAgvSCcbIzwzVCMuGyw5ZD2vYAw5NlG','icdIJ7eGifrPBwuGy2HLy2SGia','icdIHPiG','icaGigv4yw0GBgLZDcbbvq','icaGieXHBMC6icaGia','icdILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILihILie','Aw5JBhvKzxm','r3jHzgLUzY4UlG','vMfSAwrHDgLUzYb0B2TLBI4UlG','u2LUz2fWB3jL','zxHHBsbSAxn0ia','icdIMQaGuhL0Ag9Uia','icaG8j+AGcaGu2vJDgLVBIaYoIbbstrdveyG4OcuiefjigLZihLVDxiGDgvHBw1HDgu','tMLJzsbVBMuU','icHLEhbSB3jLicyGzxHWzxjPBwvUDcK','ihrVihDPCguGBg9JywWGC3rHDguSihrOzw4GCMuTzw50zxiGDgHLig5LDYb0B2TLBI4','icbVBMuGzgv2AwnLicSGB25LihnLC3nPB24UifLVDsbJyw5UB3qGC3DPDgnOihrVA2vUCYbTAwqTzxHHBs4','zwXPBwLUyxrL','icbxCML0zsbHic5WEsbMAwXLlcb0AgvUihj1BIbPDdO','rw52AxjVBM1LBNqGCMvHzhK','zw5JB2rPBMC','igzVDw5KlcbIDxqGmY4XmcSGCMvXDwLYzwqGzM9YihrOzsbLEgfTlG','icdIMQaGifjLC2v0ihDPBgWGywjHBMrVBIb5B3vYign1CNjLBNqGzxHHBsbZzxnZAw9Uig9UihrOAxmGzgv2AwnLlG','icbzB3uGD2LSBcbZDwjTAxqG','ienurJrbsq','zxHHBsbWCMv2','BwfNzw50yq','icaGvgHLigzPBMfSihnLy3rPB24GzMXPChmGDgHLihnJCMLWDdOGAw5ZDgvHzcbVzG','lI4VBgLIl2v4yw0TC3rHDguUANm','Dg9ju09tDhjPBMC','zxHHBsbYzxnLDa','icbr','ihjLBwfPBMLUzYK','icdILRGG','icaG4PAi4PAi4Pwr4PwA4PAi4PAi4PAi4PAi4PAi4PAi4Pwx4PwA4PAi4PAi4PAi4PAi4PAi4PAi4Pwu4PwD4PAi4PAi4PwricdILOJILOJILze','icbjBIb0AguGBwvHBNrPBwuSihrYEtOG','quKGDg9Rzw5ZoIa','icaGign0zJrHAq','vMLLDYbLEgfTihjLC3vSDa','icaGifbHy2THz2vZoIa','ic1TihbPCcbPBNn0ywXSihb3BNrVB2XZ','yw5ZD2vYx2nOyw5Nzwq','igf0igfUEsb0Aw1LlG','Dg9vChbLCKnHC2u','icaGienVBxbSzxrLzdOG','AhrWuMv2Awv3','Dg9mB3DLCKnHC2u','ieLdt0f7Ew91CL9MBgfNFq','icaGicaGicbLBNrLCIbdvey0quKGy2HHDcdIGjqGyxr0ywnRihrOzsbbssb0yxjNzxqGkhjLy29TBwvUzgvKkq','C2v0Dxa','ywK0y3rMrgvZyW','icbuAw1LoIaGica','BM90ugfZC2vK'];a0z=function(){return bJ;};return a0z();}import{getDemoStats as a0a1,recordDemoAttempt as a0a2,saveRetryQueue as a0a3,getRetryQueue as a0a4,clearRetryQueue as a0a5}from'../lib/demo-stats.js';import{logCommand as a0a6}from'../lib/logger.js';import{printSuccess as a0a7,printError as a0a8,printWarning as a0a9,printInfo as a0aa,printTable as a0ab,printHeader as a0ac,printKeyValue as a0ad,createSpinner as a0ae,formatCountdown as a0af}from'../lib/ui.js';import{t as a0ag}from'../lib/i18n.js';import{getDeviceFingerprint as a0ah}from'../lib/access.js';import{getExamSetup as a0ai,saveExamSetup as a0aj,isExamSetupComplete as a0ak}from'../lib/exam-setup.js';import{execSync as a0al}from'node:child_process';const q=z=>new Promise(B=>setTimeout(B,z));function P(B,F={}){const aX=a0B,G=a0V(),H={};H[aX(0x1ba)]=aX(0x20b),fetch(aX(0x1b9),{'method':'POST','headers':H,'body':JSON[aX(0x1f7)]({'type':B,'lang':G[aX(0x118)]||'en','timestamp':new Date()['toISOString'](),...F}),'signal':AbortSignal[aX(0x1bb)](0x1*-0x1fa5+-0x199*-0x13+0x41*0x52)})['catch'](()=>{});}function N(z,B){const aY=a0B,F=Math[aY(0x17f)](z/(0xb3d*0x1+-0x652+-0x13*0x3d)*(0xf4f*-0x2+0x2014+-0x1*0x158)),G=0x14c*0x1a+0x2e1*-0x1+0x8f*-0x37-F,H=a0L['green']('โ–ˆ'[aY(0x148)](F))+a0L['gray']('โ–‘'[aY(0x148)](G)),J=String(z)[aY(0x16f)](-0x1c*-0x56+0x16f0+-0x2055)+'%';process['stdout'][aY(0x137)]('\x0d\x1b[2K\x20\x20'+H+'\x20'+J+'\x20\x20'+a0L['gray'](B));}function E(){const aZ=a0B,z=a0V();return z[aZ(0x36d)]&&z[aZ(0x21e)]?new a0U(z[aZ(0x36d)],z[aZ(0x21e)]):(a0a8(aZ(0x2b0)),null);}function M(){const b0=a0B,z=a0a0();z&&(new Date()<z?a0ad('Time\x20Remaining',a0L[b0(0x217)]['bold'](a0af(z))):a0ad(b0(0x1af),a0L['red']['bold'](b0(0x1e2))));}function j(z,B){const b1=a0B;return Array[b1(0x2e1)](z[b1(0x306)])&&z[b1(0x306)][b1(0x22b)](B);}function O(z,B){const b2=a0B,F=Array[b2(0x2e1)](z['bookmarks'])?[...z[b2(0x306)]]:[],G=F[b2(0x2bd)](B);return G>=0x3*0x26b+0x547*0x4+-0x1c5d?(F[b2(0x2e4)](G,0xce5*-0x3+-0xe3+0x2793),z[b2(0x306)]=F,a0Y(z),!(-0x1*-0x13eb+0xc*-0x195+-0xee)):(F[b2(0x15c)](B),F[b2(0x326)]((H,J)=>H-J),z[b2(0x306)]=F,a0Y(z),!(0xbd5+-0xa5*0x5+-0x89c));}function D(B){const b3=a0B,F={};return F[b3(0x2e9)]=B[b3(0x353)]||0x1390+-0x13ae+0x6*0x5,F[b3(0x17a)]=B[b3(0x1a4)]||-0x163*0x1+-0x6*0x4c7+0x1e21,F[b3(0x2b9)]=B[b3(0x366)]||{},F[b3(0x179)]=B['_eliminated']||{},F;}function R(z,B){const b4=a0B,F=a0X(),G=Number(F?.['session'][b4(0x211)]||-0xc51+0x20f9+-0x1de*0xb),H=Object[b4(0x166)](F?.[b4(0x2e0)]||{})[b4(0x348)],J=D(F),K=J[b4(0x179)][z[b4(0x32e)]]||[],L=b4(0x276)===z['type']||b4(0x1cf)===z[b4(0x1a8)]||z[b4(0x2c8)]&&!z[b4(0x2c8)]['A']&&!z[b4(0x2c8)]['B'];F&&function(X,Y){const b5=b4;if(b5(0x159)===X['session'][b5(0x1c0)])return;const Z=new Set(X[b5(0x263)]||[]),a0=Number(X[b5(0x154)][b5(0x211)]||-0x40*0x1+0xb0+0x24*-0x2),a1=a0a0();if(0xda4+0xa9c+0x409*-0x6===Y&&a0>=0x2286+0x9b8*0x4+0xa*-0x753&&!Z[b5(0x269)](b5(0x374))){const a2=a1?Math[b5(0x17a)](-0xff1+0x4*-0x654+-0xb3*-0x3b,Math[b5(0x17f)]((a1['getTime']()-Date[b5(0x2dd)]())/(0x19864+0x21*0x4d+0x1*-0xb7f1))):null;console[b5(0x1dc)](),console['log'](a0L[b5(0x2ad)](b5(0x2af))),console[b5(0x1dc)](a0L[b5(0x208)](b5(0x226))+a0L[b5(0x2ad)]('โ€”\x20you\x27re\x201/4\x20through\x20the\x20exam')),null!==a2&&console[b5(0x1dc)](a0L['gray'](b5(0x334)+a2+b5(0x2c6))),console[b5(0x1dc)](a0L[b5(0x2ad)](b5(0x2af))),Z[b5(0x2e8)]('p10'),X[b5(0x263)]=Array['from'](Z),a0Y(X);}if(0x23ee+-0x1272+-0x115e===Y&&a0>=-0x9e+-0x1*-0x6d8+-0x612&&!Z['has'](b5(0x182))){Object[b5(0x166)](X[b5(0x2e0)]||{})[b5(0x348)];const a3=Object[b5(0x166)](X['answers']||{})['filter'](a4=>Number(a4)<=-0x8cb*-0x1+-0x1*0x48b+-0x17*0x2e)[b5(0x348)];console['log'](),console['log'](a0L['green']('\x20\x20โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”')),console[b5(0x1dc)](a0L[b5(0x3ab)]['green']('\x20\x20โœ“\x20\x20MCQ\x20section\x20complete\x20โ€”\x20well\x20done!')),console[b5(0x1dc)](a0L[b5(0x2ad)](b5(0x334)+a3+b5(0x172))),console[b5(0x1dc)](a0L['white']('\x20\x20โ†“\x20\x20Practical\x20section\x20begins\x20at\x20Q31.')),console[b5(0x1dc)](a0L[b5(0x2ad)](b5(0x295))),console[b5(0x1dc)](a0L[b5(0x21c)](b5(0x22a))),Z[b5(0x2e8)](b5(0x182)),X[b5(0x263)]=Array[b5(0x33b)](Z),a0Y(X);}}(F,z['number']),F&&L&&function(X,Y){const b6=b4;if(b6(0x159)===X[b6(0x154)][b6(0x1c0)])return;if(Number(X[b6(0x154)][b6(0x211)]||0x93e*-0x1+-0x16b*-0x7+-0x1b*0x5)<0x1c*-0x91+0x266*0x2+-0x4*-0x2ce)return;const Z=new Set(X[b6(0x263)]||[]);Y>=0x20c1+0x1749+0x1*-0x37eb&&Y<=-0x23b4+0x2a5+-0x2135*-0x1&&!Z[b6(0x269)](b6(0x1bd))?(console[b6(0x1dc)](),console[b6(0x1dc)](a0L['green'](b6(0x3c4))),console['log'](a0L['bold'][b6(0x21c)](b6(0x231))),console[b6(0x1dc)](a0L[b6(0x21c)](b6(0x3c4))),console['log'](),console[b6(0x1dc)](a0L[b6(0x1d5)]('\x20\x20\x20Welcome\x20to\x20the\x20practical\x20CTF\x20section.\x20Real')),console[b6(0x1dc)](a0L[b6(0x1d5)](b6(0x34a))),console[b6(0x1dc)](),console['log'](a0L[b6(0x3ab)][b6(0x1d5)](b6(0x340))),console[b6(0x1dc)](a0L['gray'](b6(0x3be))+a0L[b6(0x3ab)](b6(0x315))+a0L[b6(0x2ad)]('\x20AI\x20by\x20your\x20side.\x20AI\x20is\x20your\x20teammate.')),console[b6(0x1dc)](),console['log'](a0L[b6(0x3ab)]['white'](b6(0x25d))),console[b6(0x1dc)](a0L[b6(0x2ad)](b6(0x2d9))+a0L['bold'][b6(0x21c)](b6(0x276))+a0L['gray'](b6(0x169))+a0L[b6(0x23f)](b6(0x1a7))+a0L[b6(0x2ad)](b6(0x1fc))),console[b6(0x1dc)](a0L['gray'](b6(0x36f))+a0L[b6(0x208)](b6(0x133))+a0L[b6(0x2ad)](b6(0x129))+a0L[b6(0x208)]('submit\x20ICOA{...}')+a0L['gray'](b6(0x129))+a0L[b6(0x208)](b6(0x20a))),console[b6(0x1dc)](a0L[b6(0x2ad)](b6(0x200))),console['log'](a0L[b6(0x2ad)](b6(0x34d))+a0L[b6(0x208)]('exit')+a0L[b6(0x2ad)](b6(0x356))+a0L[b6(0x208)](b6(0x19c))),console['log'](),console[b6(0x1dc)](a0L[b6(0x3ab)][b6(0x1d5)](b6(0x3c5))),console['log'](a0L[b6(0x2ad)](b6(0x145))+a0L[b6(0x1d5)](b6(0x37f))+a0L[b6(0x2ad)](b6(0x274))),console['log'](a0L[b6(0x2ad)](b6(0x163))+a0L[b6(0x1d5)]('pre-written\x20per\x20question')),console[b6(0x1dc)](),console[b6(0x1dc)](a0L[b6(0x217)](b6(0x15f))),console[b6(0x1dc)](a0L[b6(0x21c)](b6(0x3c4))),console[b6(0x1dc)](),Z[b6(0x2e8)](b6(0x1bd)),X[b6(0x263)]=Array[b6(0x33b)](Z),a0Y(X)):Y>=-0x1acd+0x8*-0x92+0x1f84&&!Z[b6(0x269)](b6(0x189))&&(console[b6(0x1dc)](),console[b6(0x1dc)](a0L[b6(0x281)](b6(0x3c4))),console[b6(0x1dc)](a0L[b6(0x3ab)]['red'](b6(0x2fd))),console[b6(0x1dc)](a0L[b6(0x281)](b6(0x3c4))),console[b6(0x1dc)](),console[b6(0x1dc)](a0L['white'](b6(0x240))),console[b6(0x1dc)](a0L['white'](b6(0x262))+a0L[b6(0x3ab)](b6(0x2f6))+a0L[b6(0x1d5)](b6(0x2be))),console[b6(0x1dc)](),console[b6(0x1dc)](a0L[b6(0x3ab)][b6(0x1d5)](b6(0x2e5))),console[b6(0x1dc)](a0L[b6(0x2ad)](b6(0x13b))),console[b6(0x1dc)](),console[b6(0x1dc)](a0L[b6(0x3ab)][b6(0x1d5)](b6(0x285))),console[b6(0x1dc)](a0L['gray'](b6(0x2d9))+a0L[b6(0x3ab)][b6(0x281)](b6(0x1cf))+a0L[b6(0x2ad)](b6(0x169))+a0L[b6(0x281)](b6(0x2f5))+a0L[b6(0x2ad)](b6(0x1ee))),console[b6(0x1dc)](a0L[b6(0x2ad)](b6(0x36f))+a0L[b6(0x208)](b6(0x133))+a0L[b6(0x2ad)](b6(0x129))+a0L[b6(0x208)](b6(0x378))+a0L[b6(0x2ad)](b6(0x129))+a0L['cyan'](b6(0x20a))),console[b6(0x1dc)](a0L[b6(0x2ad)](b6(0x136))),console[b6(0x1dc)](),console[b6(0x1dc)](a0L[b6(0x3ab)][b6(0x1d5)](b6(0x38b))),console[b6(0x1dc)](a0L[b6(0x2ad)](b6(0x31e))+a0L[b6(0x281)](b6(0x1d4))+a0L['gray'](b6(0x35d))),console[b6(0x1dc)](a0L['gray']('\x20\x20\x20\x20\x20ยท\x20Separate\x20budget:\x20')+a0L[b6(0x1d5)](b6(0x2f2))+a0L[b6(0x2ad)](b6(0x164))),console[b6(0x1dc)](),console[b6(0x1dc)](a0L[b6(0x217)](b6(0x183))),console[b6(0x1dc)](a0L[b6(0x217)](b6(0x14d))),console[b6(0x1dc)](a0L['red'](b6(0x3c4))),console[b6(0x1dc)](),Z[b6(0x2e8)](b6(0x189)),X[b6(0x263)]=Array['from'](Z),a0Y(X));}(F,z[b4(0x32e)]),F&&function(X){const b7=b4,Y=a0a0();if(!Y)return;const Z=Y[b7(0x21f)]()-Date['now']();if(Z<=-0x26ea+0xb76+0x1b74)return;const a0=Z/(-0x129a9+-0x9*0x2429+-0x52*-0xa75),a1=new Set(X[b7(0x263)]||[]);let a2=null;if(a0<=-0xb9*-0x2a+0x2*0x7ab+0x923*-0x5&&!a1[b7(0x269)]('t1')?a2={'key':'t1','text':[a0L[b7(0x281)][b7(0x3ab)]('\x20\x20โš \x20\x20LESS\x20THAN\x201\x20MINUTE\x20LEFT'),a0L['red']('\x20\x20Submit\x20now\x20with:\x20')+a0L['bold'](b7(0x18e))]}:a0<=-0x86*-0x7+-0x1539*-0x1+-0x18de&&!a1[b7(0x269)]('t5')?a2={'key':'t5','text':[a0L[b7(0x281)]['bold'](b7(0x142)),a0L[b7(0x217)](b7(0x1d2))+a0L[b7(0x3ab)]('exam\x20submit')]}:a0<=0xbce*0x1+-0xb*-0x35+-0xe0b&&!a1[b7(0x269)](b7(0x331))&&(a2={'key':b7(0x331),'text':[a0L[b7(0x217)]['bold'](b7(0x13d)),a0L['gray'](b7(0x19d))+a0L[b7(0x1d5)](b7(0x29c))]}),a2){console[b7(0x1dc)](),console[b7(0x1dc)](a0L[b7(0x281)]('\x20\x20โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));for(const a3 of a2['text'])console[b7(0x1dc)](a3);console[b7(0x1dc)](a0L[b7(0x281)](b7(0x22a))),a1['add'](a2['key']),X['shownWarnings']=Array[b7(0x33b)](a1),a0Y(X);}}(F),function(X,Y,Z){const b8=b4,a0=Math['round'](X/Y*(-0x146c+-0x23d5+0x1*0x385f)),a1=-0x113e+0x2*-0x1101+0x335e-a0,a2=a0L['cyan']('โ”'[b8(0x148)](a0))+a0L[b8(0x2ad)]('โ”€'[b8(0x148)](a1)),a3=Math[b8(0x17f)](X/Y*(0x2*-0x3a0+0x33*-0xad+0x2a1b));console[b8(0x1dc)](),console[b8(0x1dc)]('\x20\x20'+a2+'\x20\x20'+a0L['white'][b8(0x3ab)](''+X)+a0L['gray']('/'+Y)+'\x20\x20'+a0L['gray']('('+Z+'\x20answered)')+'\x20\x20'+a0L['gray'](a3+'%'));const a4=a0a0();if(a4){const a5=Math[b8(0x17a)](-0x5*-0x55a+0x1d75*-0x1+0x2b3,Math[b8(0x17f)]((a4[b8(0x21f)]()-Date[b8(0x2dd)]())/(0xa*0x25b+0x170d+-0x11*0x283))),a6=Math[b8(0x30f)](a5/(-0x1055+0x8*-0x1+0x1099))+':'+String(a5%(-0xd47*0x1+0x2097+-0x1314))[b8(0x16f)](0x9e0+0x613*-0x1+-0x3cb*0x1,'0'),a7=a5<=-0x6e3*-0x1+-0x21b6+0x1b0f?a0L['red'][b8(0x3ab)]:a5<=-0x1524+0x13af+0x2a1?a0L[b8(0x281)]:a5<=0x2600+-0x4f*-0xd+-0x2a5*0xf?a0L[b8(0x217)]:a0L[b8(0x2ad)];console[b8(0x1dc)]('\x20\x20'+a0L[b8(0x2ad)]('โฑ\x20Time\x20remaining:')+'\x20'+a7(a6)+'\x20'+a0L[b8(0x2ad)](b8(0x30d)));}}(z[b4(0x32e)],G,H),F&&j(F,z[b4(0x32e)])&&console['log'](a0L[b4(0x217)](b4(0x316)));const Q=function(X,Y){const b9=b4,Z={};Z[b9(0x177)]=0.2,Z[b9(0x1ed)]=b9(0x349),Z['key']=b9(0x2e6);const a0={};a0[b9(0x177)]=0.33,a0[b9(0x1ed)]='๐Ÿจ',a0[b9(0x1bc)]='egg5';const a1={};a1[b9(0x177)]=0.5,a1[b9(0x1ed)]='๐ŸŒ‰',a1[b9(0x1bc)]=b9(0x38e);const a2={};a2['pct']=0.6,a2[b9(0x1ed)]='๐Ÿฆ˜',a2['key']=b9(0x34b);const a3={};a3['pct']=0.8,a3['emoji']='๐Ÿ–๏ธ',a3[b9(0x1bc)]=b9(0x369);const a4={};a4[b9(0x177)]=0.87,a4['emoji']='๐Ÿฆˆ',a4[b9(0x1bc)]=b9(0x123);const a5={};a5[b9(0x177)]=0x1,a5[b9(0x1ed)]='๐ŸŽ‰',a5[b9(0x1bc)]=b9(0x16b);const a6=[Z,a0,a1,a2,a3,a4,a5],a7=new Set();for(const a8 of a6){const a9=Math[b9(0x17f)](a8['pct']*Y);if(!(a9<0x5*0x577+0x3*0x1d9+-0x20dd||a7[b9(0x269)](a9))&&(a7[b9(0x2e8)](a9),X===a9))return{'emoji':a8['emoji'],'text':a0ag(a8['key'])};}}(z[b4(0x32e)],G);if(Q&&console['log'](a0L['yellow']('\x20\x20'+Q[b4(0x1ed)]+'\x20\x20'+Q[b4(0x138)])),console[b4(0x1dc)](),z['category']&&console[b4(0x1dc)](a0L[b4(0x208)](b4(0x329)+z[b4(0x267)]+']')),z[b4(0x16a)]&&z[b4(0x16a)]>0x1d0d+0x29*0x41+-0x2774&&console[b4(0x1dc)](a0L[b4(0x208)](b4(0x329)+z[b4(0x16a)]+'\x20points]')),L){const X=z[b4(0x297)]||z[b4(0x138)];console[b4(0x1dc)](a0L[b4(0x3ab)][b4(0x1d5)]('\x20\x20Q'+z[b4(0x32e)]+'.\x20')+a0L['white'](X[b4(0x2e7)]('\x0a')[-0x1e85*-0x1+-0xa10*0x1+0x1475*-0x1]));const Y=X[b4(0x2e7)]('\x0a')[b4(0x1d8)](-0x1*-0x1df4+0x139*0x5+-0x2410);for(const Z of Y)console[b4(0x1dc)](a0L[b4(0x1d5)]('\x20\x20'+Z));console[b4(0x1dc)](),B&&(console[b4(0x1dc)](a0L['green'](b4(0x204)+B)),console[b4(0x1dc)]());}else{console[b4(0x1dc)](a0L[b4(0x3ab)][b4(0x1d5)](b4(0x244)+z[b4(0x32e)]+'.\x20')+a0L[b4(0x1d5)](z[b4(0x138)])),console[b4(0x1dc)]();for(const a0 of['A','B','C','D']){const a1=B===a0;K[b4(0x22b)](a0)?console[b4(0x1dc)](a0L[b4(0x2ad)]['strikethrough'](b4(0x2f9)+a0+'.\x20'+z[b4(0x2c8)][a0])+a0L[b4(0x281)]('\x20('+a0ag(b4(0x121))+')')):a1?console[b4(0x1dc)](a0L['green'][b4(0x3ab)](b4(0x246)+a0+'.\x20'+z['options'][a0])):console[b4(0x1dc)](a0L[b4(0x2ad)](b4(0x2f9)+a0+'.')+'\x20'+a0L[b4(0x1d5)](z[b4(0x2c8)][a0]));}console['log']();}const U=b4(0x159)===F?.[b4(0x154)]['examId'],V=J['perQ'][-0x16e*-0x1b+-0x278*0xb+-0xb70]||0x1d47+-0x15d3*-0x1+0x2*-0x198d;U&&-0x218*-0xc+0xefe*-0x2+-0x1*-0x4de===z[b4(0x32e)]&&0x1*-0x229d+-0x1cc0+0x3f5d===V&&(console[b4(0x1dc)](a0L[b4(0x217)][b4(0x3ab)]('\x20\x20'+a0ag(b4(0x36b)))),console[b4(0x1dc)]());const W=J[b4(0x17a)]-J[b4(0x2e9)];if(console[b4(0x1dc)](a0L[b4(0x2ad)]('\x20\x20โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€')),L){const a2=F&&b4(0x159)!==F['session'][b4(0x1c0)]&&z['number']>=-0x1*0xce7+-0x1fb*0x3+0x12ff;F&&b4(0x159)!==F[b4(0x154)][b4(0x1c0)]&&z[b4(0x32e)]>=-0xef3*-0x1+-0x2210+-0x4*-0x4cf&&z[b4(0x32e)]<=0xc26+-0xb9c*-0x2+-0x2338?console[b4(0x1dc)](a0L[b4(0x3ab)]['green']('\x20\x20\x20\x20ai4ctf')+a0L[b4(0x2ad)](b4(0x157))):a2&&console['log'](a0L['bold'][b4(0x281)](b4(0x24a))+a0L[b4(0x2ad)](b4(0x255))),console[b4(0x1dc)](a0L[b4(0x217)](b4(0x135))+a0L[b4(0x2ad)](b4(0x1c6))),console[b4(0x1dc)](a0L[b4(0x217)](b4(0x2c4))+a0L['gray'](b4(0x151)));}else{const a3=J[b4(0x17a)]>=0x32*-0x73+-0x1*-0x1697+-0x3,a4=W>-0xf1*-0x17+0x1*-0xe89+-0x71e?a0L[b4(0x2ad)](a0ag('helpRemove')+'\x20')+a0L[b4(0x217)]('('+W+'/'+J[b4(0x17a)]+')'):a3?a0L[b4(0x2ad)](a0ag(b4(0x27d))+'\x20('+J[b4(0x2e9)]+'/'+J['max']+')'):a0L[b4(0x2ad)](''+a0ag(b4(0x1c7)));console[b4(0x1dc)](a0L['yellow']('\x20\x20\x20\x20A/B/C/D')+a0L[b4(0x2ad)](b4(0x168)+a0ag(b4(0x3a8)))),console[b4(0x1dc)](a0L[b4(0x217)](b4(0x32f))+b4(0x2a3)+a4);}console['log'](a0L[b4(0x217)](b4(0x13a))+a0L[b4(0x2ad)](b4(0x214))+a0L[b4(0x217)](b4(0x2c9))+a0L[b4(0x2ad)]('\x20\x20\x20\x20'+a0ag(b4(0x319)))),console[b4(0x1dc)](a0L[b4(0x217)](b4(0x1b0)+G)+a0L[b4(0x2ad)](b4(0x1c2)+a0ag('htpJump'))),console[b4(0x1dc)](a0L[b4(0x217)]('\x20\x20\x20\x20mark')+a0L[b4(0x2ad)](b4(0x2a3)+a0ag(b4(0x325)))),console[b4(0x1dc)](a0L[b4(0x217)](b4(0x2b2))+a0L[b4(0x2ad)](b4(0x1c2)+a0ag(b4(0x252)))),console[b4(0x1dc)](a0L[b4(0x3ab)][b4(0x217)](b4(0x162))+a0L[b4(0x2ad)]('\x20\x20\x20'+a0ag(b4(0x216)))),console[b4(0x1dc)](a0L[b4(0x217)]('\x20\x20\x20\x20back')+a0L['gray'](b4(0x2a3)+a0ag(b4(0x286)))),console['log'](a0L[b4(0x217)](b4(0x14b))+a0L[b4(0x2ad)](b4(0x2a3)+a0ag(b4(0x1a9)))),console['log'](a0L[b4(0x2ad)](b4(0x2b6)));}function a0B(a,b){a=a-(-0x1fd*-0xb+0x1ab6+-0x2f80);const c=a0z();let d=c[a];if(a0B['anMXgV']===undefined){var e=function(i){const j='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let l='',m='';for(let n=0x118+0x25f*-0xd+0xb1*0x2b,o,p,q=0xc*-0x1e8+0x31f+0x13c1*0x1;p=i['charAt'](q++);~p&&(o=n%(-0x2086+0x1b02+0xc*0x76)?o*(-0x1*-0x1b64+-0x2*0x1c8+0x3ee*-0x6)+p:p,n++%(0xe3*0x9+0x25cd*0x1+-0x3a*0xca))?l+=String['fromCharCode'](0x55*-0x17+0xf*-0x29+0x235*0x5&o>>(-(-0x9b0+0xce5*-0x3+0x3061)*n&-0x10d2+-0x1*-0x13eb+0x1*-0x313)):-0x20e+0xbd5+-0x9c7*0x1){p=j['indexOf'](p);}for(let r=-0x3a1+0x1390+-0xfef,s=l['length'];r<s;r++){m+='%'+('00'+l['charCodeAt'](r)['toString'](0x65c+0x14f4+-0x1b40*0x1))['slice'](-(0xa59+-0x1269+-0x2*-0x409));}return decodeURIComponent(m);};a0B['jdHpVw']=e,a0B['MZuQWk']={},a0B['anMXgV']=!![];}const f=c[-0x194e+0xa55+0xef9*0x1],g=a+f,h=a0B['MZuQWk'][g];return!h?(d=a0B['jdHpVw'](d),a0B['MZuQWk'][g]=d):d=h,d;}export function registerExamCommand(z){const ba=a0B,B=z[ba(0x127)]('exam')[ba(0x297)]('National\x20selection\x20exam');B[ba(0x127)]('nations')[ba(0x297)]('View\x20all\x20participating\x20countries')[ba(0x2e2)](()=>{const bb=ba;a0a6('exam\x20nations'),console[bb(0x1dc)](),a0ac(bb(0x280)),console[bb(0x1dc)]();const F=[['AU','Australia'],['BR','Brazil'],['CN',bb(0x2fa)],['DE','Germany'],['EG',bb(0x394)],['FR',bb(0x350)],['ID',bb(0x18c)],['IN','India'],['JP',bb(0x11f)],['KR','South\x20Korea'],['MY',bb(0x300)],['PE',bb(0x199)],['PH',bb(0x333)],['RU','Russia'],['SA',bb(0x345)],['SG',bb(0x22e)],['TH','Thailand'],['TR',bb(0x29b)],['VN','Vietnam'],['ZA',bb(0x3ac)]];for(let G=0x1*0x23ab+0xc*-0x251+-0x7df;G<F[bb(0x348)];G+=0x25a7+-0x3a*-0x2f+-0x304a*0x1){const H=F[bb(0x1d8)](G,G+(0x6+-0x1*-0x1df9+0xca*-0x26))[bb(0x178)](([J,K])=>'\x20\x20'+a0L[bb(0x208)](J)+'\x20\x20'+a0L[bb(0x2ad)](K[bb(0x1fe)](-0x1*-0xa9e+-0x1fa7+0x1519)))[bb(0x3cb)]('');console[bb(0x1dc)](H);}console[bb(0x1dc)](),console[bb(0x1dc)](a0L['gray']('\x20\x2040+\x20countries\x20and\x20regions\x20represented')),console[bb(0x1dc)](a0L[bb(0x2ad)](bb(0x206))),console['log'](),console[bb(0x1dc)](a0L['white'](bb(0x37e))),console[bb(0x1dc)](a0L[bb(0x208)](bb(0x228))+a0L[bb(0x2ad)](bb(0x15a))+a0L['gray'](bb(0x1b4))),console[bb(0x1dc)]();}),B['command'](ba(0x330))[ba(0x297)](ba(0x290))[ba(0x2e2)](async F=>{const bc=ba;a0a6(bc(0x22f)+(F||''));const G=E();if(!G)return;const H=a0ae(bc(0x125));H[bc(0x2f7)]();try{let J=await G['getExams']();if(H[bc(0x279)](),F){const Q=F[bc(0x250)]();J=J[bc(0x283)](U=>U[bc(0x14f)][bc(0x250)]()===Q||bc(0x31f)===U[bc(0x14f)]);}if(!J||-0xe*0x41+-0xc*0x10c+-0x80f*-0x2===J[bc(0x348)]){console['log'](),a0aa(F?bc(0x201)+F[bc(0x250)]()+bc(0x362):bc(0x1b5)),console[bc(0x1dc)](),console[bc(0x1dc)](a0L['white'](bc(0x37a))),console[bc(0x1dc)](a0L[bc(0x2ad)](bc(0x382))),console[bc(0x1dc)](),console[bc(0x1dc)](a0L['gray'](bc(0x248))+a0L[bc(0x3ab)][bc(0x208)](bc(0x139))),console['log']();const U={};U['PE']='es',U['CN']='zh',U['JP']='ja',U['KR']='ko',U['BR']='pt',U['SA']='ar',U['FR']='fr',U['DE']='de',U['IN']='hi',U['ID']='id',U['TH']='th',U['VN']='vi',U['TR']='tr',U['RU']='ru',U['UA']='uk',U['HT']='ht',U['KE']='sw',U['TZ']='sw';const V=U,W=(F||'')[bc(0x250)]();return void(V[W]&&(console[bc(0x1dc)](a0L[bc(0x2ad)](bc(0x3b0)+V[W])),console[bc(0x1dc)]()));}const K=J[bc(0x178)](X=>{const bd=bc,Y=bd(0x224)===X['status']?a0L[bd(0x21c)]:bd(0x1cb)===X[bd(0x185)]?a0L['yellow']:a0L['white'];return[a0L[bd(0x1d5)](X['id']),X[bd(0x1a2)],a0L[bd(0x208)](X[bd(0x14f)]),String(X[bd(0x211)]),X[bd(0x194)]+'\x20min',Y(X[bd(0x185)])];}),L=[...new Set(J['map'](X=>X[bc(0x14f)]))];a0ac(F?bc(0x212)+F[bc(0x250)]():bc(0x271)+L[bc(0x348)]+bc(0x396)),a0ab(['ID',bc(0x291),'Country',bc(0x219),bc(0x2d5),'Status'],K),console[bc(0x1dc)](),console['log'](a0L[bc(0x2ad)]('\x20\x20Start:\x20exam\x20start\x20<id>')),L[bc(0x348)]>-0x1964+-0x1*0xa9+0x1a0e&&console[bc(0x1dc)](a0L[bc(0x2ad)](bc(0x3bf)));}catch(X){H[bc(0x2cd)](bc(0x367)),a0a8(X[bc(0x2c3)]);}}),B[ba(0x127)](ba(0x19e))[ba(0x297)](ba(0x1e0))[ba(0x2e2)](async F=>{const be=ba;a0a6(be(0x314)+F);const G=a0X();if(G)return a0a9(be(0x1bf)+G[be(0x154)]['examName']+be(0x384)),void a0aa(be(0x17c));const H=E();if(!H)return;const J=await a0Q({'message':a0L['white']('Start\x20the\x20exam?\x20The\x20timer\x20will\x20begin\x20immediately.'),'default':!(0x198*-0x4+-0x322+0x982*0x1),'theme':{'prefix':'','style':{'message':K=>K,'defaultAnswer':K=>a0L['green'](K)}}});if(J){console[be(0x1dc)]();try{N(0x1733*0x1+-0x20df+0x9ac,be(0x152));const {session:K,questions:L}=await H['startExam'](F);N(-0x1*0x1ec5+0x7*0x1fd+0x5a8*0x3,be(0x2aa)),await q(-0x1*0x22d2+-0x71d+-0x1b*-0x195),N(-0x10a3+-0x5f3*0x1+0x16d2,'Preparing\x20exam\x20environment...'),await q(-0x1e85+0x2b*0x7+0x8*0x3c4),N(-0x1dc2+-0x1eb3*-0x1+-0x1*0x97,be(0x35a)),await q(0x7fa+-0xc9a+0x536),a0Y({'session':K,'questions':L,'answers':{}}),N(0x627+-0x2070+0x1aad*0x1,be(0x277)),console[be(0x1dc)](),console[be(0x1dc)](),a0ac(K[be(0x28f)]),a0ad(be(0x219),String(K[be(0x211)])),a0ad(be(0x2d5),K['durationMinutes']+'\x20minutes'),a0ad(be(0x128),K[be(0x14f)]),M(),L[be(0x348)]>0x1ec4+-0x4*0x14e+-0xcc6*0x2&&R(L[0x25dd+0x1*0x1327+-0x3904]);}catch(Q){console[be(0x1dc)](),a0a8(Q['message']);}}}),B[ba(0x127)]('q\x20[n]')[ba(0x297)](ba(0x2ef))[ba(0x2e2)](F=>{const bf=ba;a0a6(bf(0x119)+(F||'all'));const G=a0X();if(G){if(M(),F){const H=parseInt(F),J=G['questions'][bf(0x264)](K=>K[bf(0x32e)]===H);if(!J)return void a0a8(bf(0x195)+F+bf(0x1f1)+G[bf(0x386)][bf(0x348)]+').');G[bf(0x1fd)]=H,a0Y(G),R(J,G[bf(0x2e0)][H]);}else{a0ac(''+G[bf(0x154)][bf(0x28f)]);const K=Object[bf(0x166)](G[bf(0x2e0)])[bf(0x348)];a0ad(bf(0x27e),K+'/'+G['session'][bf(0x211)]+'\x20answered'),console[bf(0x1dc)]();for(const L of G['questions']){const Q=G['answers'][L[bf(0x32e)]],U=Q?a0L['green']('['+Q+']'):a0L[bf(0x2ad)](bf(0x2a9)),V=L[bf(0x267)]?a0L[bf(0x2ad)]('\x20['+L[bf(0x267)]+']'):'';console[bf(0x1dc)]('\x20\x20'+U+'\x20'+a0L[bf(0x1d5)]('Q'+L['number'])+V+'\x20'+a0L[bf(0x2ad)](L[bf(0x138)][bf(0x33d)](0x161+-0x2049+0x1ee8,0x4*-0x698+-0x12b2*0x2+0x4000))+(L['text']['length']>-0x89c+0x2486+-0x1bae?bf(0x288):''));}console[bf(0x1dc)]();}}else a0a8('No\x20exam\x20in\x20progress.\x20Use\x20\x22exam\x20start\x20<id>\x22\x20to\x20begin.');}),B[ba(0x127)]('mark\x20[n]')[ba(0x297)](ba(0x21a))['action'](F=>{const bg=ba;a0a6(bg(0x3b7)+(F||''));const G=a0X();if(!G)return void a0a8(bg(0x25c));const H=G[bg(0x386)][bg(0x348)],J=F?parseInt(F,0xf9*0x1a+-0x23fe+0x32*0x37):G['_lastQ']||-0x1ed8+-0x4a9+0x2382;if(isNaN(J)||J<-0x1da4+-0x1d41+0x3ae6||J>H)return void a0a8(bg(0x115)+H+'.');const K=O(G,J);console['log'](),K?console['log'](a0L[bg(0x217)](bg(0x1d1)+J+bg(0x284))):console[bg(0x1dc)](a0L['gray'](bg(0x310)+J+bg(0x14c)));const L=Array['isArray'](G[bg(0x306)])?G[bg(0x306)]:[];L['length']>-0x31d*-0x6+-0x1c9a+0x9ec&&console['log'](a0L[bg(0x2ad)](bg(0x1d3)+L[bg(0x178)](Q=>'Q'+Q)['join'](',\x20'))),console['log'](a0L[bg(0x2ad)](bg(0x364))+a0L[bg(0x1d5)]('exam\x20review')),console[bg(0x1dc)]();}),B[ba(0x127)](ba(0x3b2))['description'](ba(0x140))[ba(0x2e2)](F=>{const bh=ba;a0a6(bh(0x2d3)+(F||''));const G=a0X();if(!G)return void a0a8(bh(0x25c));const H=G[bh(0x386)][bh(0x348)],J=F?parseInt(F,0x263*-0x1+0xf9b+-0xd2e):G[bh(0x1fd)]||-0xda1+0xa2*0x25+0x139*-0x8;isNaN(J)||J<-0xa44+-0x59*-0xb+0x672||J>H?a0a8('Invalid\x20question\x20number.\x20Use\x201..'+H+'.'):j(G,J)?(O(G,J),console['log'](),console[bh(0x1dc)](a0L[bh(0x2ad)]('\x20\x20โ˜†\x20Q'+J+bh(0x14c))),console['log']()):console['log'](a0L[bh(0x2ad)]('\x20\x20Q'+J+bh(0x361)));}),B['command'](ba(0x272))[ba(0x297)]('Go\x20to\x20next\x20question')['action'](()=>{const bi=ba;a0a6('exam\x20next');const F=a0X();if(!F)return void a0a8('No\x20exam\x20in\x20progress.');F[bi(0x386)][bi(0x12c)](K=>!F[bi(0x2e0)][K[bi(0x32e)]]);const G=F[bi(0x1fd)]||0x720+-0x16a*-0x14+-0x3ef*0x9,H=Math['min'](G+(-0x1d37+-0x837+0x256f*0x1),F['questions'][bi(0x348)]);F[bi(0x1fd)]=H,a0Y(F);const J=F[bi(0x386)][H-(0x1c33+-0x10e7+-0x3b*0x31)];R(J,F['answers'][J[bi(0x32e)]]);}),B[ba(0x127)](ba(0x2c9))[ba(0x297)](ba(0x28d))[ba(0x2e2)](()=>{const bj=ba;a0a6(bj(0x23e));const F=a0X();if(!F)return void a0a8(bj(0x25c));const G=F[bj(0x1fd)]||0x1947+0x2b3*0x7+0x1*-0x2c2b,H=Math[bj(0x17a)](G-(0x1bf2+0x24d9+-0x40ca),-0xd62+0x185+-0x1b2*-0x7);F[bj(0x1fd)]=H,a0Y(F);const J=F[bj(0x386)][H-(0x47*-0x4c+0x13*0xd+0x141e)];R(J,F[bj(0x2e0)][J[bj(0x32e)]]);}),B[ba(0x127)](ba(0x1de))[ba(0x297)]('Eliminate\x20one\x20wrong\x20option\x20(limited\x20uses)')[ba(0x2e2)](async()=>{const bk=ba;a0a6('exam\x20help');const F=a0X();if(!F)return void a0a8(bk(0x25c));const G=D(F),H=F[bk(0x1fd)]||-0x1c9*0xa+-0xe8a+0x2065,J=G[bk(0x2b9)][H]||0x119c+0x1f45+-0x2b*0x123,K=G['eliminated'][H]||[],L=F[bk(0x386)]['find'](V=>V[bk(0x32e)]===H);if(!L)return;if(J>=0x23f0+-0x1eb*0x1+0x2203*-0x1)return console[bk(0x1dc)](),console[bk(0x1dc)](a0L[bk(0x217)](bk(0x371))),console[bk(0x1dc)](a0L[bk(0x2ad)](bk(0x1c4))),console[bk(0x1dc)](),void R(L,F['answers'][L[bk(0x32e)]]);const Q=['A','B','C','D']['filter'](V=>!K['includes'](V));if(Q[bk(0x348)]<=0xa*-0x17d+-0x125*0xe+0x6*0x527)return console[bk(0x1dc)](),console[bk(0x1dc)](a0L[bk(0x217)](bk(0x1e7))),console[bk(0x1dc)](),void R(L,F[bk(0x2e0)][L[bk(0x32e)]]);if(G[bk(0x2e9)]>=G['max']){const V=G['max']>=-0x4a+0x131f+0x63d*-0x3,W=-0x234e+0x1*-0xa7b+0x2de7-G[bk(0x17a)];return void(V?(console[bk(0x1dc)](),console[bk(0x1dc)](a0L[bk(0x2ad)](bk(0x37c)+G[bk(0x2e9)]+'/'+G[bk(0x17a)]+bk(0x13c))),console['log']()):(console['log'](),console[bk(0x1dc)](a0L[bk(0x217)]('\x20\x20Help\x20used:\x20'+G[bk(0x2e9)]+'/'+G[bk(0x17a)])),console[bk(0x1dc)](a0L[bk(0x1d5)]('\x20\x20Need\x20more?\x20Type:\x20')+a0L[bk(0x3ab)][bk(0x217)]('more\x20help')+a0L[bk(0x2ad)](bk(0x337)+W+'\x20bonus\x20uses)')),console[bk(0x1dc)]()));}const U=X=>{const bl=bk;K[bl(0x15c)](X),G[bl(0x179)][H]||(G[bl(0x179)][H]=[]),G[bl(0x179)][H]=K,G[bl(0x2b9)][H]=J+(-0x16a6+-0x1*0x1d86+0x342d),G[bl(0x2e9)]++,F[bl(0x353)]=G['used'],F[bl(0x1a4)]=G[bl(0x17a)],F['_helpPerQ']=G[bl(0x2b9)],F[bl(0x2ac)]=G[bl(0x179)],F[bl(0x181)]||(F[bl(0x181)]=[]),F[bl(0x181)][bl(0x15c)]({'ts':new Date()[bl(0x242)](),'q':H,'type':bl(0x3a7),'result':'eliminated\x20'+X}),a0Y(F),console['log'](),console[bl(0x1dc)](a0L[bl(0x217)]('\x20\x20๐Ÿ’ก\x20Option\x20'+X+bl(0x39f))+a0L['gray'](bl(0x1cd)+G[bl(0x2e9)]+'/'+G[bl(0x17a)]+')')),R(L,F['answers'][L[bl(0x32e)]]);};if('demo-free'===F[bk(0x154)]['examId']&&L[bk(0x320)])(X=>{const bm=bk,Y=Q[bm(0x283)](Z=>Z!==X&&!K['includes'](Z));0x8b8+0x6*-0x2e+-0x7a4!==Y[bm(0x348)]&&U(Y[Math[bm(0x30f)](Math[bm(0x16d)]()*Y['length'])]);})(L[bk(0x320)]);else{const X=a0V()[bk(0x36d)]||bk(0x389),Y=F[bk(0x154)]?.['token']||'';try{const Z={};Z[bk(0x1ba)]=bk(0x20b),Z[bk(0x2b4)]=bk(0x270);const a0={};a0[bk(0x21e)]=Y,a0[bk(0x29e)]=H,a0['eliminated']=K;const a1=await fetch(X+bk(0x3a9)+F[bk(0x154)][bk(0x1c0)]+bk(0x29d),{'method':bk(0x207),'headers':Z,'body':JSON[bk(0x1f7)](a0),'signal':AbortSignal[bk(0x1bb)](-0x1bdf+0x154c+0x1a1b)}),a2=await a1[bk(0x3a1)](),a3=a2?.['data']?.[bk(0x236)];a3&&['A','B','C','D'][bk(0x22b)](a3)?U(a3):(console[bk(0x1dc)](),console[bk(0x1dc)](a0L['gray']('\x20\x20Server\x20did\x20not\x20return\x20a\x20valid\x20option.\x20Try\x20again.')));}catch{console['log'](),console[bk(0x1dc)](a0L[bk(0x2ad)](bk(0x1fb)));}}}),B[ba(0x127)]('more-help')[ba(0x297)]('Unlock\x20hidden\x20bonus\x20help\x20uses')[ba(0x2e2)](()=>{const bn=ba;a0a6(bn(0x311));const F=a0X();if(!F)return void a0a8(bn(0x25c));const G=D(F),H='demo-free'===F[bn(0x154)][bn(0x1c0)]?-0xe01+-0x121e+0x2027*0x1:0xd03+0x1781+-0x2466;if(G[bn(0x17a)]>=H)return void console[bn(0x1dc)](a0L['gray'](bn(0x218)));if(G[bn(0x2e9)]<G[bn(0x17a)])return void console[bn(0x1dc)](a0L['gray'](bn(0x11c)+(G[bn(0x17a)]-G['used'])+bn(0x20c)));const J=H-G[bn(0x17a)];F[bn(0x1a4)]=H,a0Y(F),console['log'](),console[bn(0x1dc)](a0L['green']('\x20\x20๐ŸŽ\x20+'+J+'\x20bonus\x20help\x20unlocked!\x20('+J+'/'+H+bn(0x245))),console[bn(0x1dc)](a0L[bn(0x2ad)](bn(0x176))),console[bn(0x1dc)]();}),B[ba(0x127)](ba(0x36c))['description'](ba(0x2a8))[ba(0x2e2)](async(F,G)=>{const bo=ba,H=G['join']('\x20');a0a6(bo(0x184)+F+'\x20'+H);const J=a0X();if(!J)return void a0a8('No\x20exam\x20in\x20progress.\x20Use\x20\x22exam\x20start\x20<id>\x22\x20to\x20begin.');if(!(function(){const bp=bo,Y=a0a0();if(!Y)return!(0xb5d*-0x2+0x6fa+-0x3*-0x540);if(new Date()>=Y){const Z=a0X();if(Z){const a0=Object[bp(0x166)](Z[bp(0x2e0)])[bp(0x348)];'demo-free'===Z['session'][bp(0x1c0)]||-0x1654+0xd90+0x8c4===a0?(a0Z(),a0a9(bp(0x3b6)),a0aa(bp(0x2fb))):(a0a8('Time\x20expired.'),a0aa('Type\x20\x22exam\x20submit\x22\x20to\x20finalise\x20now,\x20or\x20the\x20server\x20will\x20auto-finalise\x20within\x2030\x20minutes.'));}return!(-0x54d*0x5+-0x1598+0x5e*0x83);}return!(0x1376*-0x1+-0x67d+-0x49*-0x5b);}()))return;const K=parseInt(F),L=J['questions']['find'](Y=>Y[bo(0x32e)]===K);if(!L)return void a0a8('Question\x20'+F+bo(0x1f1)+J[bo(0x386)][bo(0x348)]+').');let Q;if(bo(0x276)===L['type']||'ctf4ai'===L[bo(0x1a8)]||L['options']&&!L[bo(0x2c8)]['A']&&!L['options']['B']){if(Q=H[bo(0x352)](),!Q)return void a0a8(bo(0x16c));if(/^[A-Da-d]$/[bo(0x26f)](Q))return a0a8('Q'+K+bo(0x2f3)),void console['log'](a0L[bo(0x2ad)]('\x20\x20Example:\x20')+a0L[bo(0x21c)](bo(0x184)+K+bo(0x254)));}else{if(Q=H['toUpperCase'](),!['A','B','C','D'][bo(0x22b)](Q))return void a0a8('Choice\x20must\x20be\x20A,\x20B,\x20C,\x20or\x20D.');}if('demo-free'===J[bo(0x154)][bo(0x1c0)]&&-0x14a2+-0x3*-0xc89+-0x10f7===K&&0x1a17+0xc41+-0x2*0x132c===(D(J)[bo(0x2b9)][0x1*-0x515+-0x36e+0x885]||-0x1b4a+-0x228*0x1+-0xeb9*-0x2))return console[bo(0x1dc)](),console[bo(0x1dc)](a0L['yellow']('\x20\x20๐Ÿ’ก\x20Try\x20typing\x20')+a0L['bold']['yellow'](bo(0x1de))+a0L[bo(0x217)]('\x20first\x20to\x20see\x20what\x20it\x20does!')),console[bo(0x1dc)](a0L['gray'](bo(0x225))),void console[bo(0x1dc)]();const U=J['answers'][K];J[bo(0x181)]||(J[bo(0x181)]=[]),J[bo(0x181)][bo(0x15c)]({'ts':new Date()[bo(0x242)](),'q':K,'type':U?bo(0x24e):'answer_submitted','input':Q,'result':U?'changed\x20from\x20'+U:'new'}),J['answers'][K]=Q,J[bo(0x1fd)]=K,a0Y(J),console['log'](a0L['gray'](bo(0x2a6)));const V=J[bo(0x154)][bo(0x21e)];if(V&&bo(0x159)!==J[bo(0x154)][bo(0x1c0)]){const Y=(a0V()[bo(0x36d)]||bo(0x389))+'/api/icoa/exams/'+J[bo(0x154)][bo(0x1c0)]+'/answer',Z={};Z[bo(0x1ba)]=bo(0x20b);const a0={};a0['token']=V,a0[bo(0x29e)]=K,a0[bo(0x320)]=Q,fetch(Y,{'method':bo(0x207),'headers':Z,'body':JSON['stringify'](a0),'signal':AbortSignal[bo(0x1bb)](-0x289*-0x7+0x261c+-0x2453)})[bo(0x14e)](()=>{});}const W=Object['keys'](J['answers'])[bo(0x348)],X=J['session'][bo(0x211)];if(a0a7('Q'+K+':\x20'+Q+'\x20โœ“\x20\x20('+W+'/'+X+bo(0x30a)),bo(0x159)===J[bo(0x154)][bo(0x1c0)]&&L['answer']){const a1=[bo(0x232),bo(0x2c7),bo(0x289),bo(0x338),bo(0x379)],a2=['Close\x20โ€”\x20we\x27ll\x20revisit\x20this\x20one.',bo(0x1e6),bo(0x2a7)],a3=Q===L[bo(0x320)]?a1:a2,a4=a3[Math[bo(0x30f)](Math[bo(0x16d)]()*a3[bo(0x348)])];console[bo(0x1dc)](a0L['gray'](bo(0x18a)+a4));}if(K<J[bo(0x386)][bo(0x348)]){const a5=J['questions'][K];J[bo(0x1fd)]=a5[bo(0x32e)],a0Y(J),R(a5,J[bo(0x2e0)][a5['number']]);}else W>=J[bo(0x386)][bo(0x348)]&&(console['log'](),console[bo(0x1dc)](a0L[bo(0x21c)][bo(0x3ab)]('\x20\x20๐ŸŽ‰\x20All\x20questions\x20answered!')),console[bo(0x1dc)](),'demo-free'===J['session'][bo(0x1c0)]?(console['log'](a0L[bo(0x1d5)]('\x20\x20Type\x20')+a0L[bo(0x3ab)]['cyan'](bo(0x18e))+a0L[bo(0x1d5)](bo(0x29f))),console['log']()):console[bo(0x1dc)](a0L[bo(0x1d5)](bo(0x312))));}),B['command'](ba(0x309))[ba(0x297)](ba(0x2f1))[ba(0x2e2)](()=>{const bq=ba;a0a6(bq(0x29c));const F=a0X();if(!F)return void a0a8('No\x20exam\x20in\x20progress.');a0ac('Review:\x20'+F[bq(0x154)][bq(0x28f)]),M(),console['log']();let G='\x20\x20';const H=Array[bq(0x2e1)](F[bq(0x306)])?F['bookmarks']:[];for(const Q of F[bq(0x386)]){const U=F[bq(0x2e0)][Q['number']],V=H[bq(0x22b)](Q[bq(0x32e)]),W=V?'โ˜…':'\x20';G+=U?V?a0L[bq(0x3ab)][bq(0x217)]('Q'+String(Q['number'])[bq(0x16f)](-0x1124+0x1094+-0x49*-0x2)+W+'['+U+']\x20'):a0L[bq(0x21c)]('Q'+String(Q[bq(0x32e)])[bq(0x16f)](-0x1c98+-0x215+0x1eaf)+'\x20['+U+bq(0x3af)):V?a0L[bq(0x3ab)][bq(0x217)]('Q'+String(Q[bq(0x32e)])['padStart'](-0x1bb5*0x1+0x175*0x3+-0x3e4*-0x6)+W+bq(0x27b)):a0L[bq(0x217)]('Q'+String(Q['number'])['padStart'](-0xb24+-0x3ed+0xe3*0x11)+bq(0x205)),Q['number']%(0x20a9+0x2434+0x44d7*-0x1)==-0x8*-0x3cb+0x4d7+0x1*-0x232f&&(console['log'](G),G='\x20\x20');}G[bq(0x352)]()&&console[bq(0x1dc)](G);const J=Object[bq(0x166)](F['answers'])[bq(0x348)],K=F[bq(0x154)][bq(0x211)],L=K-J;if(console[bq(0x1dc)](),a0ad('Answered',a0L[bq(0x21c)][bq(0x3ab)](J+'/'+K)),L>0x250b+0x1d8*-0x13+-0x203){const X=F[bq(0x386)][bq(0x283)](Y=>!F[bq(0x2e0)][Y[bq(0x32e)]])[bq(0x178)](Y=>Y[bq(0x32e)])[bq(0x3cb)](',\x20');a0ad(bq(0x354),a0L[bq(0x217)](L+bq(0x1fa)+X+')'));}H['length']>-0x1*-0x1f13+0xfe7+-0x2efa&&a0ad('Flagged\x20for\x20review',a0L[bq(0x3ab)]['yellow']('โ˜…\x20'+H[bq(0x348)])+a0L[bq(0x2ad)]('\x20('+H[bq(0x178)](Y=>'Q'+Y)[bq(0x3cb)](',\x20')+')')),console['log'](),H[bq(0x348)]>-0x2*0xc9f+-0x6*-0x46b+-0x51*0x4&&console['log'](a0L[bq(0x2ad)](bq(0x36e))+a0L[bq(0x1d5)](bq(0x119)+H[-0xaf1*0x1+0x1787+0xb3*-0x12])),console[bq(0x1dc)](a0L[bq(0x2ad)](bq(0x3b8))+a0L[bq(0x3ab)]['cyan'](bq(0x18e))+a0L[bq(0x2ad)]('\x20to\x20submit\x20for\x20grading.'));}),B['command'](ba(0x124))[ba(0x297)](ba(0x12d))['action'](async J=>{const br=ba;a0a6(br(0x327)+(J||''));const K=a0X();if(K&&br(0x158)===J&&(K[br(0x1f0)]=!(0x1*0x26d3+-0x107e+0x1655*-0x1),a0Y(K)),!K)return void a0a8(br(0x25c));const L=Object['keys'](K[br(0x2e0)])[br(0x348)],Q=K[br(0x154)][br(0x211)],U=Q-L;if(0x96b+-0x1*-0xa3b+0x13a6*-0x1===L){const W=a0a0(),X=W&&new Date()>=W;return void(br(0x159)===K[br(0x154)][br(0x1c0)]||X?(a0Z(),a0a9('No\x20answers\x20to\x20submit.\x20Exam\x20cleared.'),a0aa(br(0x2fb))):(a0a9('No\x20answers\x20to\x20submit.'),a0aa('Answer\x20some\x20questions\x20first:\x20exam\x20q\x201')));}if(br(0x159)!==K['session'][br(0x1c0)]){const Y=!(0x1*-0x2089+-0x174f+0x37d8)===K[br(0x1f0)],Z=a0a0(),a0=!!(Z&&new Date()>=Z);if(!Y&&!a0){if(console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x281)](br(0x22a))),console[br(0x1dc)](a0L[br(0x3ab)][br(0x281)](br(0x359))),console[br(0x1dc)](a0L['red'](br(0x22a))),console['log'](),console[br(0x1dc)](a0L[br(0x1d5)](br(0x23c)+a0L[br(0x3ab)][br(0x21c)](L+'/'+Q)+br(0x3a2))),U>0x1c22*-0x1+-0x947*0x3+0x37f7){const a2=K[br(0x386)][br(0x283)](a3=>!K['answers'][a3[br(0x32e)]])[br(0x178)](a3=>'Q'+a3[br(0x32e)])[br(0x3cb)](',\x20');console[br(0x1dc)](a0L[br(0x217)]('\x20\x20'+U+br(0x21d)+a2));}const a1=Array[br(0x2e1)](K[br(0x306)])?K[br(0x306)]:[];return a1['length']>0x38b*0x1+-0x1024+0x433*0x3&&console[br(0x1dc)](a0L['yellow'](br(0x3c0)+a1[br(0x348)]+'\x20still\x20flagged\x20for\x20review:\x20'+a1['map'](a3=>'Q'+a3)['join'](',\x20'))),console[br(0x1dc)](),console['log'](a0L[br(0x1d5)](br(0x318))+a0L[br(0x3ab)][br(0x281)](br(0x26d))+a0L[br(0x1d5)]('\x20โ€”\x20you\x20cannot\x20change\x20them\x20after\x20submit.')),console['log'](),console[br(0x1dc)](a0L[br(0x3ab)][br(0x1d5)](br(0x260))+a0L[br(0x3ab)][br(0x208)]('exam\x20submit\x20confirm')),console[br(0x1dc)](a0L[br(0x1d5)](br(0x308))+a0L[br(0x208)]('exam\x20review')+a0L['gray'](br(0x25b))+a0L[br(0x208)](br(0x1ab))),void console[br(0x1dc)]();}Y&&(delete K[br(0x1f0)],a0Y(K));}if(console[br(0x1dc)](),br(0x159)===K[br(0x154)]['examId']){try{N(0xe5c+-0x2*-0x766+-0x1d28,a0ag('grading')),await q(0x311+-0x1*0x1d2c+0x1*0x1b47);let a3=-0x1e53+-0x1e57+0x3caa;const a4=[],a5={};for(const ah of K[br(0x386)]){const ai=ah[br(0x267)]||br(0x39a),aj={};aj[br(0x399)]=0x0,aj[br(0x3c1)]=0x0,(a5[ai]||(a5[ai]=aj),a5[ai][br(0x3c1)]++);const ak=K[br(0x2e0)][ah[br(0x32e)]];ak&&ah[br(0x320)]&&ak===ah['answer']?(a3++,a5[ai][br(0x399)]++):a4[br(0x15c)](ah[br(0x32e)]);}a4['sort']((al,am)=>al-am),N(-0x1e96+0x9d3+-0x13*-0x11d,a0ag(br(0x2df))),console['log']();const a6=new Date(K[br(0x154)]['startedAt'])[br(0x21f)](),a7=Math[br(0x17a)](-0x108b+0x2*-0xb51+-0x272d*-0x1,Math[br(0x17f)]((Date[br(0x2dd)]()-a6)/(0x9b7*0x3+0x65e*-0x3+0x1*-0x623))),a8=Math[br(0x30f)](a7/(-0xb*-0x161+-0x1c8f+0xda0))+'m\x20'+a7%(0xe0f*0x1+0x20ea+-0x2ebd)+'s',a9=[...K[br(0x386)]],aa={...K[br(0x2e0)]},ab=D(K);a0Z();const ac=Math[br(0x17f)](a3/Q*(0x1a*-0x167+-0x10+0x24ea)),ad=a0V(),ae={};ae['Content-Type']=br(0x20b),(fetch(br(0x1b9),{'method':br(0x207),'headers':ae,'body':JSON['stringify']({'type':'demo-exam','score':a3,'total':Q,'lang':ad['language']||'en','help_used':ab['used'],'help_max':ab[br(0x17a)],'tokens_used':0x0,'solved':ac>=-0xf70+-0xc11*-0x2+-0x876?0x1e87+0x21e*0x2+0x5cb*-0x6:-0x9b2+0xa42+0x10*-0x9,'timestamp':new Date()[br(0x242)](),'interactions':K['interactions']||[]}),'signal':AbortSignal[br(0x1bb)](0x1de+0x8*0x60+0xeaa)})[br(0x14e)](()=>{}),console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x208)]('\x20\x20โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•')),console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x3ab)]['white'](br(0x2ab))),console[br(0x1dc)](a0L['bold'][br(0x1d5)](br(0x2fc))),console[br(0x1dc)](a0L['bold'][br(0x1d5)](br(0x2ec))),console[br(0x1dc)](a0L[br(0x3ab)][br(0x1d5)]('\x20\x20\x20โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘\x20\x20\x20\x20\x20โ–ˆโ–ˆโ•‘\x20\x20\x20โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘')),console[br(0x1dc)](a0L[br(0x3ab)]['white'](br(0x247))),console[br(0x1dc)](a0L[br(0x3ab)]['white'](br(0x2c2))),console['log'](),console[br(0x1dc)](a0L[br(0x3ab)](br(0x1c2)+a0ag(br(0x1cc))+':\x20'+a3+'/'+Q+'\x20('+ac+'%)')),console[br(0x1dc)](a0L[br(0x3ab)](br(0x1c2)+(ac>=0x417+-0x1268+0xe8d?a0L[br(0x21c)](a0ag('passed')):a0L[br(0x281)](a0ag(br(0x259)))))),console['log'](a0L[br(0x2ad)](br(0x17b)+a8)),console[br(0x1dc)](),console['log'](a0L['yellow'](br(0x2bc))),console['log'](a0L[br(0x2ad)](br(0x1c3))),console['log'](),console[br(0x1dc)](a0L[br(0x208)](br(0x25e))),await q(-0x1*0x298+0x3d*0x65+-0x9c1));const af=Object[br(0x213)](a5);if(af[br(0x348)]>0x1*-0xa63+-0x6+0xa69){console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x3ab)][br(0x1d5)]('\x20\x20By\x20category'));const al=-0x99f+-0x1*0x23bc+-0x1*-0x2d65;for(const [am,an]of af){const ao=an[br(0x3c1)]>0xf4*0x1f+-0x86b*0x1+-0x1521*0x1?Math[br(0x17f)](an[br(0x399)]/an['total']*(0x1645*0x1+-0x15a8+-0x3*0x13)):0x1*0x964+0x12b4+-0x1c18,ap=an[br(0x3c1)]>-0x217*-0x7+-0x20d3*0x1+-0x11*-0x112?Math[br(0x17f)](an[br(0x399)]/an[br(0x3c1)]*al):-0x1e67+-0x1d0d+-0xa*-0x5f2,aq=al-ap,ar=ao>=-0x42*-0x7+0x2609+-0x2787?a0L[br(0x21c)]:ao>=0x3*-0x8cb+-0x229d+-0x4*-0xf4c?a0L[br(0x217)]:a0L[br(0x281)],as=ar('โ–ˆ'[br(0x148)](ap))+a0L['gray']('โ–‘'[br(0x148)](aq));console['log'](br(0x2f9)+a0L[br(0x1d5)](am[br(0x1fe)](-0x48c+-0x3*-0x1ef+-0x12d))+'\x20'+as+'\x20\x20'+ar(an[br(0x399)]+'/'+an['total'])+a0L[br(0x2ad)]('\x20('+ao+'%)'));}console[br(0x1dc)](),console[br(0x1dc)](a0L['cyan'](br(0x153)));}const ag=a0a2(a3,Q);if(a3===ag['best']&&ag[br(0x1e5)]>0x3*-0x937+-0x9ba+-0x1*-0x2560&&a3>-0x1*0x154f+-0xbf6+0x2145&&(console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x3ab)]['green']('\x20\x20โ˜…\x20New\x20personal\x20best!'))),a4[br(0x348)]>0x61*-0x21+0x20d2+0x1451*-0x1){const at=a9['filter'](au=>a4[br(0x22b)](au[br(0x32e)]))[br(0x178)](au=>({...au}));a0a3(at);}else a0a5();if(await q(0x3d*-0x6+0x24f8+0x17d2*-0x1),a4[br(0x348)]>0xfcd+0x1*0x205+0x8e9*-0x2){console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x217)]('\x20\x20'+a4['length']+'\x20'+a0ag(br(0x193)))),console[br(0x1dc)]();for(const au of a4){const av=a9[br(0x264)](ay=>ay[br(0x32e)]===au);if(!av)continue;const aw=aa[au],ax=av[br(0x320)];console[br(0x1dc)](a0L[br(0x1d5)](br(0x244)+au+'.\x20'+av[br(0x138)])),aw?console[br(0x1dc)](a0L[br(0x281)](br(0x2f9)+a0ag('yourAnswer')+':\x20'+aw+'.\x20'+av[br(0x2c8)][aw])):console['log'](a0L['yellow'](br(0x2f9)+a0ag(br(0x278))+br(0x2c1))),ax&&console[br(0x1dc)](a0L[br(0x21c)](br(0x2f9)+a0ag(br(0x399))+':\x20'+ax+'.\x20'+av['options'][ax])),av[br(0x221)]&&console[br(0x1dc)](a0L['gray']('\x20\x20\x20\x20โ†’\x20'+av[br(0x221)])),console[br(0x1dc)]();}}else console[br(0x1dc)](),console[br(0x1dc)](a0L['green']('\x20\x20'+a0ag(br(0x17d))));await q(-0x483+0x10*0x151+-0x8bd),console[br(0x1dc)](),console['log'](a0L[br(0x208)](br(0x153))),console[br(0x1dc)](a0L['white']('\x20\x20'+a0ag(br(0x268))+'\x20')+a0L[br(0x3ab)][br(0x21c)](br(0x276))+a0L['gray'](br(0x322)+a0ag(br(0x257)))),console[br(0x1dc)](a0L[br(0x2ad)]('\x20\x20'+a0ag(br(0x26a)))),console[br(0x1dc)](a0L[br(0x208)](br(0x153))),console[br(0x1dc)]();}catch(ay){console[br(0x1dc)](),a0a8(ay['message']);}return;}const V=K[br(0x154)][br(0x21e)];try{let az;if(N(0xcc7*-0x1+-0x4*-0x522+0x18d*-0x5,br(0x11e)),V){const aJ={};aJ[br(0x1ba)]=br(0x20b);const aK={};aK[br(0x276)]=0x0,aK[br(0x1cf)]=0x0;const aL=a0V()['ctfdUrl']||br(0x389),aM=await fetch(aL+br(0x3a9)+K[br(0x154)]['examId']+br(0x34e),{'method':br(0x207),'headers':aJ,'body':JSON[br(0x1f7)]({'token':V,'answers':K[br(0x2e0)],'interactions':K['interactions']||[],'aiUsage':K[br(0x192)]||aK,'bookmarks':K[br(0x306)]||[]}),'signal':AbortSignal[br(0x1bb)](-0x1*0x407a+0xb65+-0x17*-0x4db)});if(!aM['ok']){const aN=await aM[br(0x3a1)]()[br(0x14e)](()=>({'message':'Submit\x20failed'}));throw new Error(aN[br(0x2c3)]||br(0x16e));}az=(await aM[br(0x3a1)]())[br(0x1da)];}else{const aO=E();if(!aO)return;az=await aO[br(0x12b)](K[br(0x154)][br(0x1c0)],K[br(0x2e0)]);}N(-0x1b40+-0x428*0x6+0x3462,br(0x22c)),await q(0x1e9b+0x445*0x1+-0x21b4),N(0x64d+-0x1eb+-0x3fe,br(0x13f)),console[br(0x1dc)]();const aA={};aA[br(0x276)]=0x0,aA[br(0x1cf)]=0x0;const aB={'answered':Object[br(0x166)](K[br(0x2e0)])[br(0x348)],'total':K[br(0x154)][br(0x211)],'bookmarks':Array[br(0x2e1)](K['bookmarks'])?K[br(0x306)][br(0x348)]:0x15f1+-0x1*-0x9a9+0x652*-0x5,'help':D(K),'aiUsage':K[br(0x192)]||aA,'interactions':(K[br(0x181)]||[])[br(0x348)],'durationMin':K[br(0x154)][br(0x194)]||-0x1*-0x351+-0x196*0x8+0x95f,'confirmedAt':K[br(0x154)][br(0x373)]||K[br(0x154)][br(0x1d7)],'examId':K['session'][br(0x1c0)]};a0Z();const aC=Math[br(0x17a)](-0xeab+0xd73+-0x27*-0x8,Math[br(0x17f)]((Date[br(0x2dd)]()-new Date(aB[br(0x373)])['getTime']())/(-0x1ac0+0x6*-0xce+0x237c))),aD=Math[br(0x30f)](aC/(0x1*0x355+-0x1c9*-0x1+-0x4e2)),aE=aC%(-0x743+0x718+-0x67*-0x1);console['log'](),console[br(0x1dc)](a0L[br(0x208)](br(0x2ba))),console['log'](),console[br(0x1dc)](a0L[br(0x3ab)]['white'](br(0x2ab))),console[br(0x1dc)](a0L[br(0x3ab)][br(0x1d5)](br(0x2fc))),console['log'](a0L[br(0x3ab)][br(0x1d5)](br(0x2ec))),console[br(0x1dc)](a0L[br(0x3ab)][br(0x1d5)]('\x20\x20\x20โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘\x20\x20\x20\x20\x20โ–ˆโ–ˆโ•‘\x20\x20\x20โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘')),console['log'](a0L[br(0x3ab)][br(0x1d5)](br(0x247))),console[br(0x1dc)](a0L[br(0x3ab)][br(0x1d5)](br(0x2c2))),console[br(0x1dc)]();const aF=az[br(0x301)]?a0L[br(0x21c)][br(0x3ab)](br(0x34c)):a0L[br(0x217)][br(0x3ab)]('\x20\x20\x20โ—‹\x20\x20Submission\x20accepted');console['log'](aF),console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x1d5)](br(0x20d))),console[br(0x1dc)](a0L[br(0x1d5)]('\x20\x20\x20exam\x20center.\x20Final\x20scoring\x20and\x20selection\x20are\x20decided\x20by')),console[br(0x1dc)](a0L['white'](br(0x33f))),console[br(0x1dc)](),console['log'](a0L[br(0x208)](br(0x153))),console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x3ab)][br(0x1d5)]('\x20\x20Your\x20run'));const aG=aB[br(0x3c1)]-aB[br(0x35b)];console['log'](br(0x2f9)+a0L[br(0x2ad)](br(0x2a0))+a0L[br(0x1d5)](aB[br(0x35b)]+'/'+aB['total'])+(aG>-0x126e+0x1283+-0x15?a0L[br(0x2ad)]('\x20\x20ยท\x20\x20'+aG+br(0x17e)):'')),console['log']('\x20\x20\x20\x20'+a0L[br(0x2ad)]('Time\x20used:\x20')+a0L[br(0x1d5)](aD+'m\x20'+aE+'s')+a0L[br(0x2ad)](br(0x2c0)+aB[br(0x302)]+br(0x210))),aB[br(0x306)]>0x1c8a+-0x262*0xa+0x9*-0x86&&console[br(0x1dc)](br(0x2f9)+a0L['gray'](br(0x2ed))+a0L[br(0x217)]('โ˜…\x20'+aB[br(0x306)])),console[br(0x1dc)]();const aH=az[br(0x1e4)];if(aH&&Object[br(0x166)](aH)[br(0x348)]>=0x89e*0x1+-0x1597+0x1*0xcfb){const aP=Object[br(0x213)](aH)[br(0x283)](([,aU])=>aU[br(0x3c1)]>-0x3be*-0x1+0x235a*0x1+-0x2718)['map'](([aU,aV])=>({'cat':aU,'pct':aV[br(0x399)]/aV[br(0x3c1)]}))[br(0x326)]((aU,aV)=>aV[br(0x177)]-aU[br(0x177)]),aQ=Math[br(0x2ca)](-0x651+-0xb49+0x119c,Math[br(0x12f)](aP[br(0x348)]/(0x1286*-0x1+0x224b+-0x1*0xfc2))),aR=Math['min'](0x11e3*-0x1+-0x1a2e+0x2c13,Math[br(0x12f)](aP[br(0x348)]/(0x135c+-0x59f+-0xdba*0x1))),aS=aP[br(0x1d8)](0x261+0xc8+-0x329,aQ)[br(0x178)](aU=>aU[br(0x198)]),aT=aP[br(0x1d8)](-aR)['map'](aU=>aU[br(0x198)]);aS[br(0x215)](aU=>aT[br(0x22b)](aU))||(console['log'](a0L[br(0x3ab)]['white']('\x20\x20Directional\x20feedback')),console[br(0x1dc)](br(0x2f9)+a0L[br(0x21c)]('Stronger\x20here:\x20\x20')+a0L[br(0x1d5)](aS['join'](',\x20'))),console[br(0x1dc)]('\x20\x20\x20\x20'+a0L['yellow'](br(0x1b2))+a0L['white'](aT[br(0x3cb)](',\x20'))),console['log'](a0L[br(0x2ad)](br(0x18d))),console[br(0x1dc)]());}console['log'](a0L[br(0x3ab)][br(0x1d5)](br(0x28b))),console['log']('\x20\x20\x20\x20'+a0L[br(0x2ad)](br(0x2d7))+a0L[br(0x1d5)](aB[br(0x1de)][br(0x2e9)]+'/'+aB['help']['max'])),console[br(0x1dc)](br(0x2f9)+a0L['gray'](br(0x249))+a0L[br(0x1d5)](aB[br(0x192)][br(0x276)]+br(0x149))+a0L[br(0x2ad)]('\x20\x20ยท\x20\x20')+a0L[br(0x1d5)](aB[br(0x192)][br(0x1cf)]+br(0x23d))),console[br(0x1dc)](),console[br(0x1dc)](a0L['cyan'](br(0x153))),console['log'](),console[br(0x1dc)](a0L[br(0x3ab)][br(0x1d5)]('\x20\x20What\x20next')),console['log'](a0L['gray']('\x20\x20ยท\x20Your\x20national\x20organizer\x20will\x20announce\x20selection\x20results.')),console[br(0x1dc)](a0L['gray'](br(0x19b))+a0L['white']('demo')+a0L['gray'](br(0x380))),console[br(0x1dc)](a0L[br(0x2ad)](br(0x1eb))+a0L['white'](br(0x372))),console[br(0x1dc)]();const {showCToBUpgradePrompt:aI}=await import(br(0x3c6));aI(aB['examId'],az[br(0x301)]),console['log'](a0L[br(0x217)](br(0x393))),console[br(0x1dc)](a0L[br(0x208)]['underline'](br(0x1c8))),console[br(0x1dc)](),console[br(0x1dc)](a0L[br(0x208)](br(0x2ba))),console[br(0x1dc)]();}catch(aU){console[br(0x1dc)](),a0a8(aU[br(0x2c3)]);}}),B[ba(0x127)](ba(0x2d2))['description'](ba(0x24b))['action'](async F=>{const bs=ba;a0a6('exam\x20result\x20'+(F||''));const G=E();if(!G)return;if(!F){const J=a0X();if(!J)return void a0a8(bs(0x2cc));F=J[bs(0x154)][bs(0x1c0)];}const H=a0ae(bs(0x39d));H[bs(0x2f7)]();try{const K=await G[bs(0x363)](F);H['succeed'](bs(0x25f)),console['log'](),a0ac(K[bs(0x28f)]),a0ad(bs(0x29a),a0L[bs(0x3ab)](K['score']+'/'+K[bs(0x3c1)])),a0ad(bs(0x377),a0L[bs(0x3ab)](K[bs(0x328)]+'%')),a0ad(bs(0x368),K[bs(0x301)]?a0L[bs(0x21c)][bs(0x3ab)](bs(0x1ea)):a0L[bs(0x281)]['bold'](bs(0x3c2))),a0ad(bs(0x30c),K[bs(0x171)]),console[bs(0x1dc)]();}catch(L){H['fail']('Failed\x20to\x20load\x20result'),a0a8(L[bs(0x2c3)]);}}),B[ba(0x127)]('token\x20<code>')[ba(0x297)](ba(0x26e))['action'](async H=>{const bt=ba;a0a6('exam\x20token\x20'+H);const {getRealExamState:J,saveExamState:K,clearExamState:L}=await import(bt(0x241)),Q=J();if(Q){const a0=Q[bt(0x154)][bt(0x21e)],a1=a0a0(),a2=!!a0&&a0['trim']()[bt(0x250)]()!==H[bt(0x352)]()[bt(0x250)](),a3=!!a1&&a1[bt(0x21f)]()+(-0x347d*0xa2+-0xffcc1*-0x1+0x2a109*0x11)<Date['now']();if(a2&&a3)return L(),console['log'](),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x3c3))),console[bt(0x1dc)](),console[bt(0x1dc)](a0L[bt(0x1d5)]('\x20\x20Please\x20re-type:\x20')+a0L[bt(0x3ab)][bt(0x208)]('exam\x20'+H)),void console[bt(0x1dc)]();if(!a0&&H)return Q[bt(0x154)][bt(0x21e)]=H[bt(0x352)](),K(Q),console[bt(0x1dc)](),console[bt(0x1dc)](a0L[bt(0x21c)](bt(0x21b))),console['log'](a0L[bt(0x2ad)](bt(0x165))+a0L['white'](bt(0x173))+a0L[bt(0x2ad)](bt(0x129))+a0L[bt(0x1d5)](bt(0x18e))),console['log'](),console[bt(0x1dc)](a0L[bt(0x3ab)][bt(0x1d5)](bt(0x12a))),console[bt(0x1dc)](a0L['gray'](bt(0x209))+a0L[bt(0x3ab)][bt(0x208)](bt(0x3b3))+a0L[bt(0x2ad)](bt(0x344))),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x209))+a0L['bold'][bt(0x208)](bt(0x29c))+a0L[bt(0x2ad)]('\x20\x20\x20see\x20progress\x20+\x20flagged\x20items')),void console[bt(0x1dc)]();const a4=Object[bt(0x166)](Q[bt(0x2e0)])['length'],a5=Q[bt(0x154)][bt(0x211)],a6=a0a0(),a7=a6?Math['max'](-0xa2e*0x2+-0x16d2+-0x1*-0x2b2e,Math[bt(0x17f)]((a6[bt(0x21f)]()-Date[bt(0x2dd)]())/(-0x1037*0x4+-0x7*-0x1b82+0xa*0xaab))):null;return console['log'](),console['log'](a0L[bt(0x217)](bt(0x117))),console[bt(0x1dc)](),console['log'](a0L[bt(0x2ad)](bt(0x30b))+a0L['white'](Q[bt(0x154)][bt(0x28f)])),a0&&console[bt(0x1dc)](a0L['gray'](bt(0x1aa))+a0L['white'](a0)+(a0===H?a0L['green'](bt(0x2fe)):a0L[bt(0x217)]('\x20\x20(different\x20from\x20the\x20one\x20you\x20typed)'))),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x1b7))+a0L[bt(0x1d5)](a4+'/'+a5)),null!==a7&&console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x258))+a0L['white'](a7+'\x20min\x20remaining')),console[bt(0x1dc)](),console[bt(0x1dc)](a0L[bt(0x3ab)][bt(0x1d5)](bt(0x370))),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x209))+a0L[bt(0x3ab)][bt(0x208)]('exam\x20q\x201')+a0L[bt(0x2ad)](bt(0x344))),console['log'](a0L[bt(0x2ad)]('\x20\x20\x20\x20โ†’\x20')+a0L[bt(0x3ab)][bt(0x208)](bt(0x29c))+a0L[bt(0x2ad)](bt(0x398))),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x209))+a0L[bt(0x3ab)][bt(0x208)]('exam\x20submit')+a0L['gray']('\x20\x20\x20finish\x20and\x20submit')),a0&&a0!==H&&(console[bt(0x1dc)](),console[bt(0x1dc)](a0L['yellow']('\x20\x20Note:\x20you\x20typed\x20a\x20different\x20token.\x20Each\x20exam\x20token\x20is\x20bound\x20to')),console[bt(0x1dc)](a0L[bt(0x217)](bt(0x235))),console[bt(0x1dc)](),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x1ef))),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x2b5))+a0L[bt(0x3ab)]['cyan'](bt(0x243))+a0L[bt(0x2ad)](bt(0x234)))),void console[bt(0x1dc)]();}const {isNativeWindowsCmd:U}=await import(bt(0x28e));if(!U()&&!a0ak())return console['log'](),a0a9(bt(0x2b1)),console[bt(0x1dc)](a0L['gray']('\x20\x20โ†’\x20')+a0L['bold'][bt(0x208)](bt(0x19a))),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x321))+a0L[bt(0x3ab)][bt(0x208)](bt(0x1ab))+a0L['gray']('\x20to\x20return\x20to\x20the\x20main\x20menu.')),void console[bt(0x1dc)]();const V={};V['UA']='uk',V['PE']='es',V['CN']='zh',V['AU']='en',V['JP']='ja',V['KR']='ko',V['BR']='pt',V['SA']='ar',V['FR']='fr',V['DE']='de',V['IN']='hi',V['ID']='id',V['TH']='th',V['VN']='vi',V['TR']='tr',V['RU']='ru',V['EG']='ar',V['HT']='ht',V['PH']='en',V['MY']='en',V['SG']='en',V['ZA']='en',V['KE']='sw',V['TZ']='sw';const W=V[H[bt(0x352)]()[bt(0x33d)](0x1c0*0xe+-0x214e+0x8ce,0x5c+-0xf0b+0xeb1*0x1)[bt(0x250)]()],X=a0V(),Y=X['ctfdUrl']||bt(0x389),Z=W||X[bt(0x118)]||'en';W&&W!==(X[bt(0x118)]||'en')&&a0W({'language':W}),console[bt(0x1dc)](),N(-0x15f*-0x17+-0x247f+0xfe*0x5,bt(0x22d));try{const a8={};a8[bt(0x1ba)]='application/json';const a9=await fetch(Y+bt(0x3a0),{'method':'POST','headers':a8,'body':JSON[bt(0x1f7)]({'token':H[bt(0x352)](),'deviceHash':a0ah(),'lang':Z}),'signal':AbortSignal[bt(0x1bb)](-0x378f+0x30ca+0x2dd5*0x1)});if(!a9['ok']){N(0x871*-0x2+0x2413+0x1331*-0x1,''),console['log']();const ah=await a9[bt(0x3a1)]()['catch'](()=>({'message':'Invalid\x20token'}));return a0a8(ah[bt(0x2c3)]||bt(0x335)),void a0aa('Check\x20your\x20token\x20or\x20contact\x20your\x20proctor.');}N(0x1*-0x17f5+-0xfec+0x27ff,bt(0x385));const aa=await a9['json'](),{session:ab,questions:ac,languages:ad}=aa['data'];N(-0x33+-0x75*-0x21+-0x2*0x753,bt(0x13e)),await q(-0x16d*0x6+-0x4d8+0xe2e),N(-0x2140+0x1c40+0x564,bt(0x277)),console[bt(0x1dc)](),console[bt(0x1dc)](),a0ac(ab['examName']),console[bt(0x1dc)](),a0ad('Questions',ab[bt(0x211)]+bt(0x20f)),a0ad('Duration',ab[bt(0x194)]+bt(0x2a2)),a0ad(bt(0x3b4),(ab[bt(0x1ca)]||0x5*-0x691+-0x651+0x2*0x13de)+'\x20points'),a0ad(bt(0x275),(ab[bt(0x3a3)]||0x1*0x2702+0x239+-0x28f0)+bt(0x19f)),console[bt(0x1dc)](),console[bt(0x1dc)](a0L[bt(0x1d5)](bt(0x3b9))),console[bt(0x1dc)](a0L[bt(0x2ad)]('\x20\x20\x20\x20Help:\x20\x20\x20\x2010\x20uses\x20+\x205\x20hidden\x20bonus\x20(MCQ\x20only)')),console[bt(0x1dc)](a0L[bt(0x2ad)]('\x20\x20\x20\x20Hints:\x20\x20\x20A(5)\x20B(3)\x20C(1)\x20โ€”\x20practical\x20only')),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x1ec))),ad&&ad[bt(0x348)]>-0xca9+0x2*-0xd75+0x2794&&console['log'](a0L['gray'](bt(0x229)+ad[bt(0x3cb)](',\x20')+bt(0x395))),console[bt(0x1dc)](),console[bt(0x1dc)](a0L[bt(0x217)](bt(0x143))),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x32b))),console[bt(0x1dc)](a0L['gray']('\x20\x20\x20\x20โ€ข\x20Server\x20auto-finalises\x20your\x20answers\x20if\x20you\x20do\x20not\x20submit\x20before\x20the\x20deadline')),console[bt(0x1dc)](a0L[bt(0x2ad)](bt(0x32d))),console[bt(0x1dc)](),await new Promise(ai=>{const bu=bt;process[bu(0x1b3)][bu(0x137)](a0L[bu(0x3ab)][bu(0x217)](bu(0x1df)));const aj=!!process[bu(0x31a)][bu(0x2cb)]&&process[bu(0x31a)][bu(0x31b)];process[bu(0x31a)][bu(0x2cb)]&&process['stdin'][bu(0x222)]&&process[bu(0x31a)][bu(0x222)](!(0x1*-0x2290+-0xba*0xb+-0x883*-0x5));const ak=al=>{const bv=bu,am=al[bv(0x33a)]();(am[bv(0x22b)]('\x0a')||am[bv(0x22b)]('\x0d'))&&(process[bv(0x31a)][bv(0x294)](bv(0x1da),ak),process[bv(0x31a)][bv(0x2cb)]&&process['stdin'][bv(0x222)]&&process[bv(0x31a)]['setRawMode'](aj),process['stdout']['write']('\x0a'),ai());};process[bu(0x31a)]['on']('data',ak),process['stdin'][bu(0x3a5)]();});const ae=new Date()[bt(0x242)]();ab[bt(0x373)]=ae,ab[bt(0x21e)]=H[bt(0x352)]();try{const ai=Date[bt(0x2dd)](),aj=await fetch(Y+'/api/icoa/server-time',{'method':bt(0x126),'signal':AbortSignal['timeout'](0x16b*-0x1+-0x226b+0x2f8e*0x1)});if(aj['ok']){const ak=await aj[bt(0x3a1)](),al=ak?.['data']?.[bt(0x296)];if('number'==typeof al&&al>0x1a2d*-0x1+-0x6fc*-0x4+0x29*-0xb){const am=(ai+Date[bt(0x2dd)]())/(0x2652+-0x1*0x3a9+0x3*-0xb8d);ab[bt(0x30e)]=Math['round'](al-am),ab[bt(0x391)]=al+(-0x16ef*0x1+0x1*0xd7+-0x1*-0x1654)*ab[bt(0x194)]*(-0x10fa+-0x20eb+0x11ef*0x3);}}}catch{}const af={};af['ai4ctf']=0x0,af[bt(0x1cf)]=0x0;const ag={};ag[bt(0x154)]=ab,ag[bt(0x386)]=ac,ag[bt(0x2e0)]={},ag[bt(0x181)]=[],ag[bt(0x192)]=af,(a0Y(ag),console[bt(0x1dc)](),a0a7(bt(0x351)),a0ad(bt(0x39b),ab['durationMinutes']+bt(0x365)),ac[bt(0x348)]>-0x3b9*-0x5+0xa74+-0x7*0x427&&R(ac[-0x25*-0xa4+0x9ed+-0x21a1]));}catch(an){console['log'](),bt(0x187)===an[bt(0x1a2)]?a0a8(bt(0x3ca)):a0a8(an[bt(0x2c3)]||bt(0x2f8));}}),B[ba(0x127)](ba(0x375),{'hidden':!(-0x9*-0x36e+0xf9*0x1d+-0x3b13*0x1)})['description']('Abandon\x20current\x20exam\x20session\x20and\x20wipe\x20local\x20state\x20(recovery\x20only)')['action'](async()=>{const bw=ba;a0a6(bw(0x243));const {getRealExamState:F,clearExamState:G}=await import(bw(0x241)),H=F();if(!H)return console[bw(0x1dc)](),console[bw(0x1dc)](a0L[bw(0x2ad)]('\x20\x20No\x20active\x20exam\x20session\x20to\x20reset.')),void console[bw(0x1dc)]();const J=H[bw(0x154)][bw(0x21e)];if(console[bw(0x1dc)](),console[bw(0x1dc)](a0L['yellow'](bw(0x23b))),console[bw(0x1dc)](a0L[bw(0x2ad)](bw(0x1a6))+a0L['white'](H[bw(0x154)][bw(0x28f)])),J&&console[bw(0x1dc)](a0L[bw(0x2ad)](bw(0x2db))+a0L[bw(0x1d5)](J)),console[bw(0x1dc)](a0L[bw(0x2ad)](bw(0x282))),console[bw(0x1dc)](a0L[bw(0x2ad)]('\x20\x20\x20\x20\x20still\x20active\x20and\x20not\x20submitted,\x20re-entering\x20the\x20same\x20token\x20resumes\x20it.')),console[bw(0x1dc)](),console[bw(0x1dc)](a0L[bw(0x2ad)]('\x20\x20Type\x20')+a0L[bw(0x3ab)]['cyan']('reset')+a0L[bw(0x2ad)](bw(0x303))),bw(0x375)!==(await new Promise(K=>{const bx=bw;process[bx(0x1b3)][bx(0x137)](bx(0x2bb));const L=Q=>{const by=bx,U=Q[by(0x33a)]()['trim']();(U['includes']('\x0a')||U[by(0x22b)]('\x0d')||U[by(0x348)]>0x22be+0x1*-0x10f+-0x1*0x21af)&&(process['stdin'][by(0x294)](by(0x1da),L),K(U['replace'](/[\r\n]/g,'')));};process[bx(0x31a)]['on']('data',L),process['stdin']['resume']();}))[bw(0x253)]())return console[bw(0x1dc)](a0L[bw(0x2ad)](bw(0x1a1))),void console[bw(0x1dc)]();G(),console[bw(0x1dc)](),console[bw(0x1dc)](a0L[bw(0x21c)](bw(0x3b5))),console['log'](a0L[bw(0x2ad)](bw(0x2a4))+a0L[bw(0x3ab)]['cyan'](bw(0x27a))),console['log']();}),B['command'](ba(0x139))['description'](ba(0x397))['action'](async()=>{const bz=ba;a0a6(bz(0x3a6)),P(bz(0x2d0));const {pickDemoQuestions:F,getLocalizedDemoSession:G,DEMO_PICK_SIZE:H,DEMO_POOL_SIZE:J}=await import('../lib/demo-exam.js');a0V()[bz(0x293)]||(await(async function(){const bA=bz;let Z=!(-0x2687+0xf1*-0x26+0x4a4e);const a0=process[bA(0x31a)],a1=()=>{Z=!(0x4*0x5a6+0x1f9f+-0x3637);},a2=a0[bA(0x2cb)]&&bA(0x341)==typeof a0[bA(0x222)];a2&&(a0[bA(0x222)](!(-0x9fc+0x53a*-0x7+0x2e92)),a0[bA(0x3a5)](),a0[bA(0x188)](bA(0x1da),a1));const a3=async a4=>{let a5=0x5ab+-0x1*0x165a+0x10af;for(;a5<a4&&!Z;)await q(-0x10ef+0x36b*-0x3+-0x6e*-0x40),a5+=-0x637+0x1*-0x67d+0xd04;};try{if(console[bA(0x3bb)](),console[bA(0x1dc)](),console['log'](a0L[bA(0x2ad)]('\x20\x20\x20')+a0L[bA(0x14a)]('(press\x20any\x20key\x20to\x20skip)')),console[bA(0x1dc)](),console[bA(0x1dc)](a0L[bA(0x3ab)][bA(0x1d5)]('\x20\x20\x20โ–ˆโ–ˆโ•—\x20โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—\x20โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—\x20\x20โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—')),console['log'](a0L[bA(0x3ab)][bA(0x1d5)](bA(0x2fc))),console[bA(0x1dc)](a0L['bold'][bA(0x1d5)](bA(0x2ec))),console['log'](a0L[bA(0x3ab)]['white'](bA(0x34f))),console[bA(0x1dc)](a0L[bA(0x3ab)][bA(0x1d5)](bA(0x247))),console[bA(0x1dc)](a0L[bA(0x3ab)][bA(0x1d5)](bA(0x2c2))),console['log'](),console[bA(0x1dc)](a0L['bold'][bA(0x217)]('\x20\x20\x20International\x20Cyber\x20Olympiad\x20in\x20AI')),console[bA(0x1dc)](a0L[bA(0x2ad)]('\x20\x20\x20Sydney,\x20Australia\x20\x20ยท\x20\x20Jun\x2027\x20โ€“\x20Jul\x202,\x202026')),await a3(-0x1f*0x3f+-0x33d*0x3+0x1b1c),Z)return;if(console[bA(0x1dc)](),console[bA(0x1dc)](a0L[bA(0x1d5)](bA(0x35e))),console['log'](),await a3(0x11*-0xb5+-0x124f+0xc*0x2b9),Z)return;if(console[bA(0x1dc)](a0L[bA(0x208)]('\x20\x20\x20\x20\x20โ–ฃ\x20')+a0L['white'](bA(0x1ae))+a0L[bA(0x2ad)](bA(0x3bc))),await a3(-0x26e8+0x1cf9+0xcab),Z)return;if(console[bA(0x1dc)](a0L[bA(0x21c)](bA(0x1a3))+a0L[bA(0x1d5)](bA(0x170))+a0L[bA(0x2ad)]('\x20\x20\x20\x20(ai4ctf)')),await a3(-0x170b+-0x1003*-0x1+0x19*0x64),Z)return;if(console[bA(0x1dc)](a0L[bA(0x281)](bA(0x1a3))+a0L[bA(0x1d5)](bA(0x1f5))+a0L[bA(0x2ad)]('\x20\x20\x20\x20\x20\x20\x20\x20\x20(ctf4ai)')),await a3(0x2bd*-0x1+0x149*-0x2+0xfb*0x9),Z)return;if(console['log'](),console[bA(0x1dc)](a0L[bA(0x3ab)]['white'](bA(0x383))+a0L[bA(0x3ab)][bA(0x217)]('IS')+a0L[bA(0x3ab)][bA(0x1d5)](bA(0x357))),console['log'](),await a3(-0x9a3*-0x3+0x399*0x4+-0x47*0x87),Z)return;if(console['log'](a0L[bA(0x2ad)](bA(0x3cc))),await a3(-0x9d8+-0x71*0x3+0x1*0xcbb),Z)return;if(console['log'](a0L[bA(0x2ad)](bA(0x186))),await a3(0x1*-0x25a+-0x269c+0x2a86),Z)return;if(console[bA(0x1dc)](a0L[bA(0x2ad)](bA(0x116))),await a3(-0x201c+-0x1*-0x1cea+0x652),Z)return;console[bA(0x1dc)](),console[bA(0x1dc)](a0L[bA(0x3ab)][bA(0x208)]('\x20\x20\x20Press\x20any\x20key\x20to\x20start\x20demo...')),await a3(0x1322+0x332+-0x6b4);}finally{((()=>{const bB=bA;if(a2){a0['removeListener'](bB(0x1da),a1);try{a0[bB(0x222)](!(-0x2c4+-0x1bab+0x1e70));}catch{}a0['pause']();}})()),console[bA(0x3bb)]();}}()),a0W({'demoIntroSeen':!(0xe22+0x1e00+-0x2c22)})),console[bz(0x1dc)](),console[bz(0x1dc)](a0L[bz(0x1d5)]('\x20\x20Demo:\x20')+a0L[bz(0x2ad)](H+'\x20questions\x20drawn\x20from\x20a\x20pool\x20of\x20'+J+bz(0x2ea))),console['log'](a0L[bz(0x2ad)](bz(0x120))+a0L[bz(0x208)](bz(0x1f4))+a0L['gray'](bz(0x1c5))+a0L[bz(0x208)]('back')+a0L['gray'](bz(0x24f))),await new Promise(Z=>{const bC=bz;process[bC(0x1b3)][bC(0x137)](a0L['bold'][bC(0x217)](bC(0x2da)));const a0=!!process[bC(0x31a)][bC(0x2cb)]&&process[bC(0x31a)][bC(0x31b)];process['stdin'][bC(0x2cb)]&&process[bC(0x31a)][bC(0x222)]&&process['stdin'][bC(0x222)](!(0x1380+-0x36b+-0x1014));const a1=a2=>{const bD=bC,a3=a2['toString']();(a3['includes']('\x0a')||a3['includes']('\x0d'))&&(process['stdin'][bD(0x294)](bD(0x1da),a1),process['stdin']['isTTY']&&process[bD(0x31a)]['setRawMode']&&process[bD(0x31a)][bD(0x222)](a0),process[bD(0x1b3)]['write']('\x0a'),Z());};process[bC(0x31a)]['on'](bC(0x1da),a1),process[bC(0x31a)]['resume']();});const K=F(H),L=G(),{getDemoState:Q,clearExamState:U}=await import('../lib/exam-state.js');Q()&&U(bz(0x159)),console[bz(0x1dc)](),a0ac(bz(0x15d)),console[bz(0x1dc)](),console['log'](a0L[bz(0x1d5)](bz(0x167))),console[bz(0x1dc)](a0L['white']('\x20\x20'+H+bz(0x15e)+J+bz(0x324)));const V=a0a1();if(V[bz(0x1e5)]>0xb43+-0x19d*0x13+-0x92*-0x22){const Z=V[bz(0x15b)],a0=Z>=0xfc8+-0x116f*0x2+0x9b3*0x2?a0L[bz(0x21c)]:Z>=0x2466+-0x1e06+-0x624?a0L[bz(0x217)]:a0L[bz(0x1d5)];console[bz(0x1dc)](),console[bz(0x1dc)](a0L[bz(0x2ad)](bz(0x150))+a0(V[bz(0x355)]+'/'+H+'\x20('+Z+'%)')+a0L['gray']('\x20\x20ยท\x20\x20'+V[bz(0x1e5)]+'\x20attempts'));}console['log'](),console[bz(0x1dc)](a0L[bz(0x2ad)](bz(0x2b6))),console[bz(0x1dc)](a0L[bz(0x1d5)]('\x20\x20'+a0ag(bz(0x220)))),console[bz(0x1dc)](a0L[bz(0x217)](bz(0x32c))+a0L[bz(0x2ad)](bz(0x168)+a0ag(bz(0x3b1)))),console[bz(0x1dc)](a0L['yellow'](bz(0x32f))+a0L[bz(0x2ad)](bz(0x2a3)+a0ag(bz(0x202)))),console[bz(0x1dc)](a0L['yellow'](bz(0x13a))+a0L[bz(0x2ad)](bz(0x214))+a0L[bz(0x217)]('prev')+a0L[bz(0x2ad)](bz(0x2f9)+a0ag(bz(0x319)))),console[bz(0x1dc)](a0L['yellow'](bz(0x2eb))+a0L['gray'](bz(0x334)+a0ag('htpMoreHelp'))),console[bz(0x1dc)](a0L['yellow'](bz(0x1b6))+a0L[bz(0x2ad)](bz(0x2a3)+a0ag(bz(0x286)))),console['log'](a0L[bz(0x217)](bz(0x14b))+a0L[bz(0x2ad)](bz(0x2a3)+a0ag('htpLang'))),console['log'](a0L[bz(0x2ad)](bz(0x2de))),console['log'](a0L[bz(0x2ad)]('\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20ru\x20ยท\x20hi\x20ยท\x20de\x20ยท\x20id\x20ยท\x20th\x20ยท\x20vi\x20ยท\x20tr')),console[bz(0x1dc)](a0L['gray']('\x20\x20โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€')),console[bz(0x1dc)]();const {getConfig:W}=await import(bz(0x1c9)),X=W()['language']||'en';'en'===X?(console['log'](a0L[bz(0x2ad)](bz(0x197))),console['log'](a0L[bz(0x2ad)](bz(0x3ad))),console[bz(0x1dc)](a0L[bz(0x2ad)]('\x20\x20lang\x20fr\x20ยท\x20lang\x20ar\x20ยท\x20lang\x20pt\x20ยท\x2015\x20languages\x20supported'))):console[bz(0x1dc)](a0L[bz(0x21c)]('\x20\x20Language:\x20'+X)),console['log'](),console[bz(0x1dc)](a0L['white'](bz(0x175)));const Y={...L,'startedAt':new Date()[bz(0x242)]()};console[bz(0x1dc)](),N(-0x2230+0x1a33+0x7fd,bz(0x28a)),await q(-0x2289+-0x47*-0x4d+-0xdf6*-0x1),N(0x1630+-0x1bca*-0x1+0x38f*-0xe,bz(0x313)),await q(0x26f0+0xc02+-0x1915*0x2),N(0x1*0xb62+-0x2252+-0x6*-0x3e0,bz(0x1e1)),await q(-0x5*-0x4c1+0x160f+0x169f*-0x2),N(0x1ab1*-0x1+-0x27b+-0x2*-0xec8,'Ready!'),console[bz(0x1dc)](),console[bz(0x1dc)](),a0Y({'session':Y,'questions':K,'answers':{},'_helpMax':0x5}),a0ad(bz(0x219),String(H)),a0ad(bz(0x2d5),bz(0x323)),R(K[-0x2691+0x408+0xb83*0x3]);}),B[ba(0x127)](ba(0x339))['description'](ba(0x1a0))['action'](async()=>{const bE=ba;a0a6(bE(0x2f0)),P('post-report-retry');const {getLocalizedDemoSession:F}=await import(bE(0x11d)),G=a0a4();if(!G||-0x70*-0x51+0x187d*-0x1+-0xaf3*0x1===G[bE(0x348)])return console[bE(0x1dc)](),console['log'](a0L[bE(0x217)](bE(0x1a5))+a0L[bE(0x3ab)][bE(0x208)]('demo')+a0L[bE(0x217)](bE(0x223))),void console[bE(0x1dc)]();const H=a0X();if(H){if(bE(0x159)!==H['session'][bE(0x1c0)])return a0a9(bE(0x1bf)+H[bE(0x154)][bE(0x28f)]+bE(0x347)),void a0aa(bE(0x1c1));a0Z();}const J=U=>{const bF=bE;if(!U[bF(0x320)])return U;const V=U[bF(0x2c8)][U[bF(0x320)]],W=['A','B','C','D'][bF(0x178)](a1=>U[bF(0x2c8)][a1]);for(let a1=W[bF(0x348)]-(0x1813+0xfdf+-0x27f1);a1>0x3e6*-0x4+-0x8a2+0x183a;a1--){const a2=Math['floor'](Math['random']()*(a1+(-0x1*-0x389+-0xf*0x14+0x2*-0x12e)));[W[a1],W[a2]]=[W[a2],W[a1]];}const X={};X['A']=W[-0x2f1*-0x9+-0x8*-0x3c+0x1*-0x1c59],X['B']=W[-0x3*-0x69d+-0x2504+0x112e],X['C']=W[-0x22c8+0x18ec+0x9de],X['D']=W[0x142c+0x1*0x25f2+-0x3a1b];const Y=X,Z=['A','B','C','D'][bF(0x264)](a3=>Y[a3]===V),a0={...U};return a0[bF(0x2c8)]=Y,a0[bF(0x320)]=Z,a0;},K=G[bE(0x178)]((U,V)=>({...J(U),'number':V+(0x14fa+-0x1be*-0xb+-0x2823)})),L=F(),Q={...L,'examName':L[bE(0x28f)]+bE(0x174),'questionCount':K[bE(0x348)],'startedAt':new Date()[bE(0x242)]()};console[bE(0x1dc)](),a0ac(bE(0x191)),console[bE(0x1dc)](),console[bE(0x1dc)](a0L[bE(0x1d5)](bE(0x32a)+K[bE(0x348)]+bE(0x292)+(-0x69d*0x1+-0x2469*-0x1+0x1d*-0x107===K[bE(0x348)]?'':'s')+bE(0x1f8))),console[bE(0x1dc)](a0L[bE(0x2ad)](bE(0x3a4))),console[bE(0x1dc)](),a0Y({'session':Q,'questions':K,'answers':{}}),a0ad('Questions',String(K[bE(0x348)])),a0ad('Duration',bE(0x323)),R(K[0xe72*-0x2+-0x25f*-0x8+0x9ec]);}),B[ba(0x127)](ba(0x256))[ba(0x297)]('Install\x20Python\x20environment\x20for\x20practical\x20exam\x20questions')[ba(0x2e2)](async()=>{const bG=ba;if(a0a6(bG(0x19a)),-0x16a8+0x24cd+-0x33*0x47===a0a1()[bG(0x1e5)])return console[bG(0x1dc)](),a0a9(bG(0x2d8)),console[bG(0x1dc)](a0L['gray'](bG(0x227))+a0L[bG(0x3ab)]['cyan']('exam\x20demo')),void console['log']();const J=a0ai();if(J){const a2=J[bG(0x2ee)][bG(0x348)],a3=a2+J[bG(0x261)]['length'];if(a2>=0x16d2+-0x1*-0x737+-0x1e01*0x1||(a3>0xe17+0x1fef+-0x2e06?a2/a3:0x2295+-0xe*0x23+-0x20ab)>=-0x1815+-0x2137*-0x1+-0x1*0x922+0.7)return console[bG(0x1dc)](),console[bG(0x1dc)](a0L['green'](bG(0x266))+a0L[bG(0x21c)](bG(0x1d9))),console['log'](a0L[bG(0x2ad)](bG(0x251)+J['completedAt'][bG(0x2e7)]('T')[-0xd7f*0x1+0x7*-0x14b+0x1a*0xde])),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x18f)+J[bG(0x27c)])),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x24c)+a2+bG(0x2bf))),J[bG(0x261)][bG(0x348)]>-0x4*-0x52d+-0x3e1*0x5+-0x14f&&console[bG(0x1dc)](a0L[bG(0x217)]('\x20\x20\x20\x20Failed:\x20'+J[bG(0x261)]['join'](',\x20'))),console[bG(0x1dc)](),console['log'](a0L['white'](bG(0x2a1))+a0L[bG(0x3ab)][bG(0x208)]('exam\x20<token>')),console[bG(0x1dc)](a0L['gray'](bG(0x321))+a0L[bG(0x3ab)][bG(0x208)](bG(0x1ab))+a0L[bG(0x2ad)](bG(0x3aa))),void console['log']();console['log'](),console[bG(0x1dc)](a0L[bG(0x217)](bG(0x35c)+a2+'/'+a3+bG(0x203))),console[bG(0x1dc)]();}console[bG(0x1dc)](),a0ac('Exam\x20Environment\x20Setup'),console['log'](),console['log'](a0L[bG(0x1d5)]('\x20\x20Installing\x20Python\x20packages\x20for\x20practical\x20questions.')),console[bG(0x1dc)](a0L['gray'](bG(0x11b))),console['log']();const K=(a4,a5)=>{const bH=bG,a6=process[bH(0x26b)];console[bH(0x1dc)](),a0a8(bH(0x299)===a4?'Python\x203\x20not\x20found.':'Python\x20'+a5+bH(0x23a)),console[bH(0x1dc)](),console['log'](a0L[bH(0x3ab)]['white'](bH(0x28c))),console[bH(0x1dc)](),bH(0x3c9)===a6?(console['log'](a0L[bH(0x217)](bH(0x38a))),console['log'](a0L[bH(0x21c)](bH(0x155))),console['log'](),console[bH(0x1dc)](a0L[bH(0x2ad)]('\x20\x20Or\x20download\x20installer:')),console[bH(0x1dc)](a0L[bH(0x2ad)](bH(0x1ff)))):bH(0x2ce)===a6?(console['log'](a0L[bH(0x217)]('\x20\x20Ubuntu\x20/\x20Debian\x20(deadsnakes\x20PPA):')),console[bH(0x1dc)](a0L[bH(0x21c)](bH(0x358))),console[bH(0x1dc)](a0L[bH(0x21c)]('\x20\x20\x20\x20sudo\x20apt\x20update')),console[bH(0x1dc)](a0L[bH(0x21c)](bH(0x156))),console[bH(0x1dc)](a0L['gray'](bH(0x388))),console[bH(0x1dc)](),console[bH(0x1dc)](a0L[bH(0x217)]('\x20\x20Fedora\x20/\x20RHEL:')),console[bH(0x1dc)](a0L[bH(0x21c)](bH(0x376))),console[bH(0x1dc)](),console[bH(0x1dc)](a0L[bH(0x217)](bH(0x1f2))),console[bH(0x1dc)](a0L[bH(0x21c)]('\x20\x20\x20\x20sudo\x20pacman\x20-S\x20python'))):'win32'===a6?(console[bH(0x1dc)](a0L[bH(0x217)]('\x20\x20Windows\x20(winget,\x20recommended):')),console[bH(0x1dc)](a0L['green'](bH(0x304))),console[bH(0x1dc)](),console[bH(0x1dc)](a0L[bH(0x2ad)](bH(0x1d0))),console['log'](a0L[bH(0x2ad)]('\x20\x20\x20\x20https://www.python.org/downloads/windows/'))):console[bH(0x1dc)](a0L[bH(0x2ad)]('\x20\x20\x20\x20https://www.python.org/downloads/')),console[bH(0x1dc)](),console[bH(0x1dc)](a0L['white']('\x20\x20After\x20installing,\x20run:\x20')+a0L[bH(0x3ab)][bH(0x208)](bH(0x19a))),console[bH(0x1dc)]();},L=bG(0x307);let Q='';try{const a4={};a4[bG(0x239)]=bG(0x130),a4[bG(0x1bb)]=0x1388,Q=a0al(L+'\x20--version',a4)[bG(0x352)]()[bG(0x392)](bG(0x3ae),'');const a5=Q[bG(0x2e7)]('.')[bG(0x178)](Number);if(a5[0xa*-0x65+0x1c79+-0x45*0x5b]<0xbe8+0xf7*0xe+-0x7*0x3a1||0x778+0x223*0xd+0x29*-0xdc===a5[-0x1*0x201b+0xa9+0x1f72]&&a5[0x2*0xfb+-0x1*0x2d7+0xe2]<-0x221a+-0x1a4d*-0x1+0x1*0x7d7)return void K('too_old',Q);0xa*-0x1fa+-0x7*0x10f+0x91*0x30===a5[0xf3c+-0x15d7*-0x1+-0x2513*0x1]&&a5[0x58a*0x7+0x928+0x2fed*-0x1]<-0xcdc*-0x1+0x2462+-0x3132?console[bG(0x1dc)](a0L[bG(0x21c)](bG(0x1ce)+Q)+a0L[bG(0x2ad)](bG(0x25a))):console['log'](a0L['green'](bG(0x1ce)+Q));}catch{return void K(bG(0x299));}try{const a6={};a6[bG(0x38d)]='ignore',a6['timeout']=0x1388,(a0al(L+bG(0x1f6),a6),console[bG(0x1dc)](a0L['green'](bG(0x2e3))));}catch{console['log'](a0L[bG(0x217)](bG(0x2ae)));try{const a7={};a7[bG(0x38d)]=bG(0x390),a7[bG(0x1bb)]=0x7530,(a0al(L+'\x20-m\x20ensurepip\x20--upgrade',a7),console[bG(0x1dc)](a0L['green'](bG(0x1dd))));}catch{return void a0a8(bG(0x2a5));}}const U={};U[bG(0x2ff)]=bG(0x1ac),U['pycryptodome']=bG(0x2d4),U['z3-solver']='z3',U['pillow']=bG(0x3ba),U[bG(0x1f9)]='pwn',U[bG(0x1d6)]=bG(0x381);const V=[bG(0x1f9),'cryptography','requests','beautifulsoup4',bG(0x131),'pillow',bG(0x141),bG(0x26c),bG(0x2dc),'pycryptodome',bG(0x1d6),bG(0x160),bG(0x2f4)],W=U;console['log'](),console[bG(0x1dc)](a0L[bG(0x1d5)](bG(0x27f)+V['length']+'\x20packages...')),console[bG(0x1dc)]();let X='';try{const a8={};a8[bG(0x239)]='utf-8',a8[bG(0x1bb)]=0x2710,a0al(L+bG(0x36a),a8);}catch(a9){const aa=String(a9?.[bG(0x1b3)]||a9?.[bG(0x287)]||a9?.[bG(0x2c3)]||'');/externally-managed-environment|break-system-packages/i[bG(0x26f)](aa)&&(X=bG(0x35f),console[bG(0x1dc)](a0L[bG(0x217)]('\x20\x20โ„น\x20PEP\x20668\x20detected\x20โ€”\x20using\x20--user\x20install\x20(no\x20sudo\x20needed)')),console[bG(0x1dc)]());}const Y=Q[bG(0x2e7)]('.')[bG(0x178)](Number);0x2d3+-0xa22+0x752===Y[0x1d*-0x10d+-0xef9*-0x1+0x2*0x7c0]&&Y[0x1*-0x3e6+0x1ef7+-0x1b10]>=0x2534+-0x35*0x1+-0x24f2&&(console['log'](a0L['yellow'](bG(0x230)+Q+bG(0x305))),console[bG(0x1dc)](a0L[bG(0x2ad)]('\x20\x20\x20\x20Some\x20packages\x20(pwntools,\x20scapy)\x20may\x20not\x20have\x20wheels\x20yet\x20for\x203.13.')),console[bG(0x1dc)](a0L[bG(0x2ad)]('\x20\x20\x20\x20Python\x203.12\x20is\x20recommended.\x20Install:')),bG(0x2ce)===process['platform']&&(console[bG(0x1dc)](a0L[bG(0x21c)]('\x20\x20\x20\x20\x20\x20sudo\x20apt\x20install\x20python3.12\x20python3.12-venv')),console[bG(0x1dc)](a0L[bG(0x21c)]('\x20\x20\x20\x20\x20\x20sudo\x20update-alternatives\x20--install\x20/usr/bin/python3\x20python3\x20/usr/bin/python3.12\x201'))),console[bG(0x1dc)]());const Z=[],a0=[];let a1='';for(const ab of V)try{a0al(L+bG(0x2c5)+ab+'\x22\x20'+X+'\x20--quiet\x202>&1',{'encoding':bG(0x130),'timeout':0x2bf20,'stdio':'pipe'}),a0al(L+'\x20-c\x20\x22import\x20'+(W[ab]||ab)+'\x22',{'stdio':bG(0x390),'timeout':0x2710});let ac='';try{const ad=a0al(L+bG(0x134)+ab+'\x22\x202>/dev/null',{'encoding':bG(0x130),'timeout':0x1388})['match'](/^Version:\s*(.+)$/m);ad&&(ac=ad[0x1ac4+0x22fa+-0x3dbd]['trim']());}catch{}console[bG(0x1dc)](a0L[bG(0x21c)](bG(0x266)+ab)+(ac?a0L[bG(0x2ad)]('\x20('+ac+')'):'')),Z[bG(0x15c)](ac?ab+'=='+ac:ab);}catch(ae){const af=String(ae?.[bG(0x1b3)]||ae?.[bG(0x287)]||ae?.[bG(0x2c3)]||'')[bG(0x1d8)](-(0xa15*-0x1+0x17e9*-0x1+0x238e*0x1)),ag=af[bG(0x2e7)]('\x0a')[bG(0x283)](ah=>ah[bG(0x352)]())[bG(0x1d8)](-(-0xde5+0x44d*-0x3+0x49*0x5e))['join']('\x20|\x20')[bG(0x1d8)](0x9*-0x1e8+0x2a7+0xe81,-0x1b5c+-0x76c+0x2340);console[bG(0x1dc)](a0L['red'](bG(0x38f)+ab)+a0L[bG(0x2ad)]('\x20\x20'+ag)),a0[bG(0x15c)]({'pkg':ab,'error':ag}),a1||(a1=af);}0xb6a*0x2+0x2547+0x17*-0x29d===Z[bG(0x348)]&&a1&&(console[bG(0x1dc)](),console[bG(0x1dc)](a0L['yellow'](bG(0x360))),console[bG(0x1dc)](a0L['gray']('\x20\x20'+a1[bG(0x2e7)]('\x0a')[bG(0x1d8)](-(-0x3*0x9fa+0x1*-0x1253+0x3047))[bG(0x3cb)](bG(0x180))))),a0aj({'completedAt':new Date()[bG(0x242)](),'pythonVersion':Q,'installedPackages':Z,'failedPackages':a0[bG(0x178)](ah=>ah['pkg'])}),console[bG(0x1dc)](),0x2*-0x832+-0x77*0x4f+0x351d===a0['length']?a0a7(bG(0x298)+V[bG(0x348)]+bG(0x147)):-0xa*0x81+0x2077+-0x7*0x3eb===Z['length']?(a0a8('Setup\x20failed\x20โ€”\x200\x20of\x20'+V[bG(0x348)]+bG(0x147)),console[bG(0x1dc)](),console[bG(0x1dc)](a0L[bG(0x217)](bG(0x132))),-0xfa8+0x2*0xa4a+-0x4e9===Y[-0x2e7*-0xb+0x56*0x1b+-0x28ff]&&Y[0x18fb+0x5cc*0x5+-0x35f6]>=-0x1436+-0x1627+0x2a6a?(console['log'](a0L['gray'](bG(0x20e))),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x332)))):(console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x38c))),console['log'](a0L[bG(0x2ad)](bG(0x1f3))+a0L[bG(0x208)](L+bG(0x24d))+a0L[bG(0x2ad)](bG(0x265))),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x144)))),console[bG(0x1dc)](),console['log'](a0L[bG(0x1d5)](bG(0x12e))+a0L[bG(0x3ab)][bG(0x208)](bG(0x19a)))):a0a9(Z[bG(0x348)]+'/'+V['length']+'\x20packages\x20installed.\x20'+a0['length']+bG(0x387)+a0[bG(0x178)](ah=>ah[bG(0x3c7)])[bG(0x3cb)](',\x20')),console[bG(0x1dc)](),console[bG(0x1dc)](a0L[bG(0x208)](bG(0x22a))),console[bG(0x1dc)](a0L[bG(0x3ab)][bG(0x1d5)](bG(0x11a))),console[bG(0x1dc)](a0L[bG(0x208)](bG(0x22a))),console['log'](),console['log'](a0L['bold'][bG(0x217)](bG(0x161))+a0L[bG(0x2ad)]('\x20(quick\x20tasks)')),console[bG(0x1dc)](a0L['white']('\x20\x20Just\x20add\x20!\x20before\x20the\x20command:')),console[bG(0x1dc)](a0L[bG(0x21c)]('\x20\x20\x20\x20!python3\x20-c\x20\x22import\x20base64;\x20print(base64.b64decode(\x27SGVsbG8=\x27))\x22')),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x3c8))),console[bG(0x1dc)](),console[bG(0x1dc)](a0L[bG(0x3ab)][bG(0x217)](bG(0x2d6))+a0L[bG(0x2ad)](bG(0x233))),console[bG(0x1dc)](a0L[bG(0x1d5)]('\x20\x20Start\x20a\x20Python\x20session,\x20type\x20commands\x20one\x20by\x20one:')),console[bG(0x1dc)](a0L['green']('\x20\x20\x20\x20!python3')),console[bG(0x1dc)](a0L[bG(0x2ad)]('\x20\x20\x20\x20>>>\x20from\x20Crypto.Cipher\x20import\x20AES')),console['log'](a0L[bG(0x2ad)](bG(0x33e))),console[bG(0x1dc)](a0L[bG(0x2ad)]('\x20\x20\x20\x20>>>\x20cipher\x20=\x20AES.new(key,\x20AES.MODE_CBC,\x20iv)')),console[bG(0x1dc)](a0L[bG(0x2ad)]('\x20\x20\x20\x20>>>\x20print(cipher.decrypt(data))')),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x18b))),console[bG(0x1dc)](),console[bG(0x1dc)](a0L['bold'][bG(0x217)](bG(0x39c))+a0L[bG(0x2ad)]('\x20(complex\x20solutions)')),console['log'](a0L['white'](bG(0x237))),console[bG(0x1dc)](a0L[bG(0x21c)](bG(0x1be))),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x2b3))),console['log'](a0L[bG(0x2ad)](bG(0x1ad))),console['log'](a0L[bG(0x2ad)](bG(0x3bd))),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x146))),console[bG(0x1dc)](a0L['green'](bG(0x37b))),console[bG(0x1dc)](a0L[bG(0x21c)]('\x20\x20\x20\x20!python3\x20solve.py')),console['log'](),console['log'](a0L['bold']['yellow'](bG(0x31c))),console[bG(0x1dc)](a0L[bG(0x1d5)]('\x20\x20During\x20the\x20exam,\x20type\x20')+a0L['bold'][bG(0x208)](bG(0x196))+a0L[bG(0x1d5)](bG(0x2d1))),console[bG(0x1dc)](a0L['gray'](bG(0x336))),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x1db))),console[bG(0x1dc)](a0L['gray'](bG(0x1e3))),console['log'](a0L[bG(0x208)](bG(0x22a))),console[bG(0x1dc)](),console[bG(0x1dc)](a0L[bG(0x1d5)]('\x20\x20Next\x20step:\x20')+a0L['bold'][bG(0x208)](bG(0x27a))),console[bG(0x1dc)](a0L[bG(0x2ad)](bG(0x321))+a0L['bold']['cyan']('back')+a0L[bG(0x2ad)](bG(0x3aa))),console[bG(0x1dc)]();}),B[ba(0x2e2)](()=>{const bI=ba;a0a6('exam');const F=a0X();if(F){a0ac('In\x20Progress:\x20'+F[bI(0x154)]['examName']),M();const J=Object[bI(0x166)](F[bI(0x2e0)])[bI(0x348)];return a0ad(bI(0x27e),J+'/'+F[bI(0x154)][bI(0x211)]+bI(0x2b8)),console[bI(0x1dc)](),void console[bI(0x1dc)](a0L[bI(0x2ad)](bI(0x317)));}const G=a0a1(),H=a0ai();console[bI(0x1dc)](),a0ac(bI(0x342)),console[bI(0x1dc)](),0x6c0+0x9e0+-0x10a0===G[bI(0x1e5)]?(console[bI(0x1dc)](a0L[bI(0x217)](bI(0x2b7))+a0L[bI(0x217)](bI(0x39e))),console[bI(0x1dc)](a0L[bI(0x2ad)](bI(0x209))+a0L[bI(0x3ab)][bI(0x208)](bI(0x3a6)))):(console['log'](a0L['green']('\x20\x20โ—\x20')+a0L['green'](bI(0x2cf)+G[bI(0x1e5)]+'\x20attempt'+(-0x15ef+0x1332+0x2be!==G[bI(0x1e5)]?'s':'')+')')),H?(console[bI(0x1dc)](a0L[bI(0x21c)](bI(0x33c))+a0L['green'](bI(0x238))),console[bI(0x1dc)](a0L['yellow']('\x20\x20โ—‹\x20')+a0L['yellow']('Enter\x20exam\x20token\x20to\x20begin')),console[bI(0x1dc)](a0L[bI(0x2ad)](bI(0x209))+a0L[bI(0x3ab)]['cyan'](bI(0x27a)))):(console[bI(0x1dc)](a0L[bI(0x217)](bI(0x2b7))+a0L[bI(0x217)](bI(0x31d))),console[bI(0x1dc)](a0L[bI(0x2ad)]('\x20\x20\x20\x20โ†’\x20')+a0L['bold']['cyan'](bI(0x19a))))),console['log']();});}