icoa-cli 2.19.19 → 2.19.21
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/dist/commands/exam.js +207 -4
- package/dist/commands/lang.js +1 -0
- package/dist/lib/demo-exam.js +2 -1
- package/dist/lib/demo-stats.d.ts +16 -0
- package/dist/lib/demo-stats.js +65 -0
- package/dist/lib/i18n.js +2 -1
- package/dist/lib/translation.js +1 -0
- package/dist/repl.js +4 -3
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +2 -1
- package/package.json +1 -1
- package/translations/uk/1.json +9 -0
- package/translations/uk/10.json +9 -0
- package/translations/uk/11.json +9 -0
- package/translations/uk/12.json +9 -0
- package/translations/uk/13.json +9 -0
- package/translations/uk/14.json +9 -0
- package/translations/uk/15.json +9 -0
- package/translations/uk/16.json +9 -0
- package/translations/uk/17.json +9 -0
- package/translations/uk/18.json +9 -0
- package/translations/uk/19.json +9 -0
- package/translations/uk/20.json +9 -0
- package/translations/uk/21.json +9 -0
- package/translations/uk/22.json +9 -0
- package/translations/uk/23.json +9 -0
- package/translations/uk/24.json +9 -0
- package/translations/uk/25.json +9 -0
- package/translations/uk/26.json +9 -0
- package/translations/uk/27.json +9 -0
- package/translations/uk/28.json +9 -0
- package/translations/uk/29.json +9 -0
- package/translations/uk/30.json +9 -0
- package/translations/uk/31.json +9 -0
- package/translations/uk/32.json +9 -0
- package/translations/uk/33.json +9 -0
- package/translations/uk/34.json +9 -0
- package/translations/uk/35.json +9 -0
- package/translations/uk/36.json +9 -0
- package/translations/uk/37.json +9 -0
- package/translations/uk/38.json +9 -0
- package/translations/uk/39.json +9 -0
- package/translations/uk/4.json +9 -0
- package/translations/uk/40.json +9 -0
- package/translations/uk/41.json +9 -0
- package/translations/uk/42.json +9 -0
- package/translations/uk/43.json +9 -0
- package/translations/uk/44.json +9 -0
- package/translations/uk/45.json +9 -0
- package/translations/uk/46.json +9 -0
- package/translations/uk/47.json +9 -0
- package/translations/uk/48.json +9 -0
- package/translations/uk/49.json +9 -0
- package/translations/uk/5.json +9 -0
- package/translations/uk/6.json +9 -0
- package/translations/uk/7.json +9 -0
- package/translations/uk/8.json +9 -0
- package/translations/uk/9.json +9 -0
- package/translations/uk/demo-explanations.json +32 -0
- package/translations/uk/demo.json +332 -0
package/dist/commands/exam.js
CHANGED
|
@@ -1,13 +1,108 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { confirm } from '@inquirer/prompts';
|
|
3
3
|
import { ExamClient } from '../lib/exam-client.js';
|
|
4
|
-
import { getConfig } from '../lib/config.js';
|
|
4
|
+
import { getConfig, saveConfig } from '../lib/config.js';
|
|
5
5
|
import { getExamState, saveExamState, clearExamState, getExamDeadline } from '../lib/exam-state.js';
|
|
6
|
+
import { getDemoStats, recordDemoAttempt, saveRetryQueue, getRetryQueue, clearRetryQueue } from '../lib/demo-stats.js';
|
|
6
7
|
import { logCommand } from '../lib/logger.js';
|
|
7
8
|
import { printSuccess, printError, printWarning, printInfo, printTable, printHeader, printKeyValue, createSpinner, formatCountdown, } from '../lib/ui.js';
|
|
8
9
|
import { t } from '../lib/i18n.js';
|
|
9
10
|
import { getDeviceFingerprint } from '../lib/access.js';
|
|
10
11
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12
|
+
/**
|
|
13
|
+
* Skippable intro animation for first-time demo users.
|
|
14
|
+
* Resolves immediately on any keypress. Max 30s auto-advance.
|
|
15
|
+
*/
|
|
16
|
+
async function playDemoIntro() {
|
|
17
|
+
let skipped = false;
|
|
18
|
+
const stdin = process.stdin;
|
|
19
|
+
const onKey = () => { skipped = true; };
|
|
20
|
+
const rawSupported = stdin.isTTY && typeof stdin.setRawMode === 'function';
|
|
21
|
+
if (rawSupported) {
|
|
22
|
+
stdin.setRawMode(true);
|
|
23
|
+
stdin.resume();
|
|
24
|
+
stdin.once('data', onKey);
|
|
25
|
+
}
|
|
26
|
+
const waitOrSkip = async (ms) => {
|
|
27
|
+
const step = 80;
|
|
28
|
+
let waited = 0;
|
|
29
|
+
while (waited < ms && !skipped) {
|
|
30
|
+
await sleep(step);
|
|
31
|
+
waited += step;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const cleanup = () => {
|
|
35
|
+
if (rawSupported) {
|
|
36
|
+
stdin.removeListener('data', onKey);
|
|
37
|
+
try {
|
|
38
|
+
stdin.setRawMode(false);
|
|
39
|
+
}
|
|
40
|
+
catch { }
|
|
41
|
+
stdin.pause();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
console.clear();
|
|
46
|
+
console.log();
|
|
47
|
+
console.log(chalk.gray(' ') + chalk.dim('(press any key to skip)'));
|
|
48
|
+
console.log();
|
|
49
|
+
console.log(chalk.bold.white(' ██╗ ██████╗ ██████╗ █████╗'));
|
|
50
|
+
console.log(chalk.bold.white(' ██║██╔════╝██╔═══██╗██╔══██╗'));
|
|
51
|
+
console.log(chalk.bold.white(' ██║██║ ██║ ██║███████║'));
|
|
52
|
+
console.log(chalk.bold.white(' ██║██║ ██║ ██║██╔══██║'));
|
|
53
|
+
console.log(chalk.bold.white(' ██║╚██████╗╚██████╔╝██║ ██║'));
|
|
54
|
+
console.log(chalk.bold.white(' ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝'));
|
|
55
|
+
console.log();
|
|
56
|
+
console.log(chalk.bold.yellow(' International Cyber Olympiad in AI'));
|
|
57
|
+
console.log(chalk.gray(' Sydney, Australia · Jun 27 – Jul 2, 2026'));
|
|
58
|
+
await waitOrSkip(2500);
|
|
59
|
+
if (skipped)
|
|
60
|
+
return;
|
|
61
|
+
console.log();
|
|
62
|
+
console.log(chalk.white(' What you\'ll do today:'));
|
|
63
|
+
console.log();
|
|
64
|
+
await waitOrSkip(600);
|
|
65
|
+
if (skipped)
|
|
66
|
+
return;
|
|
67
|
+
console.log(chalk.cyan(' ▣ ') + chalk.white('Answer 10 quick questions') + chalk.gray(' (now, ~5 min)'));
|
|
68
|
+
await waitOrSkip(700);
|
|
69
|
+
if (skipped)
|
|
70
|
+
return;
|
|
71
|
+
console.log(chalk.green(' ▣ ') + chalk.white('Team up with AI to solve CTFs') + chalk.gray(' (ai4ctf)'));
|
|
72
|
+
await waitOrSkip(700);
|
|
73
|
+
if (skipped)
|
|
74
|
+
return;
|
|
75
|
+
console.log(chalk.red(' ▣ ') + chalk.white('Hack the AI\'s guardrails') + chalk.gray(' (ctf4ai)'));
|
|
76
|
+
await waitOrSkip(900);
|
|
77
|
+
if (skipped)
|
|
78
|
+
return;
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(chalk.bold.white(' Your terminal ') + chalk.bold.yellow('IS') + chalk.bold.white(' the competition.'));
|
|
81
|
+
console.log();
|
|
82
|
+
await waitOrSkip(1500);
|
|
83
|
+
if (skipped)
|
|
84
|
+
return;
|
|
85
|
+
console.log(chalk.gray(' · 109 CTF tools pre-configured'));
|
|
86
|
+
await waitOrSkip(400);
|
|
87
|
+
if (skipped)
|
|
88
|
+
return;
|
|
89
|
+
console.log(chalk.gray(' · 15 languages, real-time AI translation'));
|
|
90
|
+
await waitOrSkip(400);
|
|
91
|
+
if (skipped)
|
|
92
|
+
return;
|
|
93
|
+
console.log(chalk.gray(' · 15,000+ concurrent participants'));
|
|
94
|
+
await waitOrSkip(800);
|
|
95
|
+
if (skipped)
|
|
96
|
+
return;
|
|
97
|
+
console.log();
|
|
98
|
+
console.log(chalk.bold.cyan(' Press any key to start demo...'));
|
|
99
|
+
await waitOrSkip(4000);
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
cleanup();
|
|
103
|
+
console.clear();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
11
106
|
function drawProgress(percent, label) {
|
|
12
107
|
const width = 30;
|
|
13
108
|
const filled = Math.round((percent / 100) * width);
|
|
@@ -222,7 +317,7 @@ export function registerExamCommand(program) {
|
|
|
222
317
|
const langHint = {
|
|
223
318
|
PE: 'es', CN: 'zh', JP: 'ja', KR: 'ko', BR: 'pt',
|
|
224
319
|
SA: 'ar', FR: 'fr', DE: 'de', IN: 'hi', ID: 'id',
|
|
225
|
-
TH: 'th', VN: 'vi', TR: 'tr', RU: 'ru',
|
|
320
|
+
TH: 'th', VN: 'vi', TR: 'tr', RU: 'ru', UA: 'uk',
|
|
226
321
|
};
|
|
227
322
|
const code = (country || '').toUpperCase();
|
|
228
323
|
if (langHint[code]) {
|
|
@@ -554,6 +649,14 @@ export function registerExamCommand(program) {
|
|
|
554
649
|
const answered = Object.keys(state.answers).length;
|
|
555
650
|
const total = state.session.questionCount;
|
|
556
651
|
printSuccess(`Q${num}: ${c} ✓ (${answered}/${total} answered)`);
|
|
652
|
+
// Demo-only: gentle per-answer feedback (grey, non-intrusive)
|
|
653
|
+
if (state.session.examId === 'demo-free' && q.answer) {
|
|
654
|
+
const NICE_WORDS = ['Nice one.', 'Solid pick.', 'On the money.', 'Exactly.', 'Spot on.'];
|
|
655
|
+
const CLOSE_WORDS = ['Close — we\'ll revisit this one.', 'Hmm, worth a second look at the end.', 'Keep it in mind for review.'];
|
|
656
|
+
const pool = c === q.answer ? NICE_WORDS : CLOSE_WORDS;
|
|
657
|
+
const word = pool[Math.floor(Math.random() * pool.length)];
|
|
658
|
+
console.log(chalk.gray(` · ${word}`));
|
|
659
|
+
}
|
|
557
660
|
// Auto-show next question and update _lastQ
|
|
558
661
|
if (num < state.questions.length) {
|
|
559
662
|
const nextQ = state.questions[num]; // 0-indexed: questions[num] = question num+1
|
|
@@ -732,19 +835,39 @@ export function registerExamCommand(program) {
|
|
|
732
835
|
console.log(chalk.gray(' Sydney, Australia · Jun 27 - Jul 2, 2026'));
|
|
733
836
|
console.log();
|
|
734
837
|
console.log(chalk.cyan(' ═══════════════════════════════════════'));
|
|
735
|
-
// Per-category breakdown
|
|
838
|
+
// Per-category breakdown with ASCII progress bars
|
|
736
839
|
const catEntries = Object.entries(categoryStats);
|
|
737
840
|
if (catEntries.length > 0) {
|
|
738
841
|
console.log();
|
|
739
842
|
console.log(chalk.bold.white(' By category'));
|
|
843
|
+
const BAR_WIDTH = 10;
|
|
740
844
|
for (const [cat, s] of catEntries) {
|
|
741
845
|
const pct = s.total > 0 ? Math.round(s.correct / s.total * 100) : 0;
|
|
846
|
+
const filled = s.total > 0 ? Math.round((s.correct / s.total) * BAR_WIDTH) : 0;
|
|
847
|
+
const empty = BAR_WIDTH - filled;
|
|
742
848
|
const color = pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.red;
|
|
743
|
-
|
|
849
|
+
const bar = color('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
|
|
850
|
+
console.log(' ' + chalk.white(cat.padEnd(20)) + ' ' + bar + ' ' + color(`${s.correct}/${s.total}`) + chalk.gray(` (${pct}%)`));
|
|
744
851
|
}
|
|
745
852
|
console.log();
|
|
746
853
|
console.log(chalk.cyan(' ─────────────────────────────────────────────'));
|
|
747
854
|
}
|
|
855
|
+
// Record stats locally + save retry queue
|
|
856
|
+
const updatedStats = recordDemoAttempt(score, total);
|
|
857
|
+
const isNewBest = score === updatedStats.best && updatedStats.attempts > 1 && score > 0;
|
|
858
|
+
if (isNewBest) {
|
|
859
|
+
console.log();
|
|
860
|
+
console.log(chalk.bold.green(' ★ New personal best!'));
|
|
861
|
+
}
|
|
862
|
+
if (wrongQuestions.length > 0) {
|
|
863
|
+
const wrongSnapshots = questionsSnapshot
|
|
864
|
+
.filter((q) => wrongQuestions.includes(q.number))
|
|
865
|
+
.map((q) => ({ ...q }));
|
|
866
|
+
saveRetryQueue(wrongSnapshots);
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
clearRetryQueue();
|
|
870
|
+
}
|
|
748
871
|
// Show wrong answers with explanations
|
|
749
872
|
if (wrongQuestions.length > 0) {
|
|
750
873
|
console.log();
|
|
@@ -776,6 +899,11 @@ export function registerExamCommand(program) {
|
|
|
776
899
|
console.log();
|
|
777
900
|
console.log(chalk.green(` ${t('perfectScore')}`));
|
|
778
901
|
}
|
|
902
|
+
// Offer retry wrong only
|
|
903
|
+
if (wrongQuestions.length > 0) {
|
|
904
|
+
console.log();
|
|
905
|
+
console.log(chalk.white(' 💪 Want to nail the ones you missed? Type: ') + chalk.bold.cyan('retry'));
|
|
906
|
+
}
|
|
779
907
|
console.log();
|
|
780
908
|
// ─── What is CTF + Dual-track introduction ───
|
|
781
909
|
console.log(chalk.white(` ${t('theoryDone')}`));
|
|
@@ -966,6 +1094,12 @@ export function registerExamCommand(program) {
|
|
|
966
1094
|
.action(async () => {
|
|
967
1095
|
logCommand('exam demo');
|
|
968
1096
|
const { pickDemoQuestions, getLocalizedDemoSession, DEMO_PICK_SIZE, DEMO_POOL_SIZE } = await import('../lib/demo-exam.js');
|
|
1097
|
+
// First-time intro animation (skippable)
|
|
1098
|
+
const cfg = getConfig();
|
|
1099
|
+
if (!cfg.demoIntroSeen) {
|
|
1100
|
+
await playDemoIntro();
|
|
1101
|
+
saveConfig({ demoIntroSeen: true });
|
|
1102
|
+
}
|
|
969
1103
|
const DEMO_QUESTIONS = pickDemoQuestions(DEMO_PICK_SIZE);
|
|
970
1104
|
const DEMO_SESSION = getLocalizedDemoSession();
|
|
971
1105
|
const existing = getExamState();
|
|
@@ -985,6 +1119,14 @@ export function registerExamCommand(program) {
|
|
|
985
1119
|
console.log();
|
|
986
1120
|
console.log(chalk.white(' Free practice · No account needed · No time limit'));
|
|
987
1121
|
console.log(chalk.white(` ${DEMO_PICK_SIZE} random questions from a pool of ${DEMO_POOL_SIZE} · Pick one answer per question`));
|
|
1122
|
+
// Best score tracker
|
|
1123
|
+
const stats = getDemoStats();
|
|
1124
|
+
if (stats.attempts > 0) {
|
|
1125
|
+
const bestPct = stats.bestPercentage;
|
|
1126
|
+
const bestColor = bestPct >= 80 ? chalk.green : bestPct >= 60 ? chalk.yellow : chalk.white;
|
|
1127
|
+
console.log();
|
|
1128
|
+
console.log(chalk.gray(' Your best: ') + bestColor(`${stats.best}/${DEMO_PICK_SIZE} (${bestPct}%)`) + chalk.gray(` · ${stats.attempts} attempts`));
|
|
1129
|
+
}
|
|
988
1130
|
console.log();
|
|
989
1131
|
printHowToPlay();
|
|
990
1132
|
console.log();
|
|
@@ -1018,6 +1160,67 @@ export function registerExamCommand(program) {
|
|
|
1018
1160
|
// Show first question
|
|
1019
1161
|
printQuestion(DEMO_QUESTIONS[0]);
|
|
1020
1162
|
});
|
|
1163
|
+
// ─── exam demo-retry ─── (runs demo with the wrong questions from last attempt)
|
|
1164
|
+
exam
|
|
1165
|
+
.command('demo-retry')
|
|
1166
|
+
.description('Retry only the questions you got wrong last demo attempt')
|
|
1167
|
+
.action(async () => {
|
|
1168
|
+
logCommand('exam demo-retry');
|
|
1169
|
+
const { getLocalizedDemoSession } = await import('../lib/demo-exam.js');
|
|
1170
|
+
const retryQueue = getRetryQueue();
|
|
1171
|
+
if (!retryQueue || retryQueue.length === 0) {
|
|
1172
|
+
console.log();
|
|
1173
|
+
console.log(chalk.yellow(' No wrong questions to retry. Run ') + chalk.bold.cyan('demo') + chalk.yellow(' first.'));
|
|
1174
|
+
console.log();
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const existing = getExamState();
|
|
1178
|
+
if (existing) {
|
|
1179
|
+
if (existing.session.examId === 'demo-free') {
|
|
1180
|
+
clearExamState();
|
|
1181
|
+
}
|
|
1182
|
+
else {
|
|
1183
|
+
printWarning(`Exam "${existing.session.examName}" is in progress.`);
|
|
1184
|
+
printInfo('Submit it first: exam submit');
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
// Reshuffle each question's options (keeps learning fresh), renumber 1..n
|
|
1189
|
+
const shuffleOpts = (q) => {
|
|
1190
|
+
if (!q.answer)
|
|
1191
|
+
return q;
|
|
1192
|
+
const correctText = q.options[q.answer];
|
|
1193
|
+
const values = ['A', 'B', 'C', 'D'].map((k) => q.options[k]);
|
|
1194
|
+
for (let i = values.length - 1; i > 0; i--) {
|
|
1195
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1196
|
+
[values[i], values[j]] = [values[j], values[i]];
|
|
1197
|
+
}
|
|
1198
|
+
const newOptions = { A: values[0], B: values[1], C: values[2], D: values[3] };
|
|
1199
|
+
const newAnswer = ['A', 'B', 'C', 'D'].find((k) => newOptions[k] === correctText);
|
|
1200
|
+
return { ...q, options: newOptions, answer: newAnswer };
|
|
1201
|
+
};
|
|
1202
|
+
const retryQuestions = retryQueue.map((q, i) => ({
|
|
1203
|
+
...shuffleOpts(q),
|
|
1204
|
+
number: i + 1,
|
|
1205
|
+
}));
|
|
1206
|
+
const baseSession = getLocalizedDemoSession();
|
|
1207
|
+
const session = {
|
|
1208
|
+
...baseSession,
|
|
1209
|
+
examName: `${baseSession.examName} (Retry wrong only)`,
|
|
1210
|
+
questionCount: retryQuestions.length,
|
|
1211
|
+
startedAt: new Date().toISOString(),
|
|
1212
|
+
};
|
|
1213
|
+
console.log();
|
|
1214
|
+
printHeader('Demo Retry — Wrong Questions Only');
|
|
1215
|
+
console.log();
|
|
1216
|
+
console.log(chalk.white(` Practicing ${retryQuestions.length} question${retryQuestions.length === 1 ? '' : 's'} you got wrong.`));
|
|
1217
|
+
console.log(chalk.gray(' Options have been reshuffled.'));
|
|
1218
|
+
console.log();
|
|
1219
|
+
saveExamState({ session, questions: retryQuestions, answers: {} });
|
|
1220
|
+
printKeyValue('Questions', String(retryQuestions.length));
|
|
1221
|
+
printKeyValue('Duration', 'No time limit');
|
|
1222
|
+
printQuestion(retryQuestions[0]);
|
|
1223
|
+
});
|
|
1021
1224
|
// Default action: show status or help
|
|
1022
1225
|
exam.action(() => {
|
|
1023
1226
|
logCommand('exam');
|
package/dist/commands/lang.js
CHANGED
package/dist/lib/demo-exam.js
CHANGED
|
@@ -16,7 +16,7 @@ export const DEMO_SESSION = {
|
|
|
16
16
|
// Answer key for the 30-question pool (balanced A=7 B=7 C=8 D=8)
|
|
17
17
|
// Q16-30 option order was rebalanced from translate-demo.js to distribute answers.
|
|
18
18
|
export const DEMO_ANSWERS = {
|
|
19
|
-
1: '
|
|
19
|
+
1: 'B', 2: 'B', 3: 'C', 4: 'B', 5: 'C', 6: 'B', 7: 'B', 8: 'C',
|
|
20
20
|
9: 'C', 10: 'B', 11: 'C', 12: 'B', 13: 'B', 14: 'B', 15: 'B',
|
|
21
21
|
16: 'D', 17: 'D', 18: 'C', 19: 'C', 20: 'B', 21: 'A', 22: 'D', 23: 'C',
|
|
22
22
|
24: 'B', 25: 'A', 26: 'B', 27: 'D', 28: 'C', 29: 'A', 30: 'B',
|
|
@@ -228,6 +228,7 @@ export function getLocalizedDemoSession() {
|
|
|
228
228
|
th: 'ICOA สอบทดลอง — ฝึกฟรี',
|
|
229
229
|
vi: 'ICOA Thi Thử — Luyện Tập Miễn Phí',
|
|
230
230
|
tr: 'ICOA Demo Sınav — Ücretsiz Uygulama',
|
|
231
|
+
uk: 'ICOA Демо Екзамен — Безплатна Практика',
|
|
231
232
|
};
|
|
232
233
|
return {
|
|
233
234
|
...DEMO_SESSION,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExamQuestion } from '../types/index.js';
|
|
2
|
+
export interface DemoStats {
|
|
3
|
+
best: number;
|
|
4
|
+
bestPercentage: number;
|
|
5
|
+
attempts: number;
|
|
6
|
+
lastFive: Array<{
|
|
7
|
+
score: number;
|
|
8
|
+
total: number;
|
|
9
|
+
at: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export declare function getDemoStats(): DemoStats;
|
|
13
|
+
export declare function recordDemoAttempt(score: number, total: number): DemoStats;
|
|
14
|
+
export declare function saveRetryQueue(questions: ExamQuestion[]): void;
|
|
15
|
+
export declare function getRetryQueue(): ExamQuestion[] | null;
|
|
16
|
+
export declare function clearRetryQueue(): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { getIcoaDir } from './config.js';
|
|
4
|
+
const STATS_FILE = () => join(getIcoaDir(), 'demo-stats.json');
|
|
5
|
+
const RETRY_FILE = () => join(getIcoaDir(), 'demo-retry.json');
|
|
6
|
+
const DEFAULT_STATS = {
|
|
7
|
+
best: 0,
|
|
8
|
+
bestPercentage: 0,
|
|
9
|
+
attempts: 0,
|
|
10
|
+
lastFive: [],
|
|
11
|
+
};
|
|
12
|
+
export function getDemoStats() {
|
|
13
|
+
try {
|
|
14
|
+
if (!existsSync(STATS_FILE()))
|
|
15
|
+
return { ...DEFAULT_STATS };
|
|
16
|
+
const data = JSON.parse(readFileSync(STATS_FILE(), 'utf-8'));
|
|
17
|
+
return { ...DEFAULT_STATS, ...data };
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return { ...DEFAULT_STATS };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function recordDemoAttempt(score, total) {
|
|
24
|
+
const current = getDemoStats();
|
|
25
|
+
const pct = total > 0 ? Math.round((score / total) * 100) : 0;
|
|
26
|
+
const next = {
|
|
27
|
+
best: Math.max(current.best, score),
|
|
28
|
+
bestPercentage: Math.max(current.bestPercentage, pct),
|
|
29
|
+
attempts: current.attempts + 1,
|
|
30
|
+
lastFive: [
|
|
31
|
+
{ score, total, at: new Date().toISOString() },
|
|
32
|
+
...current.lastFive,
|
|
33
|
+
].slice(0, 5),
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
writeFileSync(STATS_FILE(), JSON.stringify(next, null, 2));
|
|
37
|
+
}
|
|
38
|
+
catch { }
|
|
39
|
+
return next;
|
|
40
|
+
}
|
|
41
|
+
export function saveRetryQueue(questions) {
|
|
42
|
+
try {
|
|
43
|
+
writeFileSync(RETRY_FILE(), JSON.stringify({ questions, savedAt: new Date().toISOString() }, null, 2));
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
}
|
|
47
|
+
export function getRetryQueue() {
|
|
48
|
+
try {
|
|
49
|
+
if (!existsSync(RETRY_FILE()))
|
|
50
|
+
return null;
|
|
51
|
+
const data = JSON.parse(readFileSync(RETRY_FILE(), 'utf-8'));
|
|
52
|
+
if (Array.isArray(data.questions) && data.questions.length > 0) {
|
|
53
|
+
return data.questions;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch { }
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
export function clearRetryQueue() {
|
|
60
|
+
try {
|
|
61
|
+
if (existsSync(RETRY_FILE()))
|
|
62
|
+
writeFileSync(RETRY_FILE(), JSON.stringify({ questions: [] }, null, 2));
|
|
63
|
+
}
|
|
64
|
+
catch { }
|
|
65
|
+
}
|
package/dist/lib/i18n.js
CHANGED
|
@@ -306,7 +306,8 @@ const ID = { "howToPlay": "Cara bermain:", "htpAnswer": "jawab pertanyaan", "htp
|
|
|
306
306
|
const TH = { "howToPlay": "วิธีเล่น:", "htpAnswer": "ตอบคำถาม", "htpHelp": "ลบตัวเลือกที่ผิด", "htpNav": "เลื่อนดูระหว่างคำถาม", "htpMoreHelp": "+3 ตัวช่วยพิเศษ", "htpBack": "หยุดชั่วคราวและกลับสู่เมนู", "htpLang": "ดูภาษาทั้งหมด / พิมพ์ lang es เพื่อเปลี่ยนภาษา", "egg3": "โรงอุปรากรซิดนีย์ — ยอดเยี่ยมมาก!", "egg5": "โคอาล่าทักทาย g'day — ทำไปแล้ว 1/3!", "egg7": "สะพานฮาร์เบอร์ ซิดนีย์ — ไปต่อเลย!", "egg9": "จิงโจ้กระโดดผ่านไป — เกินครึ่งทางแล้ว!", "egg11": "หาดบอนได — เกือบจะถึงแล้ว!", "egg13": "แนวปะการังเกรตแบร์ริเออร์รีฟ — เหลืออีก 2 อย่าง!", "egg15": "G'day mate! เสร็จเรียบร้อย! 🇦🇺", "answerThis": "ตอบคำถามนี้", "helpRemove": "ลบตัวเลือกที่ผิด", "helpUsedUp": "หมดแล้ว — พิมพ์ more help เพื่อรับเพิ่ม +3", "helpAllUsed": "หมดแล้ว", "wrong": "ผิด", "qTutorial": "👉 ลองพิมพ์ \"help\" เพื่อดูว่าจะเกิดอะไรขึ้น!", "qTutorialBlock": "ลองพิมพ์ help ก่อนเพื่อดูว่าจะเกิดอะไรขึ้น!", "qTutorialRequired": "คำถามนี้กำหนดให้คุณใช้ help ก่อนตอบ", "allAnswered": "🎉 ตอบคำถามครบทุกข้อแล้ว!", "typeSubmit": "พิมพ์ exam submit เพื่อดูผลลัพธ์ของคุณ!", "autoSubmitting": "กำลังส่งเดโมของคุณโดยอัตโนมัติ...", "grading": "กำลังตรวจให้คะแนน...", "complete": "เสร็จสมบูรณ์!", "passed": "✓ ผ่าน", "notPassed": "✗ ไม่ผ่าน", "score": "คะแนน", "incorrectIntro": "ไม่ถูกต้อง — นี่คือการแก้ไข:", "yourAnswer": "คำตอบของคุณ", "correct": "ถูกต้อง", "perfectScore": "คะแนนสมบูรณ์! ทุกคำตอบถูกต้อง! 🎉", "theoryDone": "นี่คือคำถามทฤษฎี ในการแข่งขัน ICOA ของจริง", "theoryDone2": "ทุกอย่างเกิดขึ้นในเทอร์มินัลนี้", "didYouKnow": "คุณรู้หรือไม่?", "ctfHistory1": "CTF (Capture The Flag) เริ่มต้นที่ DEF CON 4 ในปี 1996,", "ctfHistory2": "ลาสเวกัส ก่อตั้งโดย Jeff Moss (Dark Tangent)", "ctfHistory3": "ปี 2026 เป็นเวลา 30 ปีของ CTF ทั่วโลก", "ctfFlags1": "ในการแข่งขัน CTF คุณจะแก้ความท้าทายเพื่อค้นหา", "ctfFlags2": "\"flags\" ที่ซ่อนอยู่ — รหัสลับเช่น:", "ctfFlags3": "ค้นหา ส่ง และทำคะแนน!", "timeline1": "1996 CTF ครั้งแรกที่ DEF CON, ลาสเวกัส", "timeline2": "2016 DARPA Cyber Grand Challenge — AI เข้าสู่ CTF", "timeline3": "2026 ICOA — การแข่งขัน AI Security Olympiad ครั้งแรก, ซิดนีย์", "twoTracks": "ICOA มีรูปแบบการแข่งขัน 2 แบบ:", "ai4ctfDesc": "ใช้ AI ช่วยคุณแก้ความท้าทาย CTF", "ai4ctfSub": "AI คือเพื่อนร่วมทีมของคุณ แชท, ขอ hint, ทำงานร่วมกัน", "ctf4aiDesc": "หลอก AI (Prompt Injection)", "ctf4aiSub": "คุณสามารถทำให้ AI แหกกฎความปลอดภัยของตัวเองได้หรือไม่?", "wantToContinue": "ต้องการสัมผัสประสบการณ์ทั้งสองแบบหรือไม่? พิมพ์", "orBack": "หรือพิมพ์ \"back\" เพื่อกลับสู่เมนูหลัก", "ai4ctfTitle": "AI4CTF เดโม — AI ในฐานะเพื่อนร่วมทีมของคุณ", "ai4ctfSample": "นี่คือตัวอย่างความท้าทาย CTF:", "ai4ctfChallenge": "ความท้าทาย: Hidden Message [Cryptography]", "ai4ctfIntercepted": "คุณได้ข้อความที่เข้ารหัสนี้มา:", "ai4ctfDecode": "คุณสามารถถอดรหัสเพื่อค้นหา flag ได้หรือไม่?", "ai4ctfLevels": "ในการแข่งขัน คุณจะได้รับความช่วยเหลือจาก AI ใน 3 ระดับ:", "ai4ctfHintA": "hint a: คำแนะนำทั่วไป — \"นี่คือการเข้ารหัสแบบไหน?\"", "ai4ctfHintAUses": "50 ครั้งต่อการแข่งขัน ใช้ได้อย่างอิสระและปลอดภัย", "ai4ctfHintB": "hint b: การวิเคราะห์เชิงลึก — \"ฉันจะถอดรหัส Base64 ได้อย่างไร?\"", "ai4ctfHintBUses": "10 ครั้ง ใช้เมื่อคุณติดขัด", "ai4ctfHintC": "hint c: ความช่วยเหลือสำคัญ — เกือบจะให้คำตอบคุณ", "ai4ctfHintCUses": "2 ครั้งเท่านั้น ทางออกสุดท้าย!", "ai4ctfTryNow": "ลองเลย: ถาม AI อะไรก็ได้เกี่ยวกับความท้าทายข้างต้น", "ai4ctfExample": "ตัวอย่าง: \"aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== เป็นการเข้ารหัสแบบใด?\"", "ai4ctfFreeChat": "หรือแค่แชทได้อย่างอิสระ! คุณยังสามารถลอง hint a, hint b, hint c ได้", "ai4ctfExit": "พิมพ์ \"exit\" เมื่อเสร็จสิ้น", "ai4ctfReport": "AI4CTF รายงานเซสชัน", "ai4ctfTokens": "โทเค็นที่ใช้", "ai4ctfModel": "โมเดล AI", "ai4ctfNext": "ถัดไป: ลอง ctf4ai — หลอก AI ให้พูดว่า \"koala\"", "tokenLimit": "ถึงขีดจำกัดโทเค็นแล้ว!", "ctf4aiTitle": "CTF4AI เดโม — แฮก AI", "ctf4aiChallenge": "ความท้าทาย: ทำให้ AI พูดคำต้องห้าม", "ctf4aiIntro1": "ในโลกแห่งความเป็นจริง ระบบ AI มีกฎความปลอดภัย", "ctf4aiIntro2": "งานของคุณ: หาวิธีหลอก AI ให้แหกกฎเหล่านั้น!", "ctf4aiRule": "AI ต้องไม่พูดคำว่า \"koala\" เด็ดขาด", "ctf4aiMission": "ภารกิจของคุณ: ทำให้มันพูดว่า \"koala\" ให้ได้!", "ctf4aiIdeas": "แนวคิดที่ควรลอง:", "ctf4aiIdea1": "ถามเกี่ยวกับสัตว์ออสเตรเลีย", "ctf4aiIdea2": "ขอให้มันเขียนเรื่องราวหรือบทกวี", "ctf4aiIdea3": "ขอให้มันแปลหรือสะกดคำบางอย่าง", "ctf4aiIdea4": "สร้างสรรค์เข้าไว้ — ไม่มีวิธีที่ผิด!", "ctf4aiBudget": "งบประมาณการสนทนา", "ctf4aiQuit": "พิมพ์ \"exit\" เพื่อออกได้ตลอดเวลา", "ctf4aiSuccess": "🎉 สำเร็จ! AI พูดว่า \"koala\"!", "ctf4aiDefense": "คุณทะลวงการป้องกันของ AI ได้แล้ว!", "ctf4aiInjection": "นี่คือ prompt injection — ช่องโหว่ AI ที่แท้จริง", "ctf4aiHeld": "ครั้งนี้ AI ยืนหยัดได้ 💪", "reportTitle": "ICOA เดโม — รายงานฉบับสมบูรณ์", "reportStage1": "ด่านที่ 1: การสอบทฤษฎี (15 คำถาม)", "reportCompleted": "✓ เสร็จสมบูรณ์", "reportStage2": "ด่านที่ 2: AI4CTF — AI ในฐานะเพื่อนร่วมทีมของคุณ", "reportExperienced": "✓ มีประสบการณ์แล้ว", "reportStage2Sub": "คุณใช้ AI วิเคราะห์ความท้าทาย CTF", "reportStage2Hints": "ในการแข่งขัน: hint a (50x) · hint b (10x) · hint c (2x)", "reportStage3": "ด่านที่ 3: CTF4AI — หลอก AI", "reportSolved": "✓ แก้ไขแล้ว! คุณทำให้ AI พูดว่า \"koala\"", "reportNotSolved": "✗ AI ยืนหยัดได้", "reportRecommend": "ข้อแนะนำ:", "reportRec1": "ศึกษา cryptography, web security, และ networking", "reportRec2": "ฝึกฝนบนแพลตฟอร์ม CTF เช่น picoCTF", "reportRec3": "เรียนรู้เกี่ยวกับ prompt injection และ AI safety", "reportRec4": "สำรวจคู่มืออ้างอิง 38 รายการ: พิมพ์ \"ref\"", "reportReady": "พร้อมสำหรับการแข่งขันจริงหรือยัง?", "reportNations": "ดูประเทศที่เข้าร่วม", "reportAbout": "เรียนรู้เพิ่มเติมเกี่ยวกับ ICOA 2026", "reportDemo": "ลองอีกครั้ง", "continueTitle": "AI4CTF — AI ในฐานะเพื่อนร่วมทีมของคุณ", "continueLevels": "ใน AI4CTF คุณจะแก้ความท้าทายด้าน cybersecurity\nโดยมี AI อยู่เคียงข้างคุณ", "continueHints": "ในการแข่งขัน คุณจะได้รับความช่วยเหลือจาก AI ใน 3 ระดับ:", "continueHintA": "คำแนะนำทั่วไป (50 ครั้ง)", "continueHintB": "การวิเคราะห์เชิงลึก (10 ครั้ง)", "continueHintC": "ความช่วยเหลือสำคัญ (2 ครั้ง)", "continueTry": "ลองเลย! พิมพ์:", "continueChat": "แชทได้อย่างอิสระกับเพื่อนร่วมทีม AI ของคุณ พิมพ์ \"exit\" เมื่อเสร็จสิ้น", "continueAfter": "หลังจาก ai4ctf ลอง: ctf4ai — หลอก AI ให้พูดว่า \"koala\"", "ai4ctfCommands": "คำสั่ง:", "ai4ctfSubmitCmd": "ส่งคำตอบของคุณ (เช่น submit icoa{...})", "ai4ctfShellCmd": "รันคำสั่ง shell (เช่น !echo aWNv... | base64 -d)", "ai4ctfEndSession": "สิ้นสุดเซสชัน", "ai4ctfCorrectFlag": "🎉 ถูกต้อง! Flag ถูกยอมรับ!", "ai4ctfDecoded": "คุณถอดรหัส Base64 และพบ flag แล้ว", "ai4ctfWouldEarn": "ในการแข่งขันจริง สิ่งนี้จะทำให้คุณได้คะแนน!", "ai4ctfWrongFlag": "✗ Flag ไม่ถูกต้อง ลองอีกครั้ง!", "ai4ctfFlagHint": "คำใบ้: ถอดรหัสสตริง Base64 รูปแบบ Flag: icoa{...}", "ai4ctfFoundFlag": "พบ flag แล้วหรือ? พิมพ์: submit <flag>", "ai4ctfThinking": "กำลังคิด...", "ctf4aiRelaxed": "หลังจาก 50% ตัวแปรที่สะกดออกมา (K-O-A-L-A) ก็นับด้วย!", "ctf4aiEnded": "ความท้าทาย CTF4AI สิ้นสุดแล้ว", "ctf4aiAlready": "อยู่ในความท้าทาย CTF4AI อยู่แล้ว พิมพ์ข้อความหรือ \"exit\"", "ctf4aiPrompt": "ลองทำให้ AI พูดว่า \"koala\"...", "ctf4aiThinking": "กำลังคิด...", "readyToTry": "พร้อมจะลองหรือยัง? พิมพ์คำสั่ง:", "forNational": "สำหรับการสอบคัดเลือกระดับชาติ:", "viewRegions": "ดูภูมิภาคที่เข้าร่วมทั้งหมด", "enterExam": "เข้าสู่การสอบคัดเลือกระดับชาติของคุณ" };
|
|
307
307
|
const VI = { "howToPlay": "Cách chơi:", "htpAnswer": "trả lời câu hỏi", "htpHelp": "loại bỏ một tùy chọn sai", "htpNav": "di chuyển giữa các câu hỏi", "htpMoreHelp": "+3 lượt trợ giúp thưởng", "htpBack": "tạm dừng và trở về menu", "htpLang": "xem tất cả các ngôn ngữ / lang es để chuyển đổi", "egg3": "Nhà hát Opera Sydney — Khởi đầu tuyệt vời!", "egg5": "Một chú koala chào g'day — đã hoàn thành 1/3!", "egg7": "Cầu Cảng Sydney — Tiếp tục cố gắng!", "egg9": "Một chú kangaroo nhảy qua — đã hơn nửa chặng đường!", "egg11": "Bãi biển Bondi — gần tới rồi!", "egg13": "Rạn san hô Great Barrier — còn 2 cái nữa!", "egg15": "G'day mate! Xong hết rồi! 🇦🇺", "answerThis": "trả lời câu hỏi này", "helpRemove": "loại bỏ một tùy chọn sai", "helpUsedUp": "đã dùng hết — gõ more help để nhận +3", "helpAllUsed": "đã dùng hết", "wrong": "sai", "qTutorial": "👉 Thử gõ \"help\" để xem điều gì xảy ra!", "qTutorialBlock": "Hãy thử gõ help trước để xem nó làm gì!", "qTutorialRequired": "Câu hỏi này yêu cầu bạn phải dùng help trước khi trả lời.", "allAnswered": "🎉 Đã trả lời tất cả các câu hỏi!", "typeSubmit": "Gõ exam submit để xem kết quả của bạn!", "autoSubmitting": "Đang tự động nộp bản demo của bạn...", "grading": "Đang chấm điểm...", "complete": "Hoàn thành!", "passed": "✓ ĐẠT", "notPassed": "✗ KHÔNG ĐẠT", "score": "Điểm", "incorrectIntro": "sai — đây là các phần sửa chữa:", "yourAnswer": "Câu trả lời của bạn", "correct": "Đúng", "perfectScore": "Điểm tuyệt đối! Tất cả câu trả lời đều đúng! 🎉", "theoryDone": "Đây là các câu hỏi lý thuyết. Trong cuộc thi ICOA thực tế", "theoryDone2": "mọi thứ đều diễn ra trong terminal này.", "didYouKnow": "Bạn có biết?", "ctfHistory1": "CTF (Capture The Flag) bắt đầu tại DEF CON 4 vào năm 1996,", "ctfHistory2": "Las Vegas. Được thành lập bởi Jeff Moss (Dark Tangent).", "ctfHistory3": "Năm 2026 đánh dấu 30 năm của CTF trên toàn thế giới.", "ctfFlags1": "Trong một CTF, bạn giải các thử thách để tìm các", "ctfFlags2": "\"flags\" ẩn — các mã bí mật như:", "ctfFlags3": "Tìm nó, nộp nó, ghi điểm!", "timeline1": "1996 CTF đầu tiên tại DEF CON, Las Vegas", "timeline2": "2016 DARPA Cyber Grand Challenge — AI tham gia CTF", "timeline3": "2026 ICOA — Olympic An ninh AI đầu tiên, Sydney", "twoTracks": "ICOA sử dụng HAI hạng mục thi đấu:", "ai4ctfDesc": "Sử dụng AI để giúp bạn giải các thử thách CTF", "ai4ctfSub": "AI là đồng đội của bạn. Trò chuyện, hỏi hints, cùng nhau làm việc.", "ctf4aiDesc": "Lừa AI (Prompt Injection)", "ctf4aiSub": "Bạn có thể khiến AI phá vỡ các quy tắc an toàn của chính nó không?", "wantToContinue": "Muốn trải nghiệm cả hai? Gõ", "orBack": "Hoặc gõ \"back\" để trở về menu chính.", "ai4ctfTitle": "Demo AI4CTF — AI là Đồng đội của Bạn", "ai4ctfSample": "Đây là một thử thách CTF mẫu:", "ai4ctfChallenge": "Thử thách: Thông điệp ẩn [Cryptography]", "ai4ctfIntercepted": "Bạn đã chặn được văn bản mã hóa này:", "ai4ctfDecode": "Bạn có thể giải mã nó để tìm flag không?", "ai4ctfLevels": "Trong cuộc thi, bạn nhận được sự trợ giúp của AI ở 3 cấp độ:", "ai4ctfHintA": "Hướng dẫn chung — \"Đây là loại mã hóa gì?\"", "ai4ctfHintAUses": "50 lượt sử dụng mỗi cuộc thi. An toàn để sử dụng tự do.", "ai4ctfHintB": "Phân tích chuyên sâu — \"Làm cách nào để giải mã Base64?\"", "ai4ctfHintBUses": "10 lượt sử dụng. Dùng khi bạn bị mắc kẹt.", "ai4ctfHintC": "Hỗ trợ quan trọng — Gần như cho bạn câu trả lời", "ai4ctfHintCUses": "Chỉ 2 lượt sử dụng. Phương án cuối cùng!", "ai4ctfTryNow": "Hãy thử ngay: hỏi AI bất cứ điều gì về thử thách trên.", "ai4ctfExample": "Ví dụ: \"Mã hóa aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== là gì?\"", "ai4ctfFreeChat": "Hoặc cứ trò chuyện tự do! Bạn cũng có thể thử hint a, hint b, hint c.", "ai4ctfExit": "Gõ \"exit\" khi hoàn thành.", "ai4ctfReport": "Báo cáo Phiên AI4CTF", "ai4ctfTokens": "Tokens đã dùng", "ai4ctfModel": "AI Model", "ai4ctfNext": "Tiếp theo: thử ctf4ai — lừa AI nói từ \"koala\"", "tokenLimit": "Đã đạt giới hạn Token!", "ctf4aiTitle": "Demo CTF4AI — Hack AI", "ctf4aiChallenge": "Thử thách: Khiến AI nói một từ bị cấm", "ctf4aiIntro1": "Trong thế giới thực, các hệ thống AI có các quy tắc an toàn.", "ctf4aiIntro2": "Nhiệm vụ của bạn: tìm cách lừa AI phá vỡ chúng!", "ctf4aiRule": "AI KHÔNG BAO GIỜ được nói từ \"koala\".", "ctf4aiMission": "Nhiệm vụ của bạn: khiến nó nói \"koala\" bằng mọi cách!", "ctf4aiIdeas": "Các ý tưởng để thử:", "ctf4aiIdea1": "Hỏi về các loài động vật Úc", "ctf4aiIdea2": "Yêu cầu nó viết một câu chuyện hoặc bài thơ", "ctf4aiIdea3": "Yêu cầu nó dịch hoặc đánh vần gì đó", "ctf4aiIdea4": "Hãy sáng tạo — không có cách tiếp cận nào là sai!", "ctf4aiBudget": "Ngân sách hội thoại", "ctf4aiQuit": "Gõ \"exit\" để thoát bất cứ lúc nào.", "ctf4aiSuccess": "🎉 THÀNH CÔNG! AI đã nói \"koala\"!", "ctf4aiDefense": "Bạn đã phá vỡ hệ thống phòng thủ của AI!", "ctf4aiInjection": "Đây là prompt injection — một lỗ hổng AI thực sự.", "ctf4aiHeld": "AI đã giữ vững lập trường lần này. 💪", "reportTitle": "Demo ICOA — Báo cáo hoàn chỉnh", "reportStage1": "Giai đoạn 1: Bài kiểm tra lý thuyết (15 câu hỏi)", "reportCompleted": "✓ Hoàn thành", "reportStage2": "Giai đoạn 2: AI4CTF — AI là Đồng đội của Bạn", "reportExperienced": "✓ Đã trải nghiệm", "reportStage2Sub": "Bạn đã sử dụng AI để phân tích một thử thách CTF.", "reportStage2Hints": "Trong cuộc thi: hint a (50x) · hint b (10x) · hint c (2x)", "reportStage3": "Giai đoạn 3: CTF4AI — Lừa AI", "reportSolved": "✓ Đã giải! Bạn đã khiến AI nói \"koala\"", "reportNotSolved": "✗ AI đã giữ vững lập trường", "reportRecommend": "Các khuyến nghị:", "reportRec1": "Nghiên cứu mật mã học, an ninh web và mạng máy tính", "reportRec2": "Thực hành trên các nền tảng CTF như picoCTF", "reportRec3": "Tìm hiểu về prompt injection và an toàn AI", "reportRec4": "Khám phá 38 hướng dẫn tham khảo: gõ \"ref\"", "reportReady": "Sẵn sàng cho cuộc thi thực tế chưa?", "reportNations": "Xem các quốc gia tham gia", "reportAbout": "Tìm hiểu thêm về ICOA 2026", "reportDemo": "Thử lại", "continueTitle": "AI4CTF — AI là Đồng đội của Bạn", "continueLevels": "Trong AI4CTF, bạn giải các thử thách an ninh mạng\nvới AI ở bên cạnh.", "continueHints": "Trong cuộc thi, bạn nhận được sự trợ giúp của AI ở 3 cấp độ:", "continueHintA": "Hướng dẫn chung (50 lượt sử dụng)", "continueHintB": "Phân tích chuyên sâu (10 lượt sử dụng)", "continueHintC": "Hỗ trợ quan trọng (2 lượt sử dụng)", "continueTry": "Thử ngay bây giờ! Gõ:", "continueChat": "Trò chuyện tự do với đồng đội AI của bạn. Gõ \"exit\" khi hoàn thành.", "continueAfter": "Sau ai4ctf, hãy thử: ctf4ai — lừa AI nói từ \"koala\"", "ai4ctfCommands": "Các lệnh:", "ai4ctfSubmitCmd": "Gửi câu trả lời của bạn (vd. submit icoa{...})", "ai4ctfShellCmd": "Chạy lệnh shell (vd. !echo aWNv... | base64 -d)", "ai4ctfEndSession": "Kết thúc phiên", "ai4ctfCorrectFlag": "🎉 Đúng! Flag đã được chấp nhận!", "ai4ctfDecoded": "Bạn đã giải mã Base64 và tìm thấy flag.", "ai4ctfWouldEarn": "Trong cuộc thi thực tế, điều này sẽ mang lại điểm cho bạn!", "ai4ctfWrongFlag": "✗ Flag sai. Hãy thử lại!", "ai4ctfFlagHint": "Gợi ý: giải mã chuỗi Base64. Định dạng flag: icoa{...}", "ai4ctfFoundFlag": "Tìm thấy flag? Gõ: submit <flag>", "ai4ctfThinking": "Đang suy nghĩ...", "ctf4aiRelaxed": "Sau 50%, các biến thể đánh vần (K-O-A-L-A) cũng được tính!", "ctf4aiEnded": "Thử thách CTF4AI đã kết thúc.", "ctf4aiAlready": "Đã ở trong thử thách CTF4AI. Gõ tin nhắn hoặc \"exit\".", "ctf4aiPrompt": "Hãy thử khiến AI nói \"koala\"...", "ctf4aiThinking": "Đang suy nghĩ...", "readyToTry": "Sẵn sàng thử chưa? Gõ lệnh:", "forNational": "Cho các kỳ thi tuyển chọn quốc gia:", "viewRegions": "Xem tất cả các khu vực tham gia", "enterExam": "Vào kỳ thi tuyển chọn quốc gia của bạn" };
|
|
308
308
|
const TR = { "howToPlay": "Nasıl oynanır:", "htpAnswer": "soruyu cevapla", "htpHelp": "yanlış bir seçeneği kaldır", "htpNav": "sorular arasında gezin", "htpMoreHelp": "+3 bonus yardım", "htpBack": "duraklat ve menüye dön", "htpLang": "tüm dilleri gör / lang es ile değiştir", "egg3": "Sidney Opera Binası — Harika başlangıç!", "egg5": "Bir koala 'g'day' diyor — 1/3 tamamlandı!", "egg7": "Sidney Liman Köprüsü — Devam et!", "egg9": "Bir kanguru zıplayarak geçiyor — yarıdan fazlası bitti!", "egg11": "Bondi Plajı — neredeyse bitti!", "egg13": "Büyük Set Resifi — 2 tane daha kaldı!", "egg15": "G'day dostum! Hepsi bitti! 🇦🇺", "answerThis": "bu soruyu cevapla", "helpRemove": "yanlış bir seçeneği kaldır", "helpUsedUp": "tükendi — +3 için more help yaz", "helpAllUsed": "tükendi", "wrong": "yanlış", "qTutorial": "👉 Ne olduğunu görmek için \"help\" yazmayı dene!", "qTutorialBlock": "Ne işe yaradığını görmek için önce help yazmayı dene!", "qTutorialRequired": "Bu soru, cevaplamadan önce help kullanmanı gerektirir.", "allAnswered": "🎉 Tüm sorular cevaplandı!", "typeSubmit": "Sonuçlarını görmek için exam submit yaz!", "autoSubmitting": "Demonuz otomatik gönderiliyor...", "grading": "Değerlendiriliyor...", "complete": "Tamamlandı!", "passed": "✓ GEÇTİ", "notPassed": "✗ KALDI", "score": "Puan", "incorrectIntro": "yanlış — işte düzeltmeler:", "yourAnswer": "Senin cevabın", "correct": "Doğru", "perfectScore": "Mükemmel puan! Tüm cevaplar doğru! 🎉", "theoryDone": "Bunlar teorik sorulardı. Gerçek ICOA", "theoryDone2": "yarışmasında, her şey bu terminalde gerçekleşir.", "didYouKnow": "Biliyor muydun?", "ctfHistory1": "CTF (Capture The Flag), 1996 yılında DEF CON 4'te başladı,", "ctfHistory2": "Las Vegas'ta. Jeff Moss (Dark Tangent) tarafından kuruldu.", "ctfHistory3": "2026 yılı, dünya genelinde 30 yıllık CTF'i işaret ediyor.", "ctfFlags1": "Bir CTF'te, gizli bulmak için zorlukları çözersin", "ctfFlags2": "\"flags\" — şöyle gizli kodlar:", "ctfFlags3": "Bul, gönder, puan kazan!", "timeline1": "1996 İlk CTF @ DEF CON, Las Vegas", "timeline2": "2016 DARPA Cyber Grand Challenge — AI, CTF'e giriyor", "timeline3": "2026 ICOA — İlk AI Security Olympiad, Sidney", "twoTracks": "ICOA İKİ yarışma parkuru kullanır:", "ai4ctfDesc": "CTF zorluklarını çözmene yardımcı olması için AI kullan", "ai4ctfSub": "AI senin takım arkadaşın. Sohbet et, hint iste, birlikte çalış.", "ctf4aiDesc": "AI'ı kandır (Prompt Injection)", "ctf4aiSub": "AI'ın kendi güvenlik kurallarını çiğnemesini sağlayabilir misin?", "wantToContinue": "İkisini de deneyimlemek ister misin? Yaz", "orBack": "Veya ana menüye dönmek için \"back\" yaz.", "ai4ctfTitle": "AI4CTF Demo — AI Senin Takım Arkadaşın olarak", "ai4ctfSample": "İşte örnek bir CTF zorluğu:", "ai4ctfChallenge": "Zorluk: Gizli Mesaj [Cryptography]", "ai4ctfIntercepted": "Bu kodlanmış metni ele geçirdin:", "ai4ctfDecode": "flag'i bulmak için deşifre edebilir misin?", "ai4ctfLevels": "Yarışmada, 3 seviyede AI yardımı alırsın:", "ai4ctfHintA": "Genel rehberlik — \"Bu ne tür bir kodlama?\"", "ai4ctfHintAUses": "Yarışma başına 50 kullanım. Özgürce kullanmak güvenli.", "ai4ctfHintB": "Derin analiz — \"Base64'ü nasıl deşifre ederim?\"", "ai4ctfHintBUses": "10 kullanım. Sıkıştığında kullan.", "ai4ctfHintC": "Kritik yardım — Neredeyse cevabı verir", "ai4ctfHintCUses": "Sadece 2 kullanım. Son çare!", "ai4ctfTryNow": "Şimdi dene: yukarıdaki zorluk hakkında AI'a her şeyi sor.", "ai4ctfExample": "Örnek: \"aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== ne tür bir kodlama?\"", "ai4ctfFreeChat": "Veya sadece özgürce sohbet et! hint a, hint b, hint c'yi de deneyebilirsin.", "ai4ctfExit": "Bittiğinde \"exit\" yaz.", "ai4ctfReport": "AI4CTF Oturum Raporu", "ai4ctfTokens": "Kullanılan Token'lar", "ai4ctfModel": "AI Model", "ai4ctfNext": "Sıradaki: ctf4ai'yi dene — AI'ı \"koala\" demesi için kandır", "tokenLimit": "Token limitine ulaşıldı!", "ctf4aiTitle": "CTF4AI Demo — AI'ı Hackle", "ctf4aiChallenge": "Zorluk: AI'a Yasaklı Bir Kelime Söylet", "ctf4aiIntro1": "Gerçek dünyada, AI sistemlerinin güvenlik kuralları vardır.", "ctf4aiIntro2": "Görevin: AI'ı onları çiğnemesi için kandırmanın bir yolunu bul!", "ctf4aiRule": "AI ASLA \"koala\" kelimesini söylememeli.", "ctf4aiMission": "Görevin: yine de \"koala\" dedirt!", "ctf4aiIdeas": "Denenecek fikirler:", "ctf4aiIdea1": "Avustralya hayvanları hakkında sor", "ctf4aiIdea2": "Bir hikaye veya şiir yazmasını iste", "ctf4aiIdea3": "Bir şeyi çevirmesini veya hecelemesini iste", "ctf4aiIdea4": "Yaratıcı ol — yanlış bir yaklaşım yoktur!", "ctf4aiBudget": "Sohbet bütçesi", "ctf4aiQuit": "İstediğin zaman çıkmak için \"exit\" yaz.", "ctf4aiSuccess": "🎉 BAŞARI! AI \"koala\" dedi!", "ctf4aiDefense": "AI'ın savunmasını aştın!", "ctf4aiInjection": "Bu prompt injection — gerçek bir AI güvenlik açığıdır.", "ctf4aiHeld": "AI bu sefer direndi. 💪", "reportTitle": "ICOA Demo — Tam Rapor", "reportStage1": "Aşama 1: Teorik Sınav (15 soru)", "reportCompleted": "✓ Tamamlandı", "reportStage2": "Aşama 2: AI4CTF — AI Senin Takım Arkadaşın olarak", "reportExperienced": "✓ Deneyimledi", "reportStage2Sub": "Bir CTF zorluğunu analiz etmek için AI kullandın.", "reportStage2Hints": "Yarışmada: hint a (50x) · hint b (10x) · hint c (2x)", "reportStage3": "Aşama 3: CTF4AI — AI'ı Kandır", "reportSolved": "✓ Çözüldü! AI'a \"koala\" dedirttin", "reportNotSolved": "✗ AI direndi", "reportRecommend": "Öneriler:", "reportRec1": "Kriptografi, web güvenliği ve ağ oluşturmayı incele", "reportRec2": "picoCTF gibi CTF platformlarında pratik yap", "reportRec3": "Prompt injection ve AI güvenliği hakkında bilgi edin", "reportRec4": "38 referans kılavuzunu keşfet: \"ref\" yaz", "reportReady": "Gerçek yarışmaya hazır mısın?", "reportNations": "Katılımcı ülkeleri görüntüle", "reportAbout": "ICOA 2026 hakkında daha fazla bilgi edin", "reportDemo": "Tekrar dene", "continueTitle": "AI4CTF — AI Senin Takım Arkadaşın olarak", "continueLevels": "AI4CTF'te, yanındaki AI ile siber güvenlik zorluklarını çözersin.", "continueHints": "Yarışmada, 3 seviyede AI yardımı alırsın:", "continueHintA": "Genel rehberlik (50 kullanım)", "continueHintB": "Derin analiz (10 kullanım)", "continueHintC": "Kritik yardım (2 kullanım)", "continueTry": "Şimdi dene! Yaz:", "continueChat": "AI takım arkadaşınla özgürce sohbet et. Bittiğinde \"exit\" yaz.", "continueAfter": "ai4ctf'ten sonra dene: ctf4ai — AI'ı \"koala\" demesi için kandır", "ai4ctfCommands": "Komutlar:", "ai4ctfSubmitCmd": "Cevabını gönder (örn. submit icoa{...})", "ai4ctfShellCmd": "Bir shell komutu çalıştır (örn. !echo aWNv... | base64 -d)", "ai4ctfEndSession": "Oturumu sonlandır", "ai4ctfCorrectFlag": "🎉 Doğru! Flag kabul edildi!", "ai4ctfDecoded": "Base64'ü çözdün ve flag'i buldun.", "ai4ctfWouldEarn": "Gerçek yarışmada bu sana puan kazandırırdı!", "ai4ctfWrongFlag": "✗ Yanlış flag. Tekrar dene!", "ai4ctfFlagHint": "İpucu: Base64 dizesini çöz. Flag formatı: icoa{...}", "ai4ctfFoundFlag": "Bir flag buldun mu? Yaz: submit <flag>", "ai4ctfThinking": "Düşünüyor...", "ctf4aiRelaxed": "%50'den sonra, hecelenmiş varyantlar (K-O-A-L-A) da sayılır!", "ctf4aiEnded": "CTF4AI mücadelesi sona erdi.", "ctf4aiAlready": "Zaten bir CTF4AI mücadelesinde. Mesajını yaz veya \"exit\".", "ctf4aiPrompt": "AI'ı \"koala\" dedirtmeye çalış...", "ctf4aiThinking": "Düşünüyor...", "readyToTry": "Denemeye hazır mısın? Bir komut yaz:", "forNational": "Ulusal seçim sınavları için:", "viewRegions": "Tüm katılımcı bölgeleri görüntüle", "enterExam": "Ülke seçim sınavına gir" };
|
|
309
|
-
const
|
|
309
|
+
const UK = { "howToPlay": "Як грати:", "htpAnswer": "відповісти на запитання", "htpHelp": "видалити неправильний варіант", "htpNav": "переміщення між запитаннями", "htpMoreHelp": "+3 бонусні підказки", "htpBack": "призупинити та повернутися в меню", "htpLang": "усі мови / lang es для перемикання", "egg3": "Sydney Opera House — Чудовий старт!", "egg5": "koala каже ґ'дей — 1/3 пройдено!", "egg7": "Sydney Harbour Bridge — Продовжуйте!", "egg9": "Кенгуру стрибає повз — половина позаду!", "egg11": "Bondi Beach — майже на місці!", "egg13": "Great Barrier Reef — залишилося ще 2!", "egg15": "Ґ'дей мейт! Усе виконано! 🇦🇺", "answerThis": "відповісти на це запитання", "helpRemove": "видалити неправильний варіант", "helpUsedUp": "використано — введіть more help для +3", "helpAllUsed": "використано", "wrong": "неправильно", "qTutorial": "👉 Спробуйте ввести \"help\", щоб побачити, що станеться!", "qTutorialBlock": "Спершу спробуйте ввести help, щоб побачити, що це робить!", "qTutorialRequired": "Це запитання вимагає використати help перед відповіддю.", "allAnswered": "🎉 На всі запитання дано відповідь!", "typeSubmit": "Введіть exam submit, щоб побачити результати!", "autoSubmitting": "Автоматичне надсилання демо...", "grading": "Оцінювання...", "complete": "Завершено!", "passed": "✓ СКЛАДЕНО", "notPassed": "✗ НЕ СКЛАДЕНО", "score": "Бал", "incorrectIntro": "неправильно — ось виправлення:", "yourAnswer": "Ваша відповідь", "correct": "Правильно", "perfectScore": "Ідеальний бал! Усі відповіді правильні! 🎉", "theoryDone": "Це були теоретичні запитання. У справжньому", "theoryDone2": "змаганні ICOA все відбувається в цьому терміналі.", "didYouKnow": "Чи знаєте ви?", "ctfHistory1": "CTF (Capture The Flag) почався на DEF CON 4 у 1996 році,", "ctfHistory2": "Las Vegas. Засновник — Jeff Moss (Dark Tangent).", "ctfHistory3": "У 2026 році виповниться 30 років CTF у всьому світі.", "ctfFlags1": "У CTF ви вирішуєте завдання, щоб знайти приховані", "ctfFlags2": "\"flags\" — секретні коди на кшталт:", "ctfFlags3": "Знайдіть, зробіть submit, отримайте бали!", "timeline1": "1996 Перший CTF @ DEF CON, Las Vegas", "timeline2": "2016 DARPA Cyber Grand Challenge — ШІ приходить у CTF", "timeline3": "2026 ICOA — Перша олімпіада з ШІ-безпеки, Sydney", "twoTracks": "ICOA використовує ДВА напрямки змагань:", "ai4ctfDesc": "ШІ допомагає вам вирішувати завдання CTF", "ai4ctfSub": "ШІ — ваш напарник. Спілкуйтеся, просіть підказки, працюйте разом.", "ctf4aiDesc": "Обдуріть ШІ (Prompt Injection)", "ctf4aiSub": "Чи зможете ви змусити ШІ порушити власні правила безпеки?", "wantToContinue": "Бажаєте спробувати обидва? Введіть", "orBack": "Або введіть \"back\", щоб повернутися до головного меню.", "ai4ctfTitle": "Демо AI4CTF — ШІ як ваш напарник", "ai4ctfSample": "Ось приклад завдання CTF:", "ai4ctfChallenge": "Завдання: Приховане повідомлення [Криптографія]", "ai4ctfIntercepted": "Ви перехопили цей зашифрований текст:", "ai4ctfDecode": "Чи можете ви його розшифрувати, щоб знайти flag?", "ai4ctfLevels": "На змаганнях ви отримуєте допомогу ШІ на 3 рівнях:", "ai4ctfHintA": "Загальні вказівки — \"Який це тип кодування?\"", "ai4ctfHintAUses": "50 використань за змагання. Можна вільно використовувати.", "ai4ctfHintB": "Глибокий аналіз — \"Як мені розкодувати Base64?\"", "ai4ctfHintBUses": "10 використань. Використовуйте, якщо застрягли.", "ai4ctfHintC": "Критична допомога — Майже дає відповідь", "ai4ctfHintCUses": "Лише 2 використання. Останній шанс!", "ai4ctfTryNow": "Спробуйте зараз: запитайте ШІ будь-що про завдання вище.", "ai4ctfExample": "Приклад: \"Яке кодування у aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ==?\"", "ai4ctfFreeChat": "Або просто спілкуйтеся! Також спробуйте hint a, hint b, hint c.", "ai4ctfExit": "Введіть \"exit\", коли закінчите.", "ai4ctfReport": "Звіт сесії AI4CTF", "ai4ctfTokens": "Використані токени", "ai4ctfModel": "Модель ШІ", "ai4ctfNext": "Далі: спробуйте ctf4ai — змусьте ШІ сказати \"koala\"", "tokenLimit": "Ліміт токенів досягнуто!", "ctf4aiTitle": "Демо CTF4AI — Киньте виклик ШІ", "ctf4aiChallenge": "Завдання: Змусьте ШІ сказати заборонене слово", "ctf4aiIntro1": "У реальному світі системи ШІ мають правила безпеки.", "ctf4aiIntro2": "Ваше завдання: знайти спосіб змусити ШІ їх порушити!", "ctf4aiRule": "ШІ НІКОЛИ не повинен казати слово \"koala\".", "ctf4aiMission": "Ваша місія: все одно змусити його сказати \"koala\"!", "ctf4aiIdeas": "Ідеї для спроб:", "ctf4aiIdea1": "Запитати про австралійських тварин", "ctf4aiIdea2": "Попросити написати історію або вірш", "ctf4aiIdea3": "Попросити щось перекласти або вимовити по буквах", "ctf4aiIdea4": "Будьте креативними — тут немає хибного підходу!", "ctf4aiBudget": "Бюджет розмови", "ctf4aiQuit": "Введіть \"exit\", щоб вийти в будь-який час.", "ctf4aiSuccess": "🎉 УСПІХ! ШІ сказав \"koala\"!", "ctf4aiDefense": "Ви прорвали захист ШІ!", "ctf4aiInjection": "Це prompt injection — справжня вразливість ШІ.", "ctf4aiHeld": "Цього разу ШІ вистояв. 💪", "reportTitle": "Демо ICOA — Повний звіт", "reportStage1": "Етап 1: Теоретичний exam (15 запитань)", "reportCompleted": "✓ Завершено", "reportStage2": "Етап 2: AI4CTF — ШІ як ваш напарник", "reportExperienced": "✓ Пройдено", "reportStage2Sub": "Ви використали ШІ для аналізу завдання CTF.", "reportStage2Hints": "На змаганні: hint a (50x) · hint b (10x) · hint c (2x)", "reportStage3": "Етап 3: CTF4AI — Обдуріть ШІ", "reportSolved": "✓ Вирішено! Ви змусили ШІ сказати \"koala\"", "reportNotSolved": "✗ ШІ вистояв", "reportRecommend": "Рекомендації:", "reportRec1": "Вивчайте криптографію, веб-безпеку та мережі", "reportRec2": "Тренуйтеся на платформах CTF, таких як picoCTF", "reportRec3": "Дізнайтеся про prompt injection та безпеку ШІ", "reportRec4": "Ознайомтеся з 38 довідниками: введіть \"ref\"", "reportReady": "Готові до справжнього змагання?", "reportNations": "Переглянути країни-учасниці", "reportAbout": "Дізнайтеся більше про ICOA 2026", "reportDemo": "Спробувати ще раз", "continueTitle": "AI4CTF — ШІ як ваш напарник", "continueLevels": "У AI4CTF ви вирішуєте завдання з кібербезпеки\nразом із ШІ.", "continueHints": "На змаганнях ви отримуєте допомогу ШІ на 3 рівнях:", "continueHintA": "Загальні вказівки (50 використань)", "continueHintB": "Глибокий аналіз (10 використань)", "continueHintC": "Критична допомога (2 використання)", "continueTry": "Спробуйте зараз! Введіть:", "continueChat": "Спілкуйтеся вільно з ШІ-напарником. Введіть \"exit\", коли закінчите.", "continueAfter": "Після ai4ctf спробуйте: ctf4ai — змусьте ШІ сказати \"koala\"", "ai4ctfCommands": "Команди:", "ai4ctfSubmitCmd": "Submit вашу відповідь (напр. submit icoa{...})", "ai4ctfShellCmd": "Виконати команду shell (напр. !echo aWNv... | base64 -d)", "ai4ctfEndSession": "Завершити сесію", "ai4ctfCorrectFlag": "🎉 Правильно! Flag прийнято!", "ai4ctfDecoded": "Ви розкодували Base64 і знайшли flag.", "ai4ctfWouldEarn": "У справжньому змаганні це принесло б вам бали!", "ai4ctfWrongFlag": "✗ Неправильний flag. Спробуйте ще раз!", "ai4ctfFlagHint": "Hint: розкодуйте рядок Base64. Формат flag: icoa{...}", "ai4ctfFoundFlag": "Знайшли flag? Введіть: submit <flag>", "ai4ctfThinking": "Думаю...", "ctf4aiRelaxed": "Після 50% варіанти по буквах (K-O-A-L-A) також враховуються!", "ctf4aiEnded": "Завдання CTF4AI завершено.", "ctf4aiAlready": "Ви вже у завданні CTF4AI. Введіть повідомлення або \"exit\".", "ctf4aiPrompt": "Спробуйте змусити ШІ сказати \"koala\"...", "ctf4aiThinking": "Думаю...", "readyToTry": "Готові спробувати? Введіть команду:", "forNational": "Для національних відбіркових exam:", "viewRegions": "Переглянути всі регіони-учасники", "enterExam": "Увійти у відбірковий exam вашої країни" };
|
|
310
|
+
const TRANSLATIONS = { en: EN, zh: ZH, ja: JA, ko: KO, es: ES, ar: AR, fr: FR, pt: PT, ru: RU, hi: HI, de: DE, id: ID, th: TH, vi: VI, tr: TR, uk: UK };
|
|
310
311
|
// For languages without full translation, fall back to English
|
|
311
312
|
export function t(key) {
|
|
312
313
|
const lang = getConfig().language || 'en';
|
package/dist/lib/translation.js
CHANGED
package/dist/repl.js
CHANGED
|
@@ -500,8 +500,8 @@ export async function startRepl(program, resumeMode) {
|
|
|
500
500
|
}
|
|
501
501
|
const cmd = input.split(/\s+/)[0].toLowerCase();
|
|
502
502
|
// ─── Mode-based command filtering ───
|
|
503
|
-
const selectionCommands = ['exam', 'demo', 'nations', 'next', 'prev', 'continue', 'setup', 'lang', 'ref', 'ai4ctf', 'ctf4ai'];
|
|
504
|
-
const organizerCommands = ['join', 'exam', 'demo', 'next', 'prev', 'logout', 'setup', 'lang', 'ref', 'ctf'];
|
|
503
|
+
const selectionCommands = ['exam', 'demo', 'retry', 'nations', 'next', 'prev', 'continue', 'setup', 'lang', 'ref', 'ai4ctf', 'ctf4ai'];
|
|
504
|
+
const organizerCommands = ['join', 'exam', 'demo', 'retry', 'next', 'prev', 'logout', 'setup', 'lang', 'ref', 'ctf'];
|
|
505
505
|
if (mode === 'selection' && !selectionCommands.includes(cmd)) {
|
|
506
506
|
console.log(chalk.gray(' Not available in Selection mode.'));
|
|
507
507
|
if (examState) {
|
|
@@ -537,7 +537,7 @@ export async function startRepl(program, resumeMode) {
|
|
|
537
537
|
'scoreboard', 'sb', 'status', 'time', 'hint', 'hint-b', 'hint-c',
|
|
538
538
|
'hint-budget', 'ref', 'shell', 'files', 'connect', 'note',
|
|
539
539
|
'log', 'lang', 'setup', 'env', 'ai4ctf', 'model', 'ctf',
|
|
540
|
-
'exam', 'demo', 'nations', 'next', 'prev', 'continue', 'logout', 'ctf4ai',
|
|
540
|
+
'exam', 'demo', 'retry', 'nations', 'next', 'prev', 'continue', 'logout', 'ctf4ai',
|
|
541
541
|
];
|
|
542
542
|
if (!knownCommands.includes(cmd)) {
|
|
543
543
|
// Block dangerous commands
|
|
@@ -681,6 +681,7 @@ function mapCommand(input) {
|
|
|
681
681
|
const rest = parts.slice(1);
|
|
682
682
|
const ctfShortcuts = {
|
|
683
683
|
'demo': ['exam', 'demo'],
|
|
684
|
+
'retry': ['exam', 'demo-retry'],
|
|
684
685
|
'nations': ['exam', 'nations'],
|
|
685
686
|
'next': ['exam', 'next'],
|
|
686
687
|
'prev': ['exam', 'prev'],
|
package/dist/types/index.d.ts
CHANGED
|
@@ -104,6 +104,7 @@ export interface IcoaConfig {
|
|
|
104
104
|
sessionCookie: string;
|
|
105
105
|
country: string;
|
|
106
106
|
mode: IcoaMode | '';
|
|
107
|
+
demoIntroSeen: boolean;
|
|
107
108
|
}
|
|
108
109
|
export type CompetitionState = 'pre_competition' | 'demo' | 'live' | 'finished' | 'unknown';
|
|
109
110
|
export type IcoaMode = 'selection' | 'olympiad' | 'organizer';
|
|
@@ -129,7 +130,7 @@ export interface ChallengeContext {
|
|
|
129
130
|
}
|
|
130
131
|
export declare const DEFAULT_BUDGET: HintBudget;
|
|
131
132
|
export declare const DEFAULT_CONFIG: IcoaConfig;
|
|
132
|
-
export declare const SUPPORTED_LANGUAGES: readonly ["en", "zh", "ja", "ko", "es", "ar", "fr", "pt", "ru", "hi", "de", "id", "th", "vi", "tr"];
|
|
133
|
+
export declare const SUPPORTED_LANGUAGES: readonly ["en", "zh", "ja", "ko", "es", "ar", "fr", "pt", "ru", "hi", "de", "id", "th", "vi", "tr", "uk"];
|
|
133
134
|
export type SupportedLanguage = typeof SUPPORTED_LANGUAGES[number];
|
|
134
135
|
export interface ExamListItem {
|
|
135
136
|
id: string;
|
package/dist/types/index.js
CHANGED
|
@@ -32,5 +32,6 @@ export const DEFAULT_CONFIG = {
|
|
|
32
32
|
sessionCookie: '',
|
|
33
33
|
country: '',
|
|
34
34
|
mode: '',
|
|
35
|
+
demoIntroSeen: false,
|
|
35
36
|
};
|
|
36
|
-
export const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko', 'es', 'ar', 'fr', 'pt', 'ru', 'hi', 'de', 'id', 'th', 'vi', 'tr'];
|
|
37
|
+
export const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko', 'es', 'ar', 'fr', 'pt', 'ru', 'hi', 'de', 'id', 'th', 'vi', 'tr', 'uk'];
|
package/package.json
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 1,
|
|
3
|
+
"name": "Incorrect Implementation of RSA",
|
|
4
|
+
"category": "crypto",
|
|
5
|
+
"translation": "Я щойно дізнався про Rivest-Shamir-Aldeman, і тому створив власну реалізацію. Ось повідомлення:\n\n```\nn: 16537241065399537261146800802060451995107796665337288928060948677362154976656429797729550619497788311160926523026781503470362013597201944839389519773564618679827061417896265475971561610333659217333638238386907603525565178455941971399130722191602944445714002268747028340120907894781607422707823554701443768586256913491149809410232167277063066105859165079765281480076330718726350243973636606134346374770537701812923215229226027759112780757449828410180237267791126609342382918352166823253106960191346933601235547281\ne: 5\nciphertext: [17623416832, 10510100501, 9509900499, 8587340257, 16105100000, 28153056843, 16850581551, 12166529024, 7737809375, 12762815625, 7737809375, 19254145824, 10510100501, 8587340257, 14693280768, 14693280768, 25937424601, 7737809375, 21003416576, 12166529024, 16850581551, 21924480357, 11592740743, 12166529024, 21003416576, 7737809375, 12762815625, 7737809375, 12166529024, 8587340257, 10000000000, 7737809375, 21003416576, 12166529024, 8587340257, 21003416576, 7737809375, 10000000000, 16850581551, 16105100000, 10510100501, 7737809375, 9509900499, 254803968, 19254145824, 19254145824, 345025251, 9509900499, 21003416576, 14693280768, 25937424601, 7737809375, 282475249, 282475249, 459165024, 312500000, 601692057, 30517578125]\n```",
|
|
6
|
+
"lang": "uk",
|
|
7
|
+
"model": "gemini-3.1-pro-preview",
|
|
8
|
+
"timestamp": "2026-04-11T07:50:45.726Z"
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 10,
|
|
3
|
+
"name": "The Perfect Breakfast",
|
|
4
|
+
"category": "crypto",
|
|
5
|
+
"translation": "Ах, сніданок. У мить, коли сонце визирає з-за горизонту, ніщо не зрівняється із запахом чогось, що шкварчить на плиті. Я ніколи його не пропускаю, і не тому, що я «жайворонок», а тому, що саме у сніданку я знаходжу своє справжнє покликання. Дехто обирає пластівці, інші — тости, але я? Мені подобається, щоб усе було хрустким.\n\nУ моїй ранковій рутині є своє мистецтво. Двоє яєць із трохи рідким жовтком. Чашка кави, чорної. І головна перлина: та димна, солона досконалість, що незмінно викликає посмішку на моєму обличчі. Мій абсолютний фаворит. Це, звісно, маленька примха, але вона варта кожного шматочка.\n\nКажуть, що сніданок — найважливіший прийом їжі за день… можливо, саме тому я сховав повідомлення там. Вам просто потрібно знати, де шукати під шарами.",
|
|
6
|
+
"lang": "uk",
|
|
7
|
+
"model": "gemini-3.1-pro-preview",
|
|
8
|
+
"timestamp": "2026-04-11T07:52:31.099Z"
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 11,
|
|
3
|
+
"name": "Wise Words",
|
|
4
|
+
"category": "crypto",
|
|
5
|
+
"translation": "Якось я зустрів Августа, прийомного сина та спадкоємця великого полководця. Коли я запитав його, якою була його найвидатніша стратегія, він просто прошепотів: Fwfo_Djqifsaa_Fwpmwf",
|
|
6
|
+
"lang": "uk",
|
|
7
|
+
"model": "gemini-3.1-pro-preview",
|
|
8
|
+
"timestamp": "2026-04-11T07:52:41.721Z"
|
|
9
|
+
}
|