icoa-cli 2.19.35 → 2.19.36

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.
@@ -43,68 +43,45 @@ const DEMO_FLAG = 'icoa{w3lc0me_2_ai4ctf}';
43
43
  // already enough. Instead c explains the hint principle and reminds the
44
44
  // user how to run a shell command inside ai4ctf (the `!` prefix).
45
45
  // This mirrors real competition behavior where trivial challenges cap at hint b.
46
- const DEMO_HINTS = {
47
- a: [
48
- 'The string ends with "==" and uses only letters, digits, "+", "/", and "=".',
49
- 'That pattern is a fingerprint — you have seen it before.',
50
- 'Where is "=" typically used as padding?',
51
- ],
52
- b: [
53
- 'This is Base64 encoded. Decode the string to find the flag.',
54
- ],
55
- c: [
56
- 'Hints never give you the answer directly — they only point you in the right direction.',
57
- 'For simple challenges like this one, hint c adds nothing extra:',
58
- 'hint b already tells you everything you need.',
59
- '',
60
- 'Two ways forward from here:',
61
- '',
62
- ' 1. Chat with your AI teammate in natural language. Examples:',
63
- ' what is the base64 command?',
64
- ' how do I decode Base64 on macOS?',
65
- ' Just type the question — anything without "!" goes to the AI.',
66
- '',
67
- ' 2. Run a shell command directly. Shell commands inside ai4ctf',
68
- ' must start with "!", otherwise your text goes to the AI. Example:',
69
- '',
70
- ' !echo aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== | base64 -d',
71
- ],
72
- };
46
+ // Multi-line hint bodies live in i18n.ts (ai4ctfHintABody/BBody/CBody).
73
47
  // Shown when the user hits the 5000-token demo cap without solving. Keeps
74
48
  // the session alive (no chatActive=false) so they can still paste the shell
75
49
  // command below and then `submit <flag>`.
76
50
  function showTokenCapReveal() {
77
51
  console.log();
78
52
  console.log(chalk.yellow(' ─────────────────────────────────────────────'));
79
- console.log(chalk.bold.yellow(' 💡 Out of AI tokens — here is the reveal'));
53
+ console.log(chalk.bold.yellow(` ${t('ai4ctfRevealTitle')}`));
80
54
  console.log(chalk.yellow(' ─────────────────────────────────────────────'));
81
55
  console.log();
82
- console.log(chalk.white(' Looks like you have not found the flag yet.'));
83
- console.log(chalk.white(' No worries — for the demo we will just tell you.'));
84
- console.log();
85
- console.log(chalk.white(' Run this command to decode the Base64 string:'));
56
+ for (const line of t('ai4ctfRevealBody').split('\n')) {
57
+ if (line === '')
58
+ console.log();
59
+ else
60
+ console.log(chalk.white(' ' + line));
61
+ }
86
62
  console.log();
87
63
  console.log(chalk.cyan(' !echo aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== | base64 -d'));
88
64
  console.log();
89
- console.log(chalk.white(' You will see the flag:'));
65
+ console.log(chalk.white(` ${t('ai4ctfRevealSeeFlag')}`));
90
66
  console.log();
91
67
  console.log(chalk.green(' icoa{w3lc0me_2_ai4ctf}'));
92
68
  console.log();
93
- console.log(chalk.white(' Then submit it:'));
69
+ console.log(chalk.white(` ${t('ai4ctfRevealThenSubmit')}`));
94
70
  console.log();
95
71
  console.log(chalk.cyan(' submit icoa{w3lc0me_2_ai4ctf}'));
96
72
  console.log();
97
- console.log(chalk.gray(' (AI chat is locked — only shell commands and submit work now.)'));
73
+ console.log(chalk.gray(` ${t('ai4ctfRevealLockNote')}`));
98
74
  console.log();
99
75
  }
100
76
  function showDemoHint(tier) {
101
77
  const title = tier === 'a' ? t('ai4ctfHintA') : tier === 'b' ? t('ai4ctfHintB') : t('ai4ctfHintC');
78
+ const body = tier === 'a' ? t('ai4ctfHintABody') : tier === 'b' ? t('ai4ctfHintBBody') : t('ai4ctfHintCBody');
102
79
  const tierLabel = `Hint ${tier.toUpperCase()}`;
103
80
  const color = tier === 'a' ? chalk.green : tier === 'b' ? chalk.yellow : chalk.red;
104
81
  console.log();
105
82
  console.log(color.bold(` ▸ ${tierLabel} `) + chalk.gray(title));
106
83
  console.log();
107
- for (const line of DEMO_HINTS[tier]) {
84
+ for (const line of body.split('\n')) {
108
85
  if (line === '')
109
86
  console.log();
110
87
  else
@@ -112,11 +89,11 @@ function showDemoHint(tier) {
112
89
  }
113
90
  console.log();
114
91
  if (tier === 'a') {
115
- console.log(chalk.gray(' Stuck? Try: ') + chalk.cyan('hint b'));
92
+ console.log(chalk.gray(` ${t('ai4ctfHintNextA')} `) + chalk.cyan('hint b'));
116
93
  console.log();
117
94
  }
118
95
  else if (tier === 'b') {
119
- console.log(chalk.gray(' Really stuck? Try: ') + chalk.cyan('hint c'));
96
+ console.log(chalk.gray(` ${t('ai4ctfHintNextB')} `) + chalk.cyan('hint c'));
120
97
  console.log();
121
98
  }
122
99
  // No trailing CTA after hint c — the content itself explains everything.
@@ -161,41 +138,53 @@ export async function handleChatMessage(input) {
161
138
  await sleep(1500);
162
139
  console.log();
163
140
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
164
- console.log(chalk.bold.white(' ✨ What you just learned'));
141
+ console.log(chalk.bold.white(` ${t('ai4ctfWrapTitle')}`));
165
142
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
166
143
  console.log();
167
- console.log(chalk.white(' 1. Need a command? ') + chalk.gray('Just ask your AI teammate in natural'));
168
- console.log(chalk.gray(' language "how do I decode Base64 on the command line?"'));
169
- console.log(chalk.gray(' You do not need to memorize every tool. The AI era is'));
170
- console.log(chalk.gray(' about ') + chalk.white('thinking') + chalk.gray(', not mechanical memorization.'));
144
+ const wrap1Head = t('ai4ctfWrapLine1Head');
145
+ const wrap1Body = t('ai4ctfWrapLine1Body');
146
+ console.log(chalk.white(` 1. ${wrap1Head} `) + chalk.gray(wrap1Body.split('\n')[0] || ''));
147
+ for (const line of wrap1Body.split('\n').slice(1)) {
148
+ if (line.trim())
149
+ console.log(chalk.gray(' ' + line.replace(/\{thinking\}/g, 'thinking')));
150
+ }
171
151
  console.log();
172
- console.log(chalk.white(' 2. Need to run something? ') + chalk.gray('Prefix shell commands with "!":'));
152
+ console.log(chalk.white(` 2. ${t('ai4ctfWrapLine2Head')} `) + chalk.gray(t('ai4ctfWrapLine2Body')));
173
153
  console.log();
174
154
  console.log(chalk.cyan(' !echo aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== | base64 -d'));
175
155
  console.log();
176
156
  await sleep(2000);
177
157
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
178
- console.log(chalk.bold.white(' 🎯 Real competition hint quotas'));
158
+ console.log(chalk.bold.white(` ${t('ai4ctfWrapQuotasTitle')}`));
179
159
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
180
160
  console.log();
181
- console.log(chalk.yellow(' hint a ') + chalk.white('50 uses per competition ') + chalk.gray('(use freely)'));
182
- console.log(chalk.yellow(' hint b ') + chalk.white('10 uses ') + chalk.gray('(when stuck)'));
183
- console.log(chalk.yellow(' hint c ') + chalk.white(' 2 uses ') + chalk.gray('(last resort)'));
161
+ console.log(chalk.yellow(' hint a ') + chalk.white(t('ai4ctfWrapQuotaA').padEnd(26)) + chalk.gray(t('ai4ctfWrapQuotaAHint')));
162
+ console.log(chalk.yellow(' hint b ') + chalk.white(t('ai4ctfWrapQuotaB').padEnd(26)) + chalk.gray(t('ai4ctfWrapQuotaBHint')));
163
+ console.log(chalk.yellow(' hint c ') + chalk.white(t('ai4ctfWrapQuotaC').padEnd(26)) + chalk.gray(t('ai4ctfWrapQuotaCHint')));
184
164
  console.log();
185
- console.log(chalk.gray(' Prefer hint a → b → c. And when you just need to look'));
186
- console.log(chalk.gray(' something up, chat with the AI first — it costs nothing'));
187
- console.log(chalk.gray(' against your hint quota.'));
165
+ for (const line of t('ai4ctfWrapQuotaFooter').split('\n')) {
166
+ console.log(chalk.gray(' ' + line));
167
+ }
188
168
  console.log();
189
169
  await sleep(2000);
190
170
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
191
- console.log(chalk.bold.white(' 🔜 Next up: ') + chalk.red.bold('ctf4ai') + chalk.white('trick the AI into saying "koala"'));
171
+ console.log(chalk.bold.white(` ${t('ai4ctfWrapNextTitle')} `) + chalk.red.bold('ctf4ai') + chalk.white(`${t('ai4ctfWrapNextSub')}`));
192
172
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
193
173
  console.log();
194
- console.log(chalk.white(' This next demo ') + chalk.bold.white('does not need programming') + chalk.white(' — just your'));
195
- console.log(chalk.white(' wit and creativity. Can you make a "safe" AI break its'));
196
- console.log(chalk.white(' own rules?'));
174
+ const nextBody = t('ai4ctfWrapNextBody');
175
+ const noProg = t('ai4ctfWrapNoProg');
176
+ const lines = nextBody.split('\n');
177
+ // First line contains the {noProg} placeholder to bold-emphasize.
178
+ if (lines[0]) {
179
+ const [before, after] = lines[0].split('{noProg}');
180
+ console.log(chalk.white(' ' + (before || '')) + chalk.bold.white(noProg) + chalk.white(after || ''));
181
+ }
182
+ for (const line of lines.slice(1)) {
183
+ if (line.trim())
184
+ console.log(chalk.white(' ' + line));
185
+ }
197
186
  console.log();
198
- console.log(chalk.gray(' Type: ') + chalk.bold.red('ctf4ai'));
187
+ console.log(chalk.gray(` ${t('ai4ctfWrapTypeCtf4ai')} `) + chalk.bold.red('ctf4ai'));
199
188
  console.log();
200
189
  return 'exit';
201
190
  }
@@ -274,9 +263,9 @@ export async function handleChatMessage(input) {
274
263
  // back to the user with a reminder of what actually works now.
275
264
  if (tokensLocked) {
276
265
  console.log();
277
- console.log(chalk.yellow(' AI chat is locked — out of tokens.'));
278
- console.log(chalk.gray(' Use: ') + chalk.cyan('!echo aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== | base64 -d'));
279
- console.log(chalk.gray(' Then: ') + chalk.cyan('submit icoa{w3lc0me_2_ai4ctf}'));
266
+ console.log(chalk.yellow(` ${t('ai4ctfLockedTitle')}`));
267
+ console.log(chalk.gray(` ${t('ai4ctfLockedUse')} `) + chalk.cyan('!echo aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== | base64 -d'));
268
+ console.log(chalk.gray(` ${t('ai4ctfLockedThen')} `) + chalk.cyan('submit icoa{w3lc0me_2_ai4ctf}'));
280
269
  console.log();
281
270
  return 'continue';
282
271
  }
@@ -349,19 +338,19 @@ export function registerAi4ctfCommand(program) {
349
338
  console.log(chalk.gray(` ${t('ai4ctfHintCUses')}`));
350
339
  console.log();
351
340
  console.log(chalk.gray(' ─────────────────────────────────────────'));
352
- console.log(chalk.bold.white(' 👉 New here? Start with the hints in order:'));
353
- console.log(' ' + chalk.cyan('hint a') + chalk.gray('nudge'));
354
- console.log(' ' + chalk.cyan('hint b') + chalk.gray('technique'));
355
- console.log(' ' + chalk.cyan('hint c') + chalk.gray('principle + shell reminder'));
356
- console.log(chalk.gray(' (hints guide you — they never give the answer directly)'));
341
+ console.log(chalk.bold.white(` ${t('ai4ctfWelcomeCta')}`));
342
+ console.log(' ' + chalk.cyan('hint a') + chalk.gray(`${t('ai4ctfHintNudge')}`));
343
+ console.log(' ' + chalk.cyan('hint b') + chalk.gray(`${t('ai4ctfHintTechnique')}`));
344
+ console.log(' ' + chalk.cyan('hint c') + chalk.gray(`${t('ai4ctfHintPrinciple')}`));
345
+ console.log(chalk.gray(` ${t('ai4ctfWelcomeNoReveal')}`));
357
346
  console.log();
358
- console.log(chalk.white(' Or chat freely with your AI teammate — ask anything'));
359
- console.log(chalk.gray(' about the challenge. Example: ') + chalk.white('"what encoding is this?"'));
347
+ console.log(chalk.white(` ${t('ai4ctfOrChat')}`));
348
+ console.log(chalk.gray(` ${t('ai4ctfOrChatExample')} `) + chalk.white('"what encoding is this?"'));
360
349
  console.log();
361
350
  console.log(chalk.yellow(` ${t('ai4ctfCommands')}`));
362
- console.log(chalk.white(' hint a / b / c ') + chalk.gray('pre-written hints (safe to use)'));
351
+ console.log(chalk.white(' hint a / b / c ') + chalk.gray(t('ai4ctfCmdHintLine')));
363
352
  console.log(chalk.white(' submit <flag> ') + chalk.gray(t('ai4ctfSubmitCmd')));
364
- console.log(chalk.white(' !<shell cmd> ') + chalk.gray('run a shell command'));
353
+ console.log(chalk.white(' !<shell cmd> ') + chalk.gray(t('ai4ctfCmdShellLine')));
365
354
  console.log(chalk.gray(' e.g. ') + chalk.white('!echo aWNv... | base64 -d'));
366
355
  console.log(chalk.gray(` exit ${t('ai4ctfEndSession')}`));
367
356
  console.log();
@@ -50,18 +50,20 @@ function printDemoReport(ctf4aiSolved, ctf4aiTokens) {
50
50
  console.log(chalk.white(` ${t('theoryDone2')}`));
51
51
  console.log();
52
52
  if (hasWrongAnswers) {
53
- console.log(chalk.white(' 💪 Want to nail the ones you missed? Type: ') + chalk.bold.cyan('retry'));
53
+ console.log(chalk.white(` 💪 ${t('reportRetryCta')} `) + chalk.bold.cyan('retry'));
54
54
  console.log();
55
55
  }
56
56
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
57
57
  console.log(chalk.white(` ${t('reportReady')}`));
58
58
  if (hasWrongAnswers) {
59
- console.log(chalk.cyan(' retry') + chalk.gray(` retry the ${retryQueue.length} wrong question${retryQueue.length > 1 ? 's' : ''}`));
59
+ const n = retryQueue.length;
60
+ const tmpl = n === 1 ? t('reportRetryWrongN') : t('reportRetryWrongNPlural');
61
+ console.log(chalk.cyan(' retry') + chalk.gray(` ${tmpl.replace('{n}', String(n))}`));
60
62
  }
61
- console.log(chalk.white(' back') + chalk.gray(` return to main menu`));
63
+ console.log(chalk.white(' back') + chalk.gray(` ${t('reportBackHint')}`));
62
64
  console.log(chalk.white(' demo') + chalk.gray(` ${t('reportDemo')}`));
63
65
  console.log(chalk.white(' nations') + chalk.gray(` ${t('reportNations')}`));
64
- console.log(chalk.white(' about') + chalk.gray(` ${t('reportAbout')}`));
66
+ console.log(chalk.white(' about') + chalk.gray(` ${t('reportAboutHint')}`));
65
67
  console.log();
66
68
  console.log(chalk.yellow(' ICOA 2026 · Sydney, Australia · Jun 27 - Jul 2'));
67
69
  console.log(chalk.cyan.underline(' https://icoa2026.au'));
@@ -260,8 +260,8 @@ function printQuestion(q, answer) {
260
260
  console.log(chalk.yellow(' A/B/C/D') + chalk.gray(` ${t('answerThis')}`));
261
261
  console.log(chalk.yellow(' help') + ' ' + helpLabel);
262
262
  console.log(chalk.yellow(' next') + chalk.gray(' / ') + chalk.yellow('prev') + chalk.gray(` ${t('htpNav')}`));
263
- console.log(chalk.yellow(` exam q 1..${total}`) + chalk.gray(' jump to a specific question'));
264
- console.log(chalk.yellow(' exam review') + chalk.gray(' check progress'));
263
+ console.log(chalk.yellow(` exam q 1..${total}`) + chalk.gray(` ${t('htpJump')}`));
264
+ console.log(chalk.yellow(' exam review') + chalk.gray(` ${t('htpReview')}`));
265
265
  console.log(chalk.yellow(' back') + chalk.gray(` ${t('htpBack')}`));
266
266
  console.log(chalk.yellow(' lang') + chalk.gray(` ${t('htpLang')}`));
267
267
  console.log(chalk.gray(' ─────────────────────────────────────────'));
@@ -929,7 +929,7 @@ export function registerExamCommand(program) {
929
929
  await sleep(2000);
930
930
  console.log();
931
931
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
932
- console.log(chalk.white(' Next: ') + chalk.bold.green('ai4ctf') + chalk.gray(` — ${t('ai4ctfDesc')}`));
932
+ console.log(chalk.white(` ${t('nextLabel')} `) + chalk.bold.green('ai4ctf') + chalk.gray(` — ${t('ai4ctfDesc')}`));
933
933
  console.log(chalk.gray(` ${t('ai4ctfSub')}`));
934
934
  console.log(chalk.cyan(' ─────────────────────────────────────────────'));
935
935
  console.log();
@@ -1,5 +1,5 @@
1
- type Strings = typeof EN;
2
- declare const EN: {
1
+ type Strings = Partial<typeof EN>;
2
+ export declare const EN: {
3
3
  howToPlay: string;
4
4
  htpAnswer: string;
5
5
  htpHelp: string;
@@ -139,6 +139,54 @@ declare const EN: {
139
139
  forNational: string;
140
140
  viewRegions: string;
141
141
  enterExam: string;
142
+ htpJump: string;
143
+ htpReview: string;
144
+ nextLabel: string;
145
+ reportRetryCta: string;
146
+ reportRetryWrongN: string;
147
+ reportRetryWrongNPlural: string;
148
+ reportBackHint: string;
149
+ reportAboutHint: string;
150
+ ai4ctfWelcomeCta: string;
151
+ ai4ctfHintNudge: string;
152
+ ai4ctfHintTechnique: string;
153
+ ai4ctfHintPrinciple: string;
154
+ ai4ctfWelcomeNoReveal: string;
155
+ ai4ctfOrChat: string;
156
+ ai4ctfOrChatExample: string;
157
+ ai4ctfCmdHintLine: string;
158
+ ai4ctfCmdShellLine: string;
159
+ ai4ctfHintABody: string;
160
+ ai4ctfHintBBody: string;
161
+ ai4ctfHintCBody: string;
162
+ ai4ctfHintNextA: string;
163
+ ai4ctfHintNextB: string;
164
+ ai4ctfWrapTitle: string;
165
+ ai4ctfWrapLine1Head: string;
166
+ ai4ctfWrapLine1Body: string;
167
+ ai4ctfWrapLine2Head: string;
168
+ ai4ctfWrapLine2Body: string;
169
+ ai4ctfWrapQuotasTitle: string;
170
+ ai4ctfWrapQuotaA: string;
171
+ ai4ctfWrapQuotaAHint: string;
172
+ ai4ctfWrapQuotaB: string;
173
+ ai4ctfWrapQuotaBHint: string;
174
+ ai4ctfWrapQuotaC: string;
175
+ ai4ctfWrapQuotaCHint: string;
176
+ ai4ctfWrapQuotaFooter: string;
177
+ ai4ctfWrapNextTitle: string;
178
+ ai4ctfWrapNextSub: string;
179
+ ai4ctfWrapNextBody: string;
180
+ ai4ctfWrapNoProg: string;
181
+ ai4ctfWrapTypeCtf4ai: string;
182
+ ai4ctfRevealTitle: string;
183
+ ai4ctfRevealBody: string;
184
+ ai4ctfRevealSeeFlag: string;
185
+ ai4ctfRevealThenSubmit: string;
186
+ ai4ctfRevealLockNote: string;
187
+ ai4ctfLockedTitle: string;
188
+ ai4ctfLockedUse: string;
189
+ ai4ctfLockedThen: string;
142
190
  };
143
191
  export declare function t(key: keyof Strings): string;
144
192
  export declare function hasFullTranslation(lang: string): boolean;