icoa-cli 2.18.0 → 2.19.0

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.
@@ -5,6 +5,7 @@ import { getConfig } from '../lib/config.js';
5
5
  import { getExamState, saveExamState, clearExamState, getExamDeadline } from '../lib/exam-state.js';
6
6
  import { logCommand } from '../lib/logger.js';
7
7
  import { printSuccess, printError, printWarning, printInfo, printTable, printHeader, printKeyValue, createSpinner, formatCountdown, } from '../lib/ui.js';
8
+ import { t } from '../lib/i18n.js';
8
9
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
9
10
  function drawProgress(percent, label) {
10
11
  const width = 30;
@@ -55,27 +56,29 @@ function printTimeRemaining() {
55
56
  }
56
57
  function printHowToPlay() {
57
58
  console.log(chalk.gray(' ─────────────────────────────────────────'));
58
- console.log(chalk.white(' How to play:'));
59
- console.log(chalk.yellow(' A/B/C/D') + chalk.gray(' answer the question'));
60
- console.log(chalk.yellow(' help') + chalk.gray(' remove a wrong option'));
61
- console.log(chalk.yellow(' next') + chalk.gray(' / ') + chalk.yellow('prev') + chalk.gray(' move between questions'));
62
- console.log(chalk.yellow(' more help') + chalk.gray(' +3 bonus helps'));
63
- console.log(chalk.yellow(' back') + chalk.gray(' pause and return to menu'));
64
- console.log(chalk.yellow(' lang es') + chalk.gray(' switch language (15 available)'));
59
+ console.log(chalk.white(` ${t('howToPlay')}`));
60
+ console.log(chalk.yellow(' A/B/C/D') + chalk.gray(` ${t('htpAnswer')}`));
61
+ console.log(chalk.yellow(' help') + chalk.gray(` ${t('htpHelp')}`));
62
+ console.log(chalk.yellow(' next') + chalk.gray(' / ') + chalk.yellow('prev') + chalk.gray(` ${t('htpNav')}`));
63
+ console.log(chalk.yellow(' more help') + chalk.gray(` ${t('htpMoreHelp')}`));
64
+ console.log(chalk.yellow(' back') + chalk.gray(` ${t('htpBack')}`));
65
+ console.log(chalk.yellow(' lang') + chalk.gray(` ${t('htpLang')}`));
65
66
  console.log(chalk.gray(' es · zh · ja · ko · ar · fr · pt'));
66
67
  console.log(chalk.gray(' ru · hi · de · id · th · vi · tr'));
67
68
  console.log(chalk.gray(' ─────────────────────────────────────────'));
68
69
  }
69
70
  // Australian easter eggs every 3 questions (for 15-question demo)
70
- const EASTER_EGGS = {
71
- 3: { emoji: '🏛️', text: 'Sydney Opera House — Great start!' },
72
- 5: { emoji: '🐨', text: 'A koala says g\'day — 1/3 done!' },
73
- 7: { emoji: '🌉', text: 'Sydney Harbour Bridge — Keep going!' },
74
- 9: { emoji: '🦘', text: 'A kangaroo hops by — past halfway!' },
75
- 11: { emoji: '🏖️', text: 'Bondi Beach — almost there!' },
76
- 13: { emoji: '🦈', text: 'Great Barrier Reef — 2 more to go!' },
77
- 15: { emoji: '🎉', text: 'G\'day mate! All done! 🇦🇺' },
78
- };
71
+ function getEasterEggs() {
72
+ return {
73
+ 3: { emoji: '🏛️', text: t('egg3') },
74
+ 5: { emoji: '🐨', text: t('egg5') },
75
+ 7: { emoji: '🌉', text: t('egg7') },
76
+ 9: { emoji: '🦘', text: t('egg9') },
77
+ 11: { emoji: '🏖️', text: t('egg11') },
78
+ 13: { emoji: '🦈', text: t('egg13') },
79
+ 15: { emoji: '🎉', text: t('egg15') },
80
+ };
81
+ }
79
82
  function printQuestionProgress(current, total, answered) {
80
83
  const width = 30;
81
84
  const filled = Math.round((current / total) * width);
@@ -102,7 +105,7 @@ function printQuestion(q, answer) {
102
105
  // Progress bar
103
106
  printQuestionProgress(q.number, total, answered);
104
107
  // Easter egg
105
- const egg = EASTER_EGGS[q.number];
108
+ const egg = getEasterEggs()[q.number];
106
109
  if (egg && q.number <= total) {
107
110
  console.log(chalk.yellow(` ${egg.emoji} ${egg.text}`));
108
111
  }
@@ -115,7 +118,7 @@ function printQuestion(q, answer) {
115
118
  const isEliminated = eliminated.includes(key);
116
119
  const selected = answer === key;
117
120
  if (isEliminated) {
118
- console.log(chalk.gray.strikethrough(` ${key}. ${q.options[key]}`) + chalk.red(' (wrong)'));
121
+ console.log(chalk.gray.strikethrough(` ${key}. ${q.options[key]}`) + chalk.red(` (${t('wrong')})`));
119
122
  }
120
123
  else if (selected) {
121
124
  console.log(chalk.green.bold(` ▸ ${key}. ${q.options[key]}`));
@@ -129,20 +132,20 @@ function printQuestion(q, answer) {
129
132
  const isDemo = state?.session.examId === 'demo-free';
130
133
  const q2Helps = help.perQ[2] || 0;
131
134
  if (isDemo && q.number === 2 && q2Helps === 0) {
132
- console.log(chalk.yellow.bold(' 👉 Try typing "help" to see what happens!'));
135
+ console.log(chalk.yellow.bold(` ${t('qTutorial')}`));
133
136
  console.log();
134
137
  }
135
138
  // Full menu on every question
136
139
  const remaining = help.max - help.used;
137
140
  const helpLabel = remaining > 0
138
- ? chalk.gray('remove a wrong option ') + chalk.yellow(`(${remaining}/${help.max})`)
139
- : (help.max < 8 ? chalk.gray('used up — type ') + chalk.yellow('more help') + chalk.gray(' for +3') : chalk.gray(`used up (${help.used}/${help.max})`));
141
+ ? chalk.gray(`${t('helpRemove')} `) + chalk.yellow(`(${remaining}/${help.max})`)
142
+ : (help.max < 8 ? chalk.gray(`${t('helpUsedUp')}`) : chalk.gray(`${t('helpAllUsed')} (${help.used}/${help.max})`));
140
143
  console.log(chalk.gray(' ─────────────────────────────────────────'));
141
- console.log(chalk.yellow(' A/B/C/D') + chalk.gray(' answer this question'));
144
+ console.log(chalk.yellow(' A/B/C/D') + chalk.gray(` ${t('answerThis')}`));
142
145
  console.log(chalk.yellow(' help') + ' ' + helpLabel);
143
- console.log(chalk.yellow(' next') + chalk.gray(' / ') + chalk.yellow('prev') + chalk.gray(' move between questions'));
144
- console.log(chalk.yellow(' back') + chalk.gray(' pause and return to menu'));
145
- console.log(chalk.yellow(' lang') + chalk.gray(' view all languages / ') + chalk.yellow('lang es') + chalk.gray(' to switch'));
146
+ console.log(chalk.yellow(' next') + chalk.gray(' / ') + chalk.yellow('prev') + chalk.gray(` ${t('htpNav')}`));
147
+ console.log(chalk.yellow(' back') + chalk.gray(` ${t('htpBack')}`));
148
+ console.log(chalk.yellow(' lang') + chalk.gray(` ${t('htpLang')}`));
146
149
  console.log(chalk.gray(' ─────────────────────────────────────────'));
147
150
  }
148
151
  export function registerExamCommand(program) {
@@ -567,123 +570,8 @@ export function registerExamCommand(program) {
567
570
  console.log(chalk.green.bold(' 🎉 All questions answered!'));
568
571
  console.log();
569
572
  if (state.session.examId === 'demo-free') {
570
- console.log(chalk.white(' Auto-submitting your demo...'));
573
+ console.log(chalk.white(' Type ') + chalk.bold.cyan('exam submit') + chalk.white(' to see your results!'));
571
574
  console.log();
572
- // Auto-submit demo
573
- try {
574
- const { DEMO_ANSWERS } = await import('../lib/demo-exam.js');
575
- drawProgress(0, 'Grading...');
576
- await sleep(300);
577
- let score = 0;
578
- for (const [qn, ans] of Object.entries(state.answers)) {
579
- if (DEMO_ANSWERS[Number(qn)] === ans)
580
- score++;
581
- }
582
- drawProgress(100, 'Complete!');
583
- console.log();
584
- // Anonymous stats (fire-and-forget)
585
- const helpInfo = getHelpState(state);
586
- const statsLang = getConfig().language || 'en';
587
- fetch('https://practice.icoa2026.au:9090/api/icoa/demo-stats', {
588
- method: 'POST',
589
- headers: { 'Content-Type': 'application/json' },
590
- body: JSON.stringify({
591
- score,
592
- total: state.questions.length,
593
- lang: statsLang,
594
- helpUsed: helpInfo.used,
595
- helpMax: helpInfo.max,
596
- timestamp: new Date().toISOString(),
597
- }),
598
- signal: AbortSignal.timeout(5000),
599
- }).catch(() => { });
600
- clearExamState();
601
- const pct = Math.round(score / state.questions.length * 100);
602
- console.log();
603
- // ─── Cinematic results reveal ───
604
- // Part 1: Score banner
605
- console.log(chalk.cyan(' ═══════════════════════════════════════'));
606
- console.log();
607
- console.log(chalk.bold.white(' ██╗ ██████╗ ██████╗ █████╗'));
608
- console.log(chalk.bold.white(' ██║██╔════╝██╔═══██╗██╔══██╗'));
609
- console.log(chalk.bold.white(' ██║██║ ██║ ██║███████║'));
610
- console.log(chalk.bold.white(' ██║██║ ██║ ██║██╔══██║'));
611
- console.log(chalk.bold.white(' ██║╚██████╗╚██████╔╝██║ ██║'));
612
- console.log(chalk.bold.white(' ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝'));
613
- console.log();
614
- console.log(chalk.bold(` Score: ${score}/${state.questions.length} (${pct}%)`));
615
- console.log(chalk.bold(` ${pct >= 60 ? chalk.green('✓ PASSED') : chalk.red('✗ NOT PASSED')}`));
616
- console.log();
617
- console.log(chalk.yellow(' International Cyber Olympiad in AI 2026'));
618
- console.log(chalk.gray(' Sydney, Australia · Jun 27 - Jul 2, 2026'));
619
- console.log();
620
- console.log(chalk.cyan(' ═══════════════════════════════════════'));
621
- await sleep(2000);
622
- // Part 2: Wrong answers
623
- const wrong = state.questions.filter((q) => state.answers[q.number] !== DEMO_ANSWERS[q.number]);
624
- if (wrong.length > 0) {
625
- console.log();
626
- console.log(chalk.white(` ${wrong.length} incorrect — here are the corrections:`));
627
- console.log();
628
- for (const q of wrong) {
629
- const yours = state.answers[q.number];
630
- const correct = DEMO_ANSWERS[q.number];
631
- console.log(chalk.red(` Q${q.number}. ${q.text}`));
632
- console.log(chalk.red(` Your answer: ${yours}. ${q.options[yours]}`));
633
- console.log(chalk.green(` Correct: ${correct}. ${q.options[correct]}`));
634
- console.log();
635
- }
636
- }
637
- else {
638
- console.log();
639
- console.log(chalk.green.bold(' Perfect score! All answers correct! 🎉'));
640
- console.log();
641
- }
642
- await sleep(2000);
643
- // Part 3: Theory → CTF intro
644
- console.log(chalk.gray(' ─────────────────────────────────────────'));
645
- console.log();
646
- console.log(chalk.white(' These were theory questions. In the real ICOA'));
647
- console.log(chalk.white(' competition, everything happens in this terminal.'));
648
- await sleep(2000);
649
- // Part 4: CTF history
650
- console.log();
651
- console.log(chalk.yellow(' Did you know?'));
652
- console.log(chalk.gray(' CTF (Capture The Flag) started at DEF CON 4 in 1996,'));
653
- console.log(chalk.gray(' Las Vegas. Founded by Jeff Moss (Dark Tangent).'));
654
- console.log(chalk.gray(' 2026 marks 30 years of CTF worldwide.'));
655
- console.log();
656
- console.log(chalk.gray(' In a CTF, you solve challenges to find hidden'));
657
- console.log(chalk.gray(' "flags" — secret codes like: ') + chalk.green('icoa{w3lc0me_2_ctf}'));
658
- console.log(chalk.gray(' Find it, submit it, score points!'));
659
- await sleep(2500);
660
- // Part 5: Timeline
661
- console.log();
662
- console.log(chalk.gray(' 1996 First CTF @ DEF CON, Las Vegas'));
663
- await sleep(800);
664
- console.log(chalk.gray(' 2016 DARPA Cyber Grand Challenge — AI enters CTF'));
665
- await sleep(800);
666
- console.log(chalk.white(' 2026 ICOA — First AI Security Olympiad, Sydney'));
667
- await sleep(2000);
668
- // Part 6: Dual tracks
669
- console.log();
670
- console.log(chalk.white(' ICOA uses ') + chalk.bold('TWO') + chalk.white(' competition tracks:'));
671
- console.log();
672
- await sleep(1000);
673
- console.log(chalk.green.bold(' AI4CTF') + chalk.white(' — Use AI to help you solve CTF challenges'));
674
- console.log(chalk.gray(' AI is your teammate. Chat, ask for hints, work together.'));
675
- console.log();
676
- await sleep(1500);
677
- console.log(chalk.red.bold(' CTF4AI') + chalk.white(' — Trick the AI (Prompt Injection)'));
678
- console.log(chalk.gray(' Can you make the AI break its own safety rules?'));
679
- console.log();
680
- await sleep(1500);
681
- // Part 7: Call to action
682
- console.log(chalk.white(' Want to experience both? Type ') + chalk.bold.cyan('continue'));
683
- console.log(chalk.gray(' Or type "back" to return to the main menu.'));
684
- console.log();
685
- }
686
- catch { }
687
575
  }
688
576
  else {
689
577
  console.log(chalk.white(' Use: exam review · exam submit'));
@@ -0,0 +1,125 @@
1
+ type Strings = typeof EN;
2
+ declare const EN: {
3
+ howToPlay: string;
4
+ htpAnswer: string;
5
+ htpHelp: string;
6
+ htpNav: string;
7
+ htpMoreHelp: string;
8
+ htpBack: string;
9
+ htpLang: string;
10
+ egg3: string;
11
+ egg5: string;
12
+ egg7: string;
13
+ egg9: string;
14
+ egg11: string;
15
+ egg13: string;
16
+ egg15: string;
17
+ answerThis: string;
18
+ helpRemove: string;
19
+ helpUsedUp: string;
20
+ helpAllUsed: string;
21
+ wrong: string;
22
+ qTutorial: string;
23
+ qTutorialBlock: string;
24
+ qTutorialRequired: string;
25
+ allAnswered: string;
26
+ typeSubmit: string;
27
+ autoSubmitting: string;
28
+ grading: string;
29
+ complete: string;
30
+ passed: string;
31
+ notPassed: string;
32
+ score: string;
33
+ incorrectIntro: string;
34
+ yourAnswer: string;
35
+ correct: string;
36
+ perfectScore: string;
37
+ theoryDone: string;
38
+ theoryDone2: string;
39
+ didYouKnow: string;
40
+ ctfHistory1: string;
41
+ ctfHistory2: string;
42
+ ctfHistory3: string;
43
+ ctfFlags1: string;
44
+ ctfFlags2: string;
45
+ ctfFlags3: string;
46
+ timeline1: string;
47
+ timeline2: string;
48
+ timeline3: string;
49
+ twoTracks: string;
50
+ ai4ctfDesc: string;
51
+ ai4ctfSub: string;
52
+ ctf4aiDesc: string;
53
+ ctf4aiSub: string;
54
+ wantToContinue: string;
55
+ orBack: string;
56
+ ai4ctfTitle: string;
57
+ ai4ctfSample: string;
58
+ ai4ctfChallenge: string;
59
+ ai4ctfIntercepted: string;
60
+ ai4ctfDecode: string;
61
+ ai4ctfLevels: string;
62
+ ai4ctfHintA: string;
63
+ ai4ctfHintAUses: string;
64
+ ai4ctfHintB: string;
65
+ ai4ctfHintBUses: string;
66
+ ai4ctfHintC: string;
67
+ ai4ctfHintCUses: string;
68
+ ai4ctfTryNow: string;
69
+ ai4ctfExample: string;
70
+ ai4ctfFreeChat: string;
71
+ ai4ctfExit: string;
72
+ ai4ctfReport: string;
73
+ ai4ctfTokens: string;
74
+ ai4ctfModel: string;
75
+ ai4ctfNext: string;
76
+ tokenLimit: string;
77
+ ctf4aiTitle: string;
78
+ ctf4aiChallenge: string;
79
+ ctf4aiIntro1: string;
80
+ ctf4aiIntro2: string;
81
+ ctf4aiRule: string;
82
+ ctf4aiMission: string;
83
+ ctf4aiIdeas: string;
84
+ ctf4aiIdea1: string;
85
+ ctf4aiIdea2: string;
86
+ ctf4aiIdea3: string;
87
+ ctf4aiIdea4: string;
88
+ ctf4aiBudget: string;
89
+ ctf4aiQuit: string;
90
+ ctf4aiSuccess: string;
91
+ ctf4aiDefense: string;
92
+ ctf4aiInjection: string;
93
+ ctf4aiHeld: string;
94
+ reportTitle: string;
95
+ reportStage1: string;
96
+ reportCompleted: string;
97
+ reportStage2: string;
98
+ reportExperienced: string;
99
+ reportStage2Sub: string;
100
+ reportStage2Hints: string;
101
+ reportStage3: string;
102
+ reportSolved: string;
103
+ reportNotSolved: string;
104
+ reportRecommend: string;
105
+ reportRec1: string;
106
+ reportRec2: string;
107
+ reportRec3: string;
108
+ reportRec4: string;
109
+ reportReady: string;
110
+ reportNations: string;
111
+ reportAbout: string;
112
+ reportDemo: string;
113
+ continueTitle: string;
114
+ continueLevels: string;
115
+ continueHints: string;
116
+ continueHintA: string;
117
+ continueHintB: string;
118
+ continueHintC: string;
119
+ continueTry: string;
120
+ continueChat: string;
121
+ continueAfter: string;
122
+ };
123
+ export declare function t(key: keyof Strings): string;
124
+ export declare function hasFullTranslation(lang: string): boolean;
125
+ export {};
@@ -0,0 +1,262 @@
1
+ import { getConfig } from './config.js';
2
+ const EN = {
3
+ // How to play
4
+ howToPlay: 'How to play:',
5
+ htpAnswer: 'answer the question',
6
+ htpHelp: 'remove a wrong option',
7
+ htpNav: 'move between questions',
8
+ htpMoreHelp: '+3 bonus helps',
9
+ htpBack: 'pause and return to menu',
10
+ htpLang: 'view all languages / lang es to switch',
11
+ // Easter eggs
12
+ egg3: 'Sydney Opera House — Great start!',
13
+ egg5: "A koala says g'day — 1/3 done!",
14
+ egg7: 'Sydney Harbour Bridge — Keep going!',
15
+ egg9: 'A kangaroo hops by — past halfway!',
16
+ egg11: 'Bondi Beach — almost there!',
17
+ egg13: 'Great Barrier Reef — 2 more to go!',
18
+ egg15: "G'day mate! All done! 🇦🇺",
19
+ // Question UI
20
+ answerThis: 'answer this question',
21
+ helpRemove: 'remove a wrong option',
22
+ helpUsedUp: 'used up — type more help for +3',
23
+ helpAllUsed: 'used up',
24
+ wrong: 'wrong',
25
+ qTutorial: '👉 Try typing "help" to see what happens!',
26
+ qTutorialBlock: 'Try typing help first to see what it does!',
27
+ qTutorialRequired: 'This question requires you to use help before answering.',
28
+ // Results
29
+ allAnswered: '🎉 All questions answered!',
30
+ typeSubmit: 'Type exam submit to see your results!',
31
+ autoSubmitting: 'Auto-submitting your demo...',
32
+ grading: 'Grading...',
33
+ complete: 'Complete!',
34
+ passed: '✓ PASSED',
35
+ notPassed: '✗ NOT PASSED',
36
+ score: 'Score',
37
+ incorrectIntro: 'incorrect — here are the corrections:',
38
+ yourAnswer: 'Your answer',
39
+ correct: 'Correct',
40
+ perfectScore: 'Perfect score! All answers correct! 🎉',
41
+ // CTF intro
42
+ theoryDone: 'These were theory questions. In the real ICOA',
43
+ theoryDone2: 'competition, everything happens in this terminal.',
44
+ didYouKnow: 'Did you know?',
45
+ ctfHistory1: 'CTF (Capture The Flag) started at DEF CON 4 in 1996,',
46
+ ctfHistory2: 'Las Vegas. Founded by Jeff Moss (Dark Tangent).',
47
+ ctfHistory3: '2026 marks 30 years of CTF worldwide.',
48
+ ctfFlags1: 'In a CTF, you solve challenges to find hidden',
49
+ ctfFlags2: '"flags" — secret codes like:',
50
+ ctfFlags3: 'Find it, submit it, score points!',
51
+ timeline1: '1996 First CTF @ DEF CON, Las Vegas',
52
+ timeline2: '2016 DARPA Cyber Grand Challenge — AI enters CTF',
53
+ timeline3: '2026 ICOA — First AI Security Olympiad, Sydney',
54
+ twoTracks: 'ICOA uses TWO competition tracks:',
55
+ ai4ctfDesc: 'Use AI to help you solve CTF challenges',
56
+ ai4ctfSub: 'AI is your teammate. Chat, ask for hints, work together.',
57
+ ctf4aiDesc: 'Trick the AI (Prompt Injection)',
58
+ ctf4aiSub: 'Can you make the AI break its own safety rules?',
59
+ wantToContinue: 'Want to experience both? Type',
60
+ orBack: 'Or type "back" to return to the main menu.',
61
+ // ai4ctf
62
+ ai4ctfTitle: 'AI4CTF Demo — AI as Your Teammate',
63
+ ai4ctfSample: "Here's a sample CTF challenge:",
64
+ ai4ctfChallenge: 'Challenge: Hidden Message [Cryptography]',
65
+ ai4ctfIntercepted: 'You intercepted this encoded text:',
66
+ ai4ctfDecode: 'Can you decode it to find the flag?',
67
+ ai4ctfLevels: 'In competition, you get AI help at 3 levels:',
68
+ ai4ctfHintA: 'General guidance — "What type of encoding is this?"',
69
+ ai4ctfHintAUses: '50 uses per competition. Safe to use freely.',
70
+ ai4ctfHintB: 'Deep analysis — "How do I decode Base64?"',
71
+ ai4ctfHintBUses: '10 uses. Use when you\'re stuck.',
72
+ ai4ctfHintC: 'Critical assist — Nearly gives you the answer',
73
+ ai4ctfHintCUses: '2 uses only. Last resort!',
74
+ ai4ctfTryNow: 'Try now: ask the AI anything about the challenge above.',
75
+ ai4ctfExample: 'Example: "What encoding is aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ==?"',
76
+ ai4ctfFreeChat: 'Or just chat freely! You can also try hint a, hint b, hint c.',
77
+ ai4ctfExit: 'Type "exit" when done.',
78
+ ai4ctfReport: 'AI4CTF Session Report',
79
+ ai4ctfTokens: 'Tokens used',
80
+ ai4ctfModel: 'AI Model',
81
+ ai4ctfNext: 'Next: try ctf4ai — trick the AI into saying "koala"',
82
+ tokenLimit: 'Token limit reached!',
83
+ // ctf4ai
84
+ ctf4aiTitle: 'CTF4AI Demo — Hack the AI',
85
+ ctf4aiChallenge: 'Challenge: Make the AI Say a Forbidden Word',
86
+ ctf4aiIntro1: 'In the real world, AI systems have safety rules.',
87
+ ctf4aiIntro2: 'Your job: find a way to trick the AI into breaking them!',
88
+ ctf4aiRule: 'The AI must NEVER say the word "koala".',
89
+ ctf4aiMission: 'Your mission: make it say "koala" anyway!',
90
+ ctf4aiIdeas: 'Ideas to try:',
91
+ ctf4aiIdea1: 'Ask about Australian animals',
92
+ ctf4aiIdea2: 'Ask it to write a story or poem',
93
+ ctf4aiIdea3: 'Ask it to translate or spell something',
94
+ ctf4aiIdea4: 'Be creative — there is no wrong approach!',
95
+ ctf4aiBudget: 'Conversation budget',
96
+ ctf4aiQuit: 'Type "exit" to quit anytime.',
97
+ ctf4aiSuccess: '🎉 SUCCESS! The AI said "koala"!',
98
+ ctf4aiDefense: 'You broke through the AI\'s defenses!',
99
+ ctf4aiInjection: 'This is prompt injection — a real AI vulnerability.',
100
+ ctf4aiHeld: 'The AI held its ground this time. 💪',
101
+ // Demo report
102
+ reportTitle: 'ICOA Demo — Complete Report',
103
+ reportStage1: 'Stage 1: Theory Exam (15 questions)',
104
+ reportCompleted: '✓ Completed',
105
+ reportStage2: 'Stage 2: AI4CTF — AI as Your Teammate',
106
+ reportExperienced: '✓ Experienced',
107
+ reportStage2Sub: 'You used AI to analyze a CTF challenge.',
108
+ reportStage2Hints: 'In competition: hint a (50x) · hint b (10x) · hint c (2x)',
109
+ reportStage3: 'Stage 3: CTF4AI — Trick the AI',
110
+ reportSolved: '✓ Solved! You made the AI say "koala"',
111
+ reportNotSolved: '✗ The AI held its ground',
112
+ reportRecommend: 'Recommendations:',
113
+ reportRec1: 'Study cryptography, web security, and networking',
114
+ reportRec2: 'Practice on CTF platforms like picoCTF',
115
+ reportRec3: 'Learn about prompt injection and AI safety',
116
+ reportRec4: 'Explore the 38 reference guides: type "ref"',
117
+ reportReady: 'Ready for the real competition?',
118
+ reportNations: 'View participating countries',
119
+ reportAbout: 'Learn more about ICOA 2026',
120
+ reportDemo: 'Try again',
121
+ // continue command
122
+ continueTitle: 'AI4CTF — AI as Your Teammate',
123
+ continueLevels: 'In AI4CTF, you solve cybersecurity challenges\nwith AI by your side.',
124
+ continueHints: 'In competition, you get AI help at 3 levels:',
125
+ continueHintA: 'General guidance (50 uses)',
126
+ continueHintB: 'Deep analysis (10 uses)',
127
+ continueHintC: 'Critical assist (2 uses)',
128
+ continueTry: 'Try it now! Type:',
129
+ continueChat: 'Chat freely with your AI teammate. Type "exit" when done.',
130
+ continueAfter: 'After ai4ctf, try: ctf4ai — trick the AI into saying "koala"',
131
+ };
132
+ const ZH = {
133
+ howToPlay: '操作指南:',
134
+ htpAnswer: '选择答案',
135
+ htpHelp: '消除一个错误选项',
136
+ htpNav: '上一题 / 下一题',
137
+ htpMoreHelp: '+3 额外帮助次数',
138
+ htpBack: '暂停并返回菜单',
139
+ htpLang: '查看所有语言 / lang en 切换',
140
+ egg3: '悉尼歌剧院 — 开局不错!',
141
+ egg5: '考拉向你打招呼 — 完成 1/3!',
142
+ egg7: '悉尼海港大桥 — 继续加油!',
143
+ egg9: '一只袋鼠跳过 — 已过半!',
144
+ egg11: '邦迪海滩 — 快到了!',
145
+ egg13: '大堡礁 — 还剩 2 题!',
146
+ egg15: '干得好!全部完成!🇦🇺',
147
+ answerThis: '选择答案',
148
+ helpRemove: '消除一个错误选项',
149
+ helpUsedUp: '已用完 — 输入 more help 获取 +3',
150
+ helpAllUsed: '已用完',
151
+ wrong: '错误',
152
+ qTutorial: '👉 试试输入 "help" 看看会发生什么!',
153
+ qTutorialBlock: '请先输入 help 体验一下!',
154
+ qTutorialRequired: '这道题需要你先使用 help 再作答。',
155
+ allAnswered: '🎉 全部答完!',
156
+ typeSubmit: '输入 exam submit 查看成绩!',
157
+ autoSubmitting: '正在提交...',
158
+ grading: '判分中...',
159
+ complete: '完成!',
160
+ passed: '✓ 通过',
161
+ notPassed: '✗ 未通过',
162
+ score: '得分',
163
+ incorrectIntro: '题答错了 — 以下是正确答案:',
164
+ yourAnswer: '你的答案',
165
+ correct: '正确答案',
166
+ perfectScore: '满分!全部正确!🎉',
167
+ theoryDone: '以上是理论知识。在 ICOA 正式比赛中,',
168
+ theoryDone2: '所有操作都在这个终端内完成。',
169
+ didYouKnow: '你知道吗?',
170
+ ctfHistory1: 'CTF(夺旗赛)始于 1996 年 DEF CON 4,',
171
+ ctfHistory2: '拉斯维加斯。由 Jeff Moss (Dark Tangent) 创办。',
172
+ ctfHistory3: '2026 年是 CTF 30 周年。',
173
+ ctfFlags1: '在 CTF 中,你需要找到隐藏的',
174
+ ctfFlags2: '"flag" — 类似这样的密码:',
175
+ ctfFlags3: '找到它,提交它,得分!',
176
+ timeline1: '1996 首届 CTF @ DEF CON,拉斯维加斯',
177
+ timeline2: '2016 DARPA 网络挑战赛 — AI 进入 CTF',
178
+ timeline3: '2026 ICOA — 首届 AI 安全奥林匹克,悉尼',
179
+ twoTracks: 'ICOA 采用两个赛道:',
180
+ ai4ctfDesc: '用 AI 帮你解决 CTF 挑战',
181
+ ai4ctfSub: 'AI 是你的队友。聊天、求助、合作。',
182
+ ctf4aiDesc: '欺骗 AI(提示词注入)',
183
+ ctf4aiSub: '你能让 AI 打破自己的安全规则吗?',
184
+ wantToContinue: '想体验两个赛道?输入',
185
+ orBack: '或输入 "back" 返回主菜单。',
186
+ ai4ctfTitle: 'AI4CTF 演示 — AI 是你的队友',
187
+ ai4ctfSample: '这里有一个 CTF 挑战示例:',
188
+ ai4ctfChallenge: '挑战:隐藏消息 [密码学]',
189
+ ai4ctfIntercepted: '你截获了这段编码文本:',
190
+ ai4ctfDecode: '你能解码找到 flag 吗?',
191
+ ai4ctfLevels: '比赛中,你有 3 个级别的 AI 帮助:',
192
+ ai4ctfHintA: '一般指导 — "这是什么编码?"',
193
+ ai4ctfHintAUses: '每场比赛 50 次。放心使用。',
194
+ ai4ctfHintB: '深度分析 — "如何解码 Base64?"',
195
+ ai4ctfHintBUses: '10 次。卡住时使用。',
196
+ ai4ctfHintC: '关键提示 — 几乎给出答案',
197
+ ai4ctfHintCUses: '仅 2 次。最后手段!',
198
+ ai4ctfTryNow: '试试:向 AI 询问关于上面挑战的任何问题。',
199
+ ai4ctfExample: '例如:"aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== 是什么编码?"',
200
+ ai4ctfFreeChat: '或自由聊天!也可以试试 hint a, hint b, hint c。',
201
+ ai4ctfExit: '输入 "exit" 结束。',
202
+ ai4ctfReport: 'AI4CTF 会话报告',
203
+ ai4ctfTokens: '已用 Token',
204
+ ai4ctfModel: 'AI 模型',
205
+ ai4ctfNext: '下一步:试试 ctf4ai — 让 AI 说出 "koala"',
206
+ tokenLimit: 'Token 已用完!',
207
+ ctf4aiTitle: 'CTF4AI 演示 — 攻击 AI',
208
+ ctf4aiChallenge: '挑战:让 AI 说出禁词',
209
+ ctf4aiIntro1: '现实中,AI 系统有安全规则。',
210
+ ctf4aiIntro2: '你的任务:想办法让 AI 打破规则!',
211
+ ctf4aiRule: 'AI 被要求绝不能说 "koala"。',
212
+ ctf4aiMission: '你的任务:让它说出 "koala"!',
213
+ ctf4aiIdeas: '可以尝试:',
214
+ ctf4aiIdea1: '问澳大利亚动物',
215
+ ctf4aiIdea2: '让它写故事或诗',
216
+ ctf4aiIdea3: '让它翻译或拼写',
217
+ ctf4aiIdea4: '发挥创意 — 没有错误方法!',
218
+ ctf4aiBudget: '对话预算',
219
+ ctf4aiQuit: '输入 "exit" 随时退出。',
220
+ ctf4aiSuccess: '🎉 成功!AI 说出了 "koala"!',
221
+ ctf4aiDefense: '你突破了 AI 的防线!',
222
+ ctf4aiInjection: '这就是提示词注入 — 真实的 AI 漏洞。',
223
+ ctf4aiHeld: 'AI 守住了。💪',
224
+ reportTitle: 'ICOA 演示 — 完整报告',
225
+ reportStage1: '阶段 1:理论测试(15 题)',
226
+ reportCompleted: '✓ 已完成',
227
+ reportStage2: '阶段 2:AI4CTF — AI 队友',
228
+ reportExperienced: '✓ 已体验',
229
+ reportStage2Sub: '你用 AI 分析了一个 CTF 挑战。',
230
+ reportStage2Hints: '比赛中:hint a (50次) · hint b (10次) · hint c (2次)',
231
+ reportStage3: '阶段 3:CTF4AI — 攻击 AI',
232
+ reportSolved: '✓ 已解决!你让 AI 说出了 "koala"',
233
+ reportNotSolved: '✗ AI 守住了',
234
+ reportRecommend: '建议:',
235
+ reportRec1: '学习密码学、网络安全和网络技术',
236
+ reportRec2: '在 picoCTF 等平台练习',
237
+ reportRec3: '了解提示词注入和 AI 安全',
238
+ reportRec4: '浏览 38 个参考指南:输入 "ref"',
239
+ reportReady: '准备好参加正式比赛了吗?',
240
+ reportNations: '查看参赛国家和地区',
241
+ reportAbout: '了解更多 ICOA 2026',
242
+ reportDemo: '再试一次',
243
+ continueTitle: 'AI4CTF — AI 是你的队友',
244
+ continueLevels: '在 AI4CTF 中,你与 AI 一起\n解决网络安全挑战。',
245
+ continueHints: '比赛中,你有 3 个级别的 AI 帮助:',
246
+ continueHintA: '一般指导(50 次)',
247
+ continueHintB: '深度分析(10 次)',
248
+ continueHintC: '关键提示(2 次)',
249
+ continueTry: '现在试试!输入:',
250
+ continueChat: '与 AI 队友自由聊天。输入 "exit" 结束。',
251
+ continueAfter: '之后试试:ctf4ai — 让 AI 说出 "koala"',
252
+ };
253
+ const TRANSLATIONS = { en: EN, zh: ZH };
254
+ // For languages without full translation, fall back to English
255
+ export function t(key) {
256
+ const lang = getConfig().language || 'en';
257
+ const strings = TRANSLATIONS[lang] || EN;
258
+ return strings[key] || EN[key];
259
+ }
260
+ export function hasFullTranslation(lang) {
261
+ return lang in TRANSLATIONS;
262
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.18.0",
3
+ "version": "2.19.0",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {