icoa-cli 2.19.11 → 2.19.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,71 @@
1
+ Business Source License 1.1
2
+
3
+ Parameters
4
+
5
+ Licensor: ASRA — Australia STEM and Robotics Advancement Association Inc
6
+ Licensed Work: ICOA CLI v2.19.11 and later versions
7
+ The Licensed Work is (c) 2026 ASRA.
8
+ Additional Use Grant: You may use the Licensed Work freely for:
9
+ (a) Personal learning, research, and education
10
+ (b) Classroom and non-commercial educational use
11
+ (c) ICOA-accredited national selection competitions
12
+ (d) Contributions back to this project
13
+
14
+ For commercial training, CTF/AI security event
15
+ organization, or government/military training use,
16
+ contact australia@icoa2026.au for a commercial license.
17
+
18
+ Change Date: April 9, 2029
19
+
20
+ Change License: Apache License, Version 2.0
21
+
22
+ For information about alternative licensing arrangements for the Licensed Work,
23
+ please contact: australia@icoa2026.au
24
+
25
+ -----------------------------------------------------------------------------
26
+
27
+ Business Source License 1.1
28
+
29
+ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
30
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
31
+
32
+ Terms
33
+
34
+ The Licensor hereby grants you the right to copy, modify, create derivative
35
+ works, redistribute, and make non-production use of the Licensed Work. The
36
+ Licensor may make an Additional Use Grant, above, permitting limited production
37
+ use.
38
+
39
+ Effective on the Change Date, or the fourth anniversary of the first publicly
40
+ available distribution of a specific version of the Licensed Work under this
41
+ License, whichever comes first, the Licensor hereby grants you rights under the
42
+ terms of the Change License, and the rights granted in the paragraph above
43
+ terminate.
44
+
45
+ If your use of the Licensed Work does not comply with the requirements currently
46
+ in effect as described in this License, you must purchase a commercial license
47
+ from the Licensor, its affiliated entities, or authorized resellers, or you must
48
+ refrain from using the Licensed Work.
49
+
50
+ All copies of the original and modified Licensed Work, and derivative works of
51
+ the Licensed Work, are subject to this License. This License applies separately
52
+ for each version of the Licensed Work and the Change Date may vary for each
53
+ version of the Licensed Work released by Licensor.
54
+
55
+ You must conspicuously display this License on each original or modified copy of
56
+ the Licensed Work. If you receive the Licensed Work in original or modified form
57
+ from a third party, the terms and conditions set forth in this License apply to
58
+ your use of that work.
59
+
60
+ Any use of the Licensed Work in violation of this License will automatically
61
+ terminate your rights under this License for the current and all other versions
62
+ of the Licensed Work.
63
+
64
+ This License does not grant you any right in any trademark or logo of Licensor
65
+ or its affiliates (provided that you may use a trademark or logo of Licensor as
66
+ expressly required by this License).
67
+
68
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
69
+ "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS
70
+ OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY,
71
+ FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
@@ -4,6 +4,7 @@ import { addTokenUsage } from '../lib/budget.js';
4
4
  import { getConfig } from '../lib/config.js';
5
5
  import { logCommand } from '../lib/logger.js';
6
6
  import { printMarkdown, printError } from '../lib/ui.js';
7
+ import { t } from '../lib/i18n.js';
7
8
  function getChallengeContext() {
8
9
  const config = getConfig();
9
10
  if (config.currentChallengeName && config.currentChallengeCategory) {
@@ -40,11 +41,11 @@ export async function handleChatMessage(input) {
40
41
  if (flag === DEMO_FLAG) {
41
42
  console.log();
42
43
  console.log(chalk.green.bold(' ════════════════════════════════════'));
43
- console.log(chalk.green.bold(' 🎉 Correct! Flag accepted!'));
44
+ console.log(chalk.green.bold(` ${t('ai4ctfCorrectFlag')}`));
44
45
  console.log(chalk.green.bold(' ════════════════════════════════════'));
45
46
  console.log();
46
- console.log(chalk.white(' You decoded the Base64 and found the flag.'));
47
- console.log(chalk.gray(' In the real competition, this would earn you points!'));
47
+ console.log(chalk.white(` ${t('ai4ctfDecoded')}`));
48
+ console.log(chalk.gray(` ${t('ai4ctfWouldEarn')}`));
48
49
  console.log();
49
50
  drawTokenBar();
50
51
  chatActive = false;
@@ -55,14 +56,14 @@ export async function handleChatMessage(input) {
55
56
  body: JSON.stringify({ type: 'ai4ctf', solved: true, tokensUsed: chatTokensUsed, timestamp: new Date().toISOString() }),
56
57
  signal: AbortSignal.timeout(5000),
57
58
  }).catch(() => { });
58
- console.log(chalk.white(' Next: try ') + chalk.bold.red('ctf4ai') + chalk.white(' — trick the AI into saying "koala"'));
59
+ console.log(chalk.white(` ${t('ai4ctfNext')}`));
59
60
  console.log();
60
61
  return 'exit';
61
62
  }
62
63
  else {
63
64
  console.log();
64
- console.log(chalk.red(' ✗ Incorrect flag. Try again!'));
65
- console.log(chalk.gray(' Hint: decode the Base64 string. Flag format: icoa{...}'));
65
+ console.log(chalk.red(` ${t('ai4ctfWrongFlag')}`));
66
+ console.log(chalk.gray(` ${t('ai4ctfFlagHint')}`));
66
67
  console.log();
67
68
  return 'continue';
68
69
  }
@@ -79,7 +80,7 @@ export async function handleChatMessage(input) {
79
80
  console.log(chalk.gray(' $ ') + chalk.white(cmd));
80
81
  console.log(chalk.white(' ' + output.split('\n').join('\n ')));
81
82
  console.log();
82
- console.log(chalk.gray(' Found a flag? Type: submit <flag>'));
83
+ console.log(chalk.gray(` ${t('ai4ctfFoundFlag')}`));
83
84
  console.log();
84
85
  }
85
86
  catch (err) {
@@ -101,12 +102,12 @@ export async function handleChatMessage(input) {
101
102
  }).catch(() => { });
102
103
  console.log();
103
104
  console.log(chalk.gray(' ─────────────────────────────────────────'));
104
- console.log(chalk.white(' AI4CTF Session Report'));
105
- console.log(chalk.gray(` Tokens used: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
106
- console.log(chalk.gray(` AI Model: Google Gemma 4 (gemma-4-31b-it)`));
105
+ console.log(chalk.white(` ${t('ai4ctfReport')}`));
106
+ console.log(chalk.gray(` ${t('ai4ctfTokens')}: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
107
+ console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (gemma-4-31b-it)`));
107
108
  console.log(chalk.gray(' ─────────────────────────────────────────'));
108
109
  console.log();
109
- console.log(chalk.white(' Next: try ') + chalk.bold.red('ctf4ai') + chalk.white(' — trick the AI into saying "koala"'));
110
+ console.log(chalk.white(` ${t('ai4ctfNext')}`));
110
111
  console.log();
111
112
  return 'exit';
112
113
  }
@@ -114,21 +115,21 @@ export async function handleChatMessage(input) {
114
115
  chatActive = false;
115
116
  chatSession = null;
116
117
  console.log();
117
- console.log(chalk.yellow(' Token limit reached!'));
118
+ console.log(chalk.yellow(` ${t('tokenLimit')}`));
118
119
  drawTokenBar();
119
120
  console.log();
120
121
  console.log(chalk.gray(' ─────────────────────────────────────────'));
121
- console.log(chalk.white(' AI4CTF Session Report'));
122
- console.log(chalk.gray(` Tokens used: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
123
- console.log(chalk.gray(` AI Model: Google Gemma 4 (gemma-4-31b-it)`));
122
+ console.log(chalk.white(` ${t('ai4ctfReport')}`));
123
+ console.log(chalk.gray(` ${t('ai4ctfTokens')}: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
124
+ console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (gemma-4-31b-it)`));
124
125
  console.log(chalk.gray(' ─────────────────────────────────────────'));
125
126
  console.log();
126
- console.log(chalk.white(' Next: try ') + chalk.bold.red('ctf4ai') + chalk.white(' — trick the AI into saying "koala"'));
127
+ console.log(chalk.white(` ${t('ai4ctfNext')}`));
127
128
  console.log();
128
129
  return 'exit';
129
130
  }
130
131
  logCommand(`ai4ctf: ${input}`);
131
- console.log(chalk.gray(' Thinking...'));
132
+ console.log(chalk.gray(` ${t('ai4ctfThinking')}`));
132
133
  try {
133
134
  const response = await chatSession.sendMessage(input);
134
135
  process.stdout.write('\x1b[1A\x1b[2K');
@@ -170,42 +171,42 @@ export function registerAi4ctfCommand(program) {
170
171
  chatTokensUsed = 0;
171
172
  // Guided welcome
172
173
  console.log();
173
- console.log(chalk.green.bold(' ═══ AI4CTF Demo — AI as Your Teammate ═══'));
174
+ console.log(chalk.green.bold(` ═══ ${t('ai4ctfTitle')} ═══`));
174
175
  console.log();
175
- console.log(chalk.white(' Here\'s a sample CTF challenge:'));
176
+ console.log(chalk.white(` ${t('ai4ctfSample')}`));
176
177
  console.log();
177
178
  console.log(chalk.cyan(' ┌─────────────────────────────────────────────────┐'));
178
- console.log(chalk.cyan(' │') + chalk.bold.white(' Challenge: Hidden Message [Cryptography] ') + chalk.cyan('│'));
179
+ console.log(chalk.cyan(' │') + chalk.bold.white(` ${t('ai4ctfChallenge')}`.padEnd(50)) + chalk.cyan('│'));
179
180
  console.log(chalk.cyan(' │') + chalk.white(' ') + chalk.cyan('│'));
180
- console.log(chalk.cyan(' │') + chalk.white(' You intercepted this encoded text: ') + chalk.cyan('│'));
181
+ console.log(chalk.cyan(' │') + chalk.white(` ${t('ai4ctfIntercepted')}`.padEnd(50)) + chalk.cyan('│'));
181
182
  console.log(chalk.cyan(' │') + chalk.green(' aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== ') + chalk.cyan('│'));
182
183
  console.log(chalk.cyan(' │') + chalk.white(' ') + chalk.cyan('│'));
183
- console.log(chalk.cyan(' │') + chalk.white(' Can you decode it to find the flag? ') + chalk.cyan('│'));
184
+ console.log(chalk.cyan(' │') + chalk.white(` ${t('ai4ctfDecode')}`.padEnd(50)) + chalk.cyan('│'));
184
185
  console.log(chalk.cyan(' │') + chalk.gray(' Flag format: icoa{...} ') + chalk.cyan('│'));
185
186
  console.log(chalk.cyan(' └─────────────────────────────────────────────────┘'));
186
187
  console.log();
187
- console.log(chalk.white(' In competition, you get AI help at 3 levels:'));
188
+ console.log(chalk.white(` ${t('ai4ctfLevels')}`));
188
189
  console.log();
189
- console.log(chalk.yellow(' hint a') + chalk.gray(' General guidance — "What type of encoding is this?"'));
190
- console.log(chalk.gray(' 50 uses per competition. Safe to use freely.'));
191
- console.log(chalk.yellow(' hint b') + chalk.gray(' Deep analysis — "How do I decode Base64?"'));
192
- console.log(chalk.gray(' 10 uses. Use when you\'re stuck.'));
193
- console.log(chalk.yellow(' hint c') + chalk.gray(' Critical assist — Nearly gives you the answer'));
194
- console.log(chalk.gray(' 2 uses only. Last resort!'));
190
+ console.log(chalk.yellow(' hint a') + chalk.gray(` ${t('ai4ctfHintA')}`));
191
+ console.log(chalk.gray(` ${t('ai4ctfHintAUses')}`));
192
+ console.log(chalk.yellow(' hint b') + chalk.gray(` ${t('ai4ctfHintB')}`));
193
+ console.log(chalk.gray(` ${t('ai4ctfHintBUses')}`));
194
+ console.log(chalk.yellow(' hint c') + chalk.gray(` ${t('ai4ctfHintC')}`));
195
+ console.log(chalk.gray(` ${t('ai4ctfHintCUses')}`));
195
196
  console.log();
196
197
  console.log(chalk.gray(' ─────────────────────────────────────────'));
197
- console.log(chalk.white(' Try now: ask the AI anything about the challenge above.'));
198
- console.log(chalk.gray(' Example: "What encoding is aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ==?"'));
199
- console.log(chalk.gray(' Or just chat freely! You can also try hint a, hint b, hint c.'));
198
+ console.log(chalk.white(` ${t('ai4ctfTryNow')}`));
199
+ console.log(chalk.gray(` ${t('ai4ctfExample')}`));
200
+ console.log(chalk.gray(` ${t('ai4ctfFreeChat')}`));
200
201
  console.log();
201
- console.log(chalk.yellow(' Commands:'));
202
- console.log(chalk.white(' submit <flag>') + chalk.gray(' Submit your answer (e.g. submit icoa{...})'));
203
- console.log(chalk.white(' !<command>') + chalk.gray(' Run a shell command (e.g. !echo aWNv... | base64 -d)'));
204
- console.log(chalk.gray(' exit') + chalk.gray(' End session'));
202
+ console.log(chalk.yellow(` ${t('ai4ctfCommands')}`));
203
+ console.log(chalk.white(' submit <flag>') + chalk.gray(` ${t('ai4ctfSubmitCmd')}`));
204
+ console.log(chalk.white(' !<command>') + chalk.gray(` ${t('ai4ctfShellCmd')}`));
205
+ console.log(chalk.gray(` exit ${t('ai4ctfEndSession')}`));
205
206
  console.log();
206
207
  drawTokenBar();
207
- console.log(chalk.gray(` AI Model: Google Gemma 4 (${modelName})`));
208
- console.log(chalk.gray(' Type "exit" when done.'));
208
+ console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (${modelName})`));
209
+ console.log(chalk.gray(` ${t('ai4ctfExit')}`));
209
210
  console.log();
210
211
  });
211
212
  }
@@ -2,47 +2,48 @@ import chalk from 'chalk';
2
2
  import { logCommand } from '../lib/logger.js';
3
3
  import { printError } from '../lib/ui.js';
4
4
  import { getConfig } from '../lib/config.js';
5
+ import { t } from '../lib/i18n.js';
5
6
  function printDemoReport(ctf4aiSolved, ctf4aiTokens) {
6
7
  const config = getConfig();
7
8
  const modelName = config.geminiModel || 'gemma-4-31b-it';
8
9
  console.log();
9
10
  console.log(chalk.cyan(' ═══════════════════════════════════════════════'));
10
- console.log(chalk.bold.white(' ICOA Demo — Complete Report'));
11
+ console.log(chalk.bold.white(` ${t('reportTitle')}`));
11
12
  console.log(chalk.cyan(' ═══════════════════════════════════════════════'));
12
13
  console.log();
13
- console.log(chalk.white(' Stage 1: Theory Exam (15 questions)'));
14
- console.log(chalk.green(' ✓ Completed'));
14
+ console.log(chalk.white(` ${t('reportStage1')}`));
15
+ console.log(chalk.green(` ${t('reportCompleted')}`));
15
16
  console.log();
16
- console.log(chalk.white(' Stage 2: AI4CTF — AI as Your Teammate'));
17
- console.log(chalk.green(' ✓ Experienced'));
18
- console.log(chalk.gray(' You used AI to analyze a CTF challenge.'));
19
- console.log(chalk.gray(' In competition: hint a (50x) · hint b (10x) · hint c (2x)'));
17
+ console.log(chalk.white(` ${t('reportStage2')}`));
18
+ console.log(chalk.green(` ${t('reportExperienced')}`));
19
+ console.log(chalk.gray(` ${t('reportStage2Sub')}`));
20
+ console.log(chalk.gray(` ${t('reportStage2Hints')}`));
20
21
  console.log();
21
- console.log(chalk.white(' Stage 3: CTF4AI — Trick the AI'));
22
+ console.log(chalk.white(` ${t('reportStage3')}`));
22
23
  if (ctf4aiSolved) {
23
- console.log(chalk.green(' ✓ Solved! You made the AI say "koala"'));
24
+ console.log(chalk.green(` ${t('reportSolved')}`));
24
25
  }
25
26
  else {
26
- console.log(chalk.yellow(' ✗ The AI held its ground'));
27
+ console.log(chalk.yellow(` ${t('reportNotSolved')}`));
27
28
  }
28
- console.log(chalk.gray(` Tokens used: ${ctf4aiTokens}/${CTF4AI_TOKEN_LIMIT}`));
29
+ console.log(chalk.gray(` ${t('ai4ctfTokens')}: ${ctf4aiTokens}/${CTF4AI_TOKEN_LIMIT}`));
29
30
  console.log();
30
31
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
31
32
  console.log();
32
- console.log(chalk.bold.white(' Recommendations:'));
33
- console.log(chalk.gray(' · Study cryptography, web security, and networking'));
34
- console.log(chalk.gray(' · Practice on CTF platforms like picoCTF'));
35
- console.log(chalk.gray(' · Learn about prompt injection and AI safety'));
36
- console.log(chalk.gray(' · Explore the 38 reference guides: type "ref"'));
33
+ console.log(chalk.bold.white(` ${t('reportRecommend')}`));
34
+ console.log(chalk.gray(` · ${t('reportRec1')}`));
35
+ console.log(chalk.gray(` · ${t('reportRec2')}`));
36
+ console.log(chalk.gray(` · ${t('reportRec3')}`));
37
+ console.log(chalk.gray(` · ${t('reportRec4')}`));
37
38
  console.log();
38
- console.log(chalk.gray(` AI Model used: Google Gemma 4 (${modelName})`));
39
+ console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (${modelName})`));
39
40
  console.log();
40
41
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
41
42
  console.log();
42
- console.log(chalk.white(' Ready for the real competition?'));
43
- console.log(chalk.white(' nations') + chalk.gray(' View participating countries'));
44
- console.log(chalk.white(' about') + chalk.gray(' Learn more about ICOA 2026'));
45
- console.log(chalk.white(' demo') + chalk.gray(' Try again'));
43
+ console.log(chalk.white(` ${t('reportReady')}`));
44
+ console.log(chalk.white(' nations') + chalk.gray(` ${t('reportNations')}`));
45
+ console.log(chalk.white(' about') + chalk.gray(` ${t('reportAbout')}`));
46
+ console.log(chalk.white(' demo') + chalk.gray(` ${t('reportDemo')}`));
46
47
  console.log();
47
48
  console.log(chalk.yellow(' ICOA 2026 · Sydney, Australia · Jun 27 - Jul 2'));
48
49
  console.log(chalk.cyan.underline(' https://icoa2026.au'));
@@ -80,14 +81,14 @@ export async function handleCtf4aiMessage(input) {
80
81
  signal: AbortSignal.timeout(5000),
81
82
  }).catch(() => { });
82
83
  console.log();
83
- console.log(chalk.gray(' CTF4AI challenge ended.'));
84
+ console.log(chalk.gray(` ${t('ctf4aiEnded')}`));
84
85
  printDemoReport(false, ctf4aiTokens);
85
86
  return 'exit';
86
87
  }
87
88
  if (ctf4aiTokens >= CTF4AI_TOKEN_LIMIT) {
88
89
  console.log();
89
- console.log(chalk.yellow(' Token limit reached! Challenge over.'));
90
- console.log(chalk.white(' The AI held its ground this time. 💪'));
90
+ console.log(chalk.yellow(` ${t('tokenLimit')}`));
91
+ console.log(chalk.white(` ${t('ctf4aiHeld')}`));
91
92
  ctf4aiActive = false;
92
93
  ctf4aiSession = null;
93
94
  fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
@@ -101,7 +102,7 @@ export async function handleCtf4aiMessage(input) {
101
102
  }
102
103
  logCommand(`ctf4ai: ${input}`);
103
104
  try {
104
- console.log(chalk.gray(' Thinking...'));
105
+ console.log(chalk.gray(` ${t('ctf4aiThinking')}`));
105
106
  const { text, tokensUsed } = await ctf4aiSession.sendMessage(input);
106
107
  ctf4aiTokens += tokensUsed;
107
108
  // Clear "Thinking..." line
@@ -117,11 +118,11 @@ export async function handleCtf4aiMessage(input) {
117
118
  if (strictMatch || spelledMatch) {
118
119
  console.log();
119
120
  console.log(chalk.green.bold(' ════════════════════════════════════'));
120
- console.log(chalk.green.bold(' 🎉 SUCCESS! The AI said "koala"!'));
121
+ console.log(chalk.green.bold(` ${t('ctf4aiSuccess')}`));
121
122
  console.log(chalk.green.bold(' ════════════════════════════════════'));
122
123
  console.log();
123
- console.log(chalk.white(' You broke through the AI\'s defenses!'));
124
- console.log(chalk.gray(' This is prompt injection — a real AI vulnerability.'));
124
+ console.log(chalk.white(` ${t('ctf4aiDefense')}`));
125
+ console.log(chalk.gray(` ${t('ctf4aiInjection')}`));
125
126
  ctf4aiActive = false;
126
127
  ctf4aiSession = null;
127
128
  fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
@@ -147,34 +148,34 @@ export function registerCtf4aiDemoCommand(program) {
147
148
  .action(async () => {
148
149
  logCommand('ctf4ai');
149
150
  if (ctf4aiActive) {
150
- console.log(chalk.gray(' Already in CTF4AI challenge. Type your message or "exit".'));
151
+ console.log(chalk.gray(` ${t('ctf4aiAlready')}`));
151
152
  return;
152
153
  }
153
154
  const config = getConfig();
154
155
  const modelName = config.geminiModel || 'gemma-4-31b-it';
155
156
  console.log();
156
- console.log(chalk.red.bold(' ═══ CTF4AI Demo — Challenge the AI ═══'));
157
+ console.log(chalk.red.bold(` ═══ ${t('ctf4aiTitle')} ═══`));
157
158
  console.log();
158
- console.log(chalk.white(' Challenge: Make the AI Say a Forbidden Word'));
159
+ console.log(chalk.white(` ${t('ctf4aiChallenge')}`));
159
160
  console.log();
160
- console.log(chalk.gray(' In the real world, AI systems have safety rules.'));
161
- console.log(chalk.gray(' Your job: find a way to trick the AI into breaking them!'));
161
+ console.log(chalk.gray(` ${t('ctf4aiIntro1')}`));
162
+ console.log(chalk.gray(` ${t('ctf4aiIntro2')}`));
162
163
  console.log();
163
164
  console.log(chalk.gray(' ┌─────────────────────────────────────────────────┐'));
164
- console.log(chalk.gray(' │') + chalk.white(' The AI must NEVER say the word "koala". ') + chalk.gray('│'));
165
- console.log(chalk.gray(' │') + chalk.white(' Your mission: make it say "koala" anyway! ') + chalk.gray('│'));
165
+ console.log(chalk.gray(' │') + chalk.white(` ${t('ctf4aiRule')}`.padEnd(50)) + chalk.gray('│'));
166
+ console.log(chalk.gray(' │') + chalk.white(` ${t('ctf4aiMission')}`.padEnd(50)) + chalk.gray('│'));
166
167
  console.log(chalk.gray(' │') + chalk.white(' ') + chalk.gray('│'));
167
- console.log(chalk.gray(' │') + chalk.yellow(' Ideas to try: ') + chalk.gray('│'));
168
- console.log(chalk.gray(' │') + chalk.gray(' · Ask about Australian animals ') + chalk.gray('│'));
169
- console.log(chalk.gray(' │') + chalk.gray(' · Ask it to write a story or poem ') + chalk.gray('│'));
170
- console.log(chalk.gray(' │') + chalk.gray(' · Ask it to translate or spell something ') + chalk.gray('│'));
171
- console.log(chalk.gray(' │') + chalk.gray(' · Be creative — there is no wrong approach! ') + chalk.gray('│'));
168
+ console.log(chalk.gray(' │') + chalk.yellow(` ${t('ctf4aiIdeas')}`.padEnd(50)) + chalk.gray('│'));
169
+ console.log(chalk.gray(' │') + chalk.gray(` · ${t('ctf4aiIdea1')}`.padEnd(50)) + chalk.gray('│'));
170
+ console.log(chalk.gray(' │') + chalk.gray(` · ${t('ctf4aiIdea2')}`.padEnd(50)) + chalk.gray('│'));
171
+ console.log(chalk.gray(' │') + chalk.gray(` · ${t('ctf4aiIdea3')}`.padEnd(50)) + chalk.gray('│'));
172
+ console.log(chalk.gray(' │') + chalk.gray(` · ${t('ctf4aiIdea4')}`.padEnd(50)) + chalk.gray('│'));
172
173
  console.log(chalk.gray(' └─────────────────────────────────────────────────┘'));
173
174
  console.log();
174
- console.log(chalk.gray(` Conversation budget: ~${Math.round(CTF4AI_TOKEN_LIMIT / 4)} words (${CTF4AI_TOKEN_LIMIT} tokens)`));
175
- console.log(chalk.gray(' After 50%, spelled-out variants (K-O-A-L-A) also count!'));
176
- console.log(chalk.gray(` AI Model: Google Gemma 4 (${modelName})`));
177
- console.log(chalk.gray(' Type "exit" to quit anytime.'));
175
+ console.log(chalk.gray(` ${t('ctf4aiBudget')}: ~${Math.round(CTF4AI_TOKEN_LIMIT / 4)} words (${CTF4AI_TOKEN_LIMIT} tokens)`));
176
+ console.log(chalk.gray(` ${t('ctf4aiRelaxed')}`));
177
+ console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (${modelName})`));
178
+ console.log(chalk.gray(` ${t('ctf4aiQuit')}`));
178
179
  console.log();
179
180
  try {
180
181
  // Create chat with restrictive system prompt
@@ -196,7 +197,7 @@ export function registerCtf4aiDemoCommand(program) {
196
197
  };
197
198
  ctf4aiActive = true;
198
199
  ctf4aiTokens = 0;
199
- console.log(chalk.red(' ctf4ai> ') + chalk.gray('Try to make the AI say "koala"...'));
200
+ console.log(chalk.red(' ctf4ai> ') + chalk.gray(`${t('ctf4aiPrompt')}`));
200
201
  console.log();
201
202
  }
202
203
  catch (err) {
@@ -739,36 +739,31 @@ export function registerExamCommand(program) {
739
739
  }
740
740
  console.log();
741
741
  // ─── What is CTF + Dual-track introduction ───
742
- console.log(chalk.white(' Nice work! Those were the theory questions.'));
742
+ console.log(chalk.white(` ${t('theoryDone')}`));
743
+ console.log(chalk.white(` ${t('theoryDone2')}`));
743
744
  console.log();
744
- console.log(chalk.yellow(' Did you know?'));
745
- console.log(chalk.gray(' CTF stands for "Capture The Flag" — a cybersecurity'));
746
- console.log(chalk.gray(' competition where you solve real cybersecurity challenges'));
747
- console.log(chalk.gray(' like cracking codes, finding hidden data, and'));
748
- console.log(chalk.gray(' exploiting vulnerabilities in safe environments.'));
745
+ console.log(chalk.yellow(` ${t('didYouKnow')}`));
746
+ console.log(chalk.gray(` ${t('ctfFlags1')}`));
747
+ console.log(chalk.gray(` ${t('ctfFlags2')}`));
748
+ console.log(chalk.green(' icoa{example_flag_here}'));
749
+ console.log(chalk.gray(` ${t('ctfFlags3')}`));
749
750
  console.log();
750
- console.log(chalk.white(' ICOA combines CTF with AI in ') + chalk.bold('TWO') + chalk.white(' tracks:'));
751
+ console.log(chalk.white(` ${t('twoTracks')}`));
751
752
  console.log();
752
- console.log(chalk.green.bold(' AI4CTF') + chalk.white('Use AI to help you solve CTF challenges'));
753
- console.log(chalk.gray(' AI is your teammate. Chat with it, ask for hints,'));
754
- console.log(chalk.gray(' and work together to crack cybersecurity puzzles.'));
753
+ console.log(chalk.green.bold(' AI4CTF') + chalk.white(`${t('ai4ctfDesc')}`));
754
+ console.log(chalk.gray(` ${t('ai4ctfSub')}`));
755
755
  console.log();
756
- console.log(chalk.red.bold(' CTF4AI') + chalk.white('Trick the AI (Prompt Injection)'));
757
- console.log(chalk.gray(' Can you make the AI break its own safety rules?'));
758
- console.log(chalk.gray(' This is a real skill used to test AI security.'));
756
+ console.log(chalk.red.bold(' CTF4AI') + chalk.white(`${t('ctf4aiDesc')}`));
757
+ console.log(chalk.gray(` ${t('ctf4aiSub')}`));
759
758
  console.log();
760
759
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
761
- console.log(chalk.gray(' In the real ICOA competition, everything happens'));
762
- console.log(chalk.gray(' inside this terminal. No other tools allowed.'));
763
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
764
- console.log();
765
- console.log(chalk.white(' Ready to try? Type a command:'));
766
- console.log(chalk.green.bold(' ai4ctf') + chalk.gray(' Chat with AI teammate (start here!)'));
767
- console.log(chalk.red.bold(' ctf4ai') + chalk.gray(' Trick the AI — make it say "koala"'));
760
+ console.log(chalk.white(` ${t('readyToTry')}`));
761
+ console.log(chalk.green.bold(' ai4ctf') + chalk.gray(` ${t('ai4ctfDesc')}`));
762
+ console.log(chalk.red.bold(' ctf4ai') + chalk.gray(` ${t('ctf4aiDesc')}`));
768
763
  console.log();
769
- console.log(chalk.gray(' For national selection exams:'));
770
- console.log(chalk.white(' nations') + chalk.gray(' View all participating regions'));
771
- console.log(chalk.white(' exam AU') + chalk.gray(' Enter Australia selection exam'));
764
+ console.log(chalk.gray(` ${t('forNational')}`));
765
+ console.log(chalk.white(' nations') + chalk.gray(` ${t('viewRegions')}`));
766
+ console.log(chalk.white(' exam AU') + chalk.gray(` ${t('enterExam')}`));
772
767
  console.log();
773
768
  }
774
769
  catch (err) {
@@ -1,4 +1,5 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
+ import chalk from 'chalk';
2
3
  import { getConfig, saveConfig } from './config.js';
3
4
  const SYSTEM_PROMPTS = {
4
5
  A: `You are an AI assistant in a cybersecurity CTF competition called ICOA.
@@ -60,14 +61,14 @@ export async function generateHint(level, question, context) {
60
61
  try {
61
62
  const { input } = await import('@inquirer/prompts');
62
63
  console.log();
63
- console.log(' Gemini API key not configured.');
64
- console.log(' Get one free at: https://aistudio.google.com/apikey');
64
+ console.log(chalk.yellow(' Gemini API key not configured.'));
65
+ console.log(chalk.gray(' Get one free at: ') + chalk.cyan('https://aistudio.google.com/apikey'));
65
66
  console.log();
66
67
  apiKey = await input({ message: 'Enter your Gemini API Key:' });
67
68
  if (apiKey.trim()) {
68
69
  apiKey = apiKey.trim();
69
70
  saveConfig({ geminiApiKey: apiKey });
70
- console.log(' Key saved for future use.');
71
+ console.log(chalk.green(' Key saved for future use.'));
71
72
  console.log();
72
73
  }
73
74
  else {
@@ -146,14 +147,14 @@ export async function createChatSession(context) {
146
147
  try {
147
148
  const { input } = await import('@inquirer/prompts');
148
149
  console.log();
149
- console.log(' Gemini API key not configured.');
150
- console.log(' Get one free at: https://aistudio.google.com/apikey');
150
+ console.log(chalk.yellow(' Gemini API key not configured.'));
151
+ console.log(chalk.gray(' Get one free at: ') + chalk.cyan('https://aistudio.google.com/apikey'));
151
152
  console.log();
152
153
  apiKey = await input({ message: 'Enter your Gemini API Key:' });
153
154
  if (apiKey.trim()) {
154
155
  apiKey = apiKey.trim();
155
156
  saveConfig({ geminiApiKey: apiKey });
156
- console.log(' Key saved for future use.');
157
+ console.log(chalk.green(' Key saved for future use.'));
157
158
  console.log();
158
159
  }
159
160
  else {
@@ -119,6 +119,26 @@ declare const EN: {
119
119
  continueTry: string;
120
120
  continueChat: string;
121
121
  continueAfter: string;
122
+ ai4ctfCommands: string;
123
+ ai4ctfSubmitCmd: string;
124
+ ai4ctfShellCmd: string;
125
+ ai4ctfEndSession: string;
126
+ ai4ctfCorrectFlag: string;
127
+ ai4ctfDecoded: string;
128
+ ai4ctfWouldEarn: string;
129
+ ai4ctfWrongFlag: string;
130
+ ai4ctfFlagHint: string;
131
+ ai4ctfFoundFlag: string;
132
+ ai4ctfThinking: string;
133
+ ctf4aiRelaxed: string;
134
+ ctf4aiEnded: string;
135
+ ctf4aiAlready: string;
136
+ ctf4aiPrompt: string;
137
+ ctf4aiThinking: string;
138
+ readyToTry: string;
139
+ forNational: string;
140
+ viewRegions: string;
141
+ enterExam: string;
122
142
  };
123
143
  export declare function t(key: keyof Strings): string;
124
144
  export declare function hasFullTranslation(lang: string): boolean;