icoa-cli 2.19.100 → 2.19.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/commands/ai4ctf.js +1 -700
  2. package/dist/commands/connect.js +1 -66
  3. package/dist/commands/ctf.js +1 -620
  4. package/dist/commands/ctf4ai-demo.js +1 -525
  5. package/dist/commands/env.js +1 -738
  6. package/dist/commands/exam.js +1 -2353
  7. package/dist/commands/files.js +1 -52
  8. package/dist/commands/hint.js +1 -119
  9. package/dist/commands/lang.js +1 -155
  10. package/dist/commands/log.js +1 -165
  11. package/dist/commands/note.js +1 -40
  12. package/dist/commands/ref.js +1 -68
  13. package/dist/commands/setup.js +1 -122
  14. package/dist/commands/shell.js +1 -55
  15. package/dist/commands/theme.js +1 -50
  16. package/dist/index.js +1 -225
  17. package/dist/lib/access.js +1 -246
  18. package/dist/lib/budget.js +1 -42
  19. package/dist/lib/colors.js +1 -21
  20. package/dist/lib/config.js +1 -60
  21. package/dist/lib/ctfd-client.js +1 -274
  22. package/dist/lib/demo-exam.js +1 -249
  23. package/dist/lib/demo-flags.js +1 -27
  24. package/dist/lib/demo-stats.js +1 -65
  25. package/dist/lib/exam-client.js +1 -57
  26. package/dist/lib/exam-setup.js +1 -23
  27. package/dist/lib/exam-state.js +1 -112
  28. package/dist/lib/gemini.js +1 -235
  29. package/dist/lib/i18n.js +1 -273
  30. package/dist/lib/log-sync.js +1 -110
  31. package/dist/lib/logger.js +1 -59
  32. package/dist/lib/paper-upgrade.js +1 -117
  33. package/dist/lib/platform.js +1 -86
  34. package/dist/lib/sandbox.js +1 -93
  35. package/dist/lib/terminal.js +1 -49
  36. package/dist/lib/theme.js +1 -108
  37. package/dist/lib/translation.js +1 -66
  38. package/dist/lib/ui.js +1 -80
  39. package/dist/lib/update-check.js +1 -102
  40. package/dist/postinstall.js +1 -48
  41. package/dist/repl.js +1 -1281
  42. package/dist/types/index.d.ts +1 -1
  43. package/dist/types/index.js +1 -38
  44. package/package.json +6 -2
  45. package/translations/sw/i18n-snippet.ts +1 -0
@@ -1,700 +1 @@
1
- import chalk from 'chalk';
2
- import { createChatSession } from '../lib/gemini.js';
3
- import { addTokenUsage } from '../lib/budget.js';
4
- import { getConfig } from '../lib/config.js';
5
- import { logCommand } from '../lib/logger.js';
6
- import { printMarkdown, printError } from '../lib/ui.js';
7
- import { t } from '../lib/i18n.js';
8
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
9
- function getChallengeContext() {
10
- const config = getConfig();
11
- if (config.currentChallengeName && config.currentChallengeCategory) {
12
- return { name: config.currentChallengeName, category: config.currentChallengeCategory };
13
- }
14
- return undefined;
15
- }
16
- // Chat state — shared with REPL
17
- let chatActive = false;
18
- let chatSession = null;
19
- let chatTokensUsed = 0;
20
- // Set true when the user burns through DEMO_TOKEN_CAP without solving. The
21
- // chat session stays alive so `!<shell>` and `submit <flag>` still work, but
22
- // further AI messages are blocked. See the reveal path in handleChatMessage.
23
- let tokensLocked = false;
24
- const DEMO_TOKEN_CAP = 5000;
25
- const EXAM_AI4CTF_CAP = 25000;
26
- let examChatCtx = null;
27
- export function isChatActive() {
28
- return chatActive;
29
- }
30
- export function isExamAi4ctfChatActive() {
31
- return chatActive && examChatCtx !== null;
32
- }
33
- function drawTokenBar() {
34
- const cap = examChatCtx ? EXAM_AI4CTF_CAP : DEMO_TOKEN_CAP;
35
- const used = chatTokensUsed;
36
- const pct = Math.min(Math.round((used / cap) * 100), 100);
37
- const width = 20;
38
- const filled = Math.round((pct / 100) * width);
39
- const empty = width - filled;
40
- const color = pct > 80 ? chalk.red : pct > 50 ? chalk.yellow : chalk.green;
41
- const label = examChatCtx ? 'AI4CTF section' : 'Tokens';
42
- console.log(chalk.gray(` ${label}: `) + color('█'.repeat(filled)) + chalk.gray('░'.repeat(empty)) + chalk.gray(` ${used}/${cap} (${pct}%)`));
43
- }
44
- const DEMO_FLAG = 'icoa{w3lc0me_2_ai4ctf}';
45
- // Scripted hints for the built-in Base64 demo challenge. The hint philosophy:
46
- // - a: nudge you toward the fingerprint (padding, charset).
47
- // - b: tell you the encoding name, nothing more. No shell command.
48
- // - c: for simple challenges, c deliberately adds no new info — hint b was
49
- // already enough. Instead c explains the hint principle and reminds the
50
- // user how to run a shell command inside ai4ctf (the `!` prefix).
51
- // This mirrors real competition behavior where trivial challenges cap at hint b.
52
- // Multi-line hint bodies live in i18n.ts (ai4ctfHintABody/BBody/CBody).
53
- // Shown when the user hits the 5000-token demo cap without solving. Keeps
54
- // the session alive (no chatActive=false) so they can still paste the shell
55
- // command below and then `submit <flag>`.
56
- function showTokenCapReveal() {
57
- console.log();
58
- console.log(chalk.yellow(' ─────────────────────────────────────────────'));
59
- console.log(chalk.bold.yellow(` ${t('ai4ctfRevealTitle')}`));
60
- console.log(chalk.yellow(' ─────────────────────────────────────────────'));
61
- console.log();
62
- for (const line of t('ai4ctfRevealBody').split('\n')) {
63
- if (line === '')
64
- console.log();
65
- else
66
- console.log(chalk.white(' ' + line));
67
- }
68
- console.log();
69
- console.log(chalk.cyan(' !echo aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== | base64 -d'));
70
- console.log();
71
- console.log(chalk.white(` ${t('ai4ctfRevealSeeFlag')}`));
72
- console.log();
73
- console.log(chalk.green(' icoa{w3lc0me_2_ai4ctf}'));
74
- console.log();
75
- console.log(chalk.white(` ${t('ai4ctfRevealThenSubmit')}`));
76
- console.log();
77
- console.log(chalk.cyan(' submit icoa{w3lc0me_2_ai4ctf}'));
78
- console.log();
79
- console.log(chalk.gray(` ${t('ai4ctfRevealLockNote')}`));
80
- console.log();
81
- }
82
- function showDemoHint(tier) {
83
- const title = tier === 'a' ? t('ai4ctfHintA') : tier === 'b' ? t('ai4ctfHintB') : t('ai4ctfHintC');
84
- const body = tier === 'a' ? t('ai4ctfHintABody') : tier === 'b' ? t('ai4ctfHintBBody') : t('ai4ctfHintCBody');
85
- const tierLabel = `Hint ${tier.toUpperCase()}`;
86
- const color = tier === 'a' ? chalk.green : tier === 'b' ? chalk.yellow : chalk.red;
87
- console.log();
88
- console.log(color.bold(` ▸ ${tierLabel} `) + chalk.gray(title));
89
- console.log();
90
- for (const line of body.split('\n')) {
91
- if (line === '')
92
- console.log();
93
- else
94
- console.log(chalk.white(' ' + line));
95
- }
96
- console.log();
97
- if (tier === 'a') {
98
- console.log(chalk.gray(` ${t('ai4ctfHintNextA')} `) + chalk.cyan('hint b'));
99
- console.log();
100
- }
101
- else if (tier === 'b') {
102
- console.log(chalk.gray(` ${t('ai4ctfHintNextB')} `) + chalk.cyan('hint c'));
103
- console.log();
104
- }
105
- // No trailing CTA after hint c — the content itself explains everything.
106
- }
107
- export async function handleChatMessage(input) {
108
- if (!chatSession)
109
- return 'exit';
110
- // Capture every input (including special commands like hint/submit/exit)
111
- // so the full flow shows up in session.log even before any early return.
112
- logCommand(`ai4ctf: ${input}`);
113
- // Exam-mode chat routes to a separate handler. Everything after this line
114
- // is demo-mode scripted Base64 logic.
115
- if (examChatCtx) {
116
- return handleExamAi4ctfMessage(input);
117
- }
118
- // Scripted demo hints — intercept before the AI chat so that typing
119
- // `hint a` / `hint b` / `hint c` behaves like a real competition command
120
- // instead of becoming a generic AI chat turn.
121
- const hintMatch = input.trim().toLowerCase().match(/^hint\s+([abc])$/);
122
- if (hintMatch) {
123
- showDemoHint(hintMatch[1]);
124
- return 'continue';
125
- }
126
- // Flag submission
127
- const submitMatch = input.match(/^submit\s+(.+)/i);
128
- if (submitMatch) {
129
- const flag = submitMatch[1].trim();
130
- if (flag === DEMO_FLAG) {
131
- console.log();
132
- console.log(chalk.green.bold(' ════════════════════════════════════'));
133
- console.log(chalk.green.bold(` ${t('ai4ctfCorrectFlag')}`));
134
- console.log(chalk.green.bold(' ════════════════════════════════════'));
135
- console.log();
136
- console.log(chalk.white(` ${t('ai4ctfDecoded')}`));
137
- console.log(chalk.gray(` ${t('ai4ctfWouldEarn')}`));
138
- console.log();
139
- drawTokenBar();
140
- chatActive = false;
141
- chatSession = null;
142
- fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
143
- method: 'POST',
144
- headers: { 'Content-Type': 'application/json' },
145
- body: JSON.stringify({ type: 'ai4ctf', solved: true, tokensUsed: chatTokensUsed, timestamp: new Date().toISOString() }),
146
- signal: AbortSignal.timeout(5000),
147
- }).catch(() => { });
148
- // Wrap-up summary — only shown after a successful solve.
149
- await sleep(1500);
150
- console.log();
151
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
152
- console.log(chalk.bold.white(` ${t('ai4ctfWrapTitle')}`));
153
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
154
- console.log();
155
- const wrap1Head = t('ai4ctfWrapLine1Head');
156
- const wrap1Body = t('ai4ctfWrapLine1Body');
157
- console.log(chalk.white(` 1. ${wrap1Head} `) + chalk.gray(wrap1Body.split('\n')[0] || ''));
158
- for (const line of wrap1Body.split('\n').slice(1)) {
159
- if (line.trim())
160
- console.log(chalk.gray(' ' + line.replace(/\{thinking\}/g, 'thinking')));
161
- }
162
- console.log();
163
- console.log(chalk.white(` 2. ${t('ai4ctfWrapLine2Head')} `) + chalk.gray(t('ai4ctfWrapLine2Body')));
164
- console.log();
165
- console.log(chalk.cyan(' !echo aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== | base64 -d'));
166
- console.log();
167
- await sleep(2000);
168
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
169
- console.log(chalk.bold.white(` ${t('ai4ctfWrapQuotasTitle')}`));
170
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
171
- console.log();
172
- console.log(chalk.yellow(' hint a ') + chalk.white(t('ai4ctfWrapQuotaA').padEnd(26)) + chalk.gray(t('ai4ctfWrapQuotaAHint')));
173
- console.log(chalk.yellow(' hint b ') + chalk.white(t('ai4ctfWrapQuotaB').padEnd(26)) + chalk.gray(t('ai4ctfWrapQuotaBHint')));
174
- console.log(chalk.yellow(' hint c ') + chalk.white(t('ai4ctfWrapQuotaC').padEnd(26)) + chalk.gray(t('ai4ctfWrapQuotaCHint')));
175
- console.log();
176
- for (const line of t('ai4ctfWrapQuotaFooter').split('\n')) {
177
- console.log(chalk.gray(' ' + line));
178
- }
179
- console.log();
180
- await sleep(2000);
181
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
182
- console.log(chalk.bold.white(` ${t('ai4ctfWrapNextTitle')} `) + chalk.red.bold('ctf4ai') + chalk.white(` — ${t('ai4ctfWrapNextSub')}`));
183
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
184
- console.log();
185
- const nextBody = t('ai4ctfWrapNextBody');
186
- const noProg = t('ai4ctfWrapNoProg');
187
- const lines = nextBody.split('\n');
188
- // First line contains the {noProg} placeholder to bold-emphasize.
189
- if (lines[0]) {
190
- const [before, after] = lines[0].split('{noProg}');
191
- console.log(chalk.white(' ' + (before || '')) + chalk.bold.white(noProg) + chalk.white(after || ''));
192
- }
193
- for (const line of lines.slice(1)) {
194
- if (line.trim())
195
- console.log(chalk.white(' ' + line));
196
- }
197
- console.log();
198
- console.log(chalk.gray(` ${t('ai4ctfWrapTypeCtf4ai')} `) + chalk.bold.red('ctf4ai'));
199
- console.log();
200
- return 'exit';
201
- }
202
- else {
203
- console.log();
204
- console.log(chalk.red(` ${t('ai4ctfWrongFlag')}`));
205
- console.log(chalk.gray(` ${t('ai4ctfFlagHint')}`));
206
- console.log();
207
- return 'continue';
208
- }
209
- }
210
- // Shell command execution
211
- if (input.startsWith('!')) {
212
- const cmd = input.slice(1).trim();
213
- if (!cmd)
214
- return 'continue';
215
- try {
216
- const { execSync } = await import('node:child_process');
217
- const output = execSync(cmd, { encoding: 'utf-8', timeout: 10000 }).trim();
218
- console.log();
219
- console.log(chalk.gray(' $ ') + chalk.white(cmd));
220
- console.log(chalk.white(' ' + output.split('\n').join('\n ')));
221
- console.log();
222
- console.log(chalk.gray(` ${t('ai4ctfFoundFlag')}`));
223
- console.log();
224
- }
225
- catch (err) {
226
- console.log();
227
- console.log(chalk.red(` Error: ${err.message?.split('\n')[0] || 'Command failed'}`));
228
- console.log();
229
- }
230
- return 'continue';
231
- }
232
- if (input === 'exit' || input === 'back' || input === 'quit' || input === 'menu') {
233
- chatActive = false;
234
- chatSession = null;
235
- // Anonymous stats
236
- fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
237
- method: 'POST',
238
- headers: { 'Content-Type': 'application/json' },
239
- body: JSON.stringify({ type: 'ai4ctf', tokensUsed: chatTokensUsed, timestamp: new Date().toISOString() }),
240
- signal: AbortSignal.timeout(5000),
241
- }).catch(() => { });
242
- console.log();
243
- console.log(chalk.gray(' ─────────────────────────────────────────'));
244
- console.log(chalk.white(` ${t('ai4ctfReport')}`));
245
- console.log(chalk.gray(` ${t('ai4ctfTokens')}: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
246
- console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (gemma-4-31b-it)`));
247
- console.log(chalk.gray(' ─────────────────────────────────────────'));
248
- console.log();
249
- // Prominent transition to CTF4AI — same style as exam → ai4ctf handoff.
250
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
251
- console.log(chalk.white(` ${t('ai4ctfExitNextTitle')} `) + chalk.bold.red('ctf4ai') + chalk.white(` — ${t('ai4ctfExitNextSub')}`));
252
- console.log(chalk.gray(` ${t('ai4ctfExitNextBody')}`));
253
- console.log(chalk.cyan(' ─────────────────────────────────────────────'));
254
- console.log();
255
- console.log(chalk.bold.red(' ctf4ai') + chalk.gray(` ${t('ai4ctfExitCmdNext')}`));
256
- console.log(chalk.white(' back') + chalk.gray(` ${t('ai4ctfExitCmdBack')}`));
257
- console.log();
258
- return 'exit';
259
- }
260
- // Token cap: first hit → reveal the answer, lock AI, but keep the session
261
- // alive so the user can still paste the shell command and then submit.
262
- // `tokensLocked` prevents any further sendMessage calls.
263
- if (!tokensLocked && chatTokensUsed >= DEMO_TOKEN_CAP) {
264
- tokensLocked = true;
265
- // Report the token-cap session once (solved:false). If the user then
266
- // submits the revealed flag, the submit-success path fires another POST
267
- // with solved:true which is the canonical record.
268
- fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
269
- method: 'POST',
270
- headers: { 'Content-Type': 'application/json' },
271
- body: JSON.stringify({ type: 'ai4ctf', solved: false, tokensUsed: chatTokensUsed, timestamp: new Date().toISOString() }),
272
- signal: AbortSignal.timeout(5000),
273
- }).catch(() => { });
274
- console.log();
275
- console.log(chalk.yellow(` ${t('tokenLimit')}`));
276
- drawTokenBar();
277
- showTokenCapReveal();
278
- return 'continue';
279
- }
280
- // If AI is locked (post-reveal), bounce any non-shell, non-submit input
281
- // back to the user with a reminder of what actually works now.
282
- if (tokensLocked) {
283
- console.log();
284
- console.log(chalk.yellow(` ${t('ai4ctfLockedTitle')}`));
285
- console.log(chalk.gray(` ${t('ai4ctfLockedUse')} `) + chalk.cyan('!echo aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== | base64 -d'));
286
- console.log(chalk.gray(` ${t('ai4ctfLockedThen')} `) + chalk.cyan('submit icoa{w3lc0me_2_ai4ctf}'));
287
- console.log();
288
- return 'continue';
289
- }
290
- console.log(chalk.gray(` ${t('ai4ctfThinking')}`));
291
- try {
292
- const response = await chatSession.sendMessage(input);
293
- process.stdout.write('\x1b[1A\x1b[2K');
294
- chatTokensUsed += response.tokensUsed;
295
- addTokenUsage(response.tokensUsed);
296
- console.log();
297
- printMarkdown(response.text);
298
- drawTokenBar();
299
- console.log();
300
- }
301
- catch (err) {
302
- process.stdout.write('\x1b[1A\x1b[2K');
303
- printError(`AI error: ${err.message}`);
304
- console.log();
305
- }
306
- return 'continue';
307
- }
308
- // ═══════════════════════════════════════════════════════════════
309
- // Real-exam AI4CTF chat mode (Q31-38)
310
- // ═══════════════════════════════════════════════════════════════
311
- function printExamAi4ctfWelcome(q, qNum, existingAnswer) {
312
- const config = getConfig();
313
- const modelName = config.geminiModel || 'gemma-4-31b-it';
314
- const isResume = chatTokensUsed > 0;
315
- console.log();
316
- console.log(chalk.green.bold(` ═══ AI4CTF — Q${qNum}: ${q.category} ═══`));
317
- if (isResume) {
318
- console.log(chalk.gray(` (resuming — prior chat is not remembered, but tokens already used stay deducted)`));
319
- }
320
- if (existingAnswer) {
321
- console.log(chalk.gray(` Current answer: `) + chalk.yellow(existingAnswer) + chalk.gray(` (submit again to change)`));
322
- }
323
- console.log();
324
- console.log(chalk.cyan(' ┌─────────────────────────────────────────────'));
325
- console.log(chalk.cyan(' │ ') + chalk.bold.white(`Q${qNum} [${q.category}] · ${q.points || 6} pts`));
326
- console.log(chalk.cyan(' │'));
327
- for (const line of String(q.text).split('\n')) {
328
- // Truncate very long lines for the welcome panel
329
- const display = line.length > 60 ? line.slice(0, 57) + '...' : line;
330
- console.log(chalk.cyan(' │ ') + chalk.white(display));
331
- }
332
- console.log(chalk.cyan(' │'));
333
- console.log(chalk.cyan(' │ ') + chalk.gray('Full question: ') + chalk.white(`exam q ${qNum}`));
334
- console.log(chalk.cyan(' │ ') + chalk.gray('Flag format: ICOA{...}'));
335
- console.log(chalk.cyan(' └─────────────────────────────────────────────'));
336
- console.log();
337
- console.log(chalk.bold.white(' How to work this question'));
338
- console.log();
339
- console.log(chalk.yellow(' hint a') + chalk.gray(' A gentle nudge (pre-written)'));
340
- console.log(chalk.yellow(' hint b') + chalk.gray(' Technique hint (pre-written)'));
341
- console.log(chalk.yellow(' hint c') + chalk.gray(' Key breakthrough (pre-written)'));
342
- console.log(chalk.white(' submit ICOA{...}') + chalk.gray(' Submit your flag for this question'));
343
- console.log(chalk.white(' !python3 ...') + chalk.gray(' Run Python in shell'));
344
- console.log(chalk.gray(' Just type freely to chat with the AI teammate.'));
345
- console.log(chalk.gray(' e.g. ') + chalk.white('"how do I decrypt AES-CBC in Python?"'));
346
- console.log();
347
- console.log(chalk.gray(' exit') + chalk.gray(' Leave chat, return to exam'));
348
- console.log();
349
- drawTokenBar();
350
- console.log(chalk.gray(` Model: Google Gemma 4 (${modelName})`));
351
- console.log();
352
- }
353
- function buildExamSystemPrompt(q, qNum) {
354
- return `You are an AI teammate helping a contestant solve an ICOA 2026 cybersecurity exam question.
355
-
356
- CURRENT QUESTION (Q${qNum}, ${q.category}, ${q.points || 6} points):
357
- ${q.text}
358
-
359
- YOUR RULES:
360
- - You MAY explain concepts, describe approaches, and help the contestant reason through the problem.
361
- - You MAY discuss the technology involved (AES, pwntools, Python struct, HTTP, etc.).
362
- - You MAY help debug Python code the contestant writes.
363
- - You MUST NOT reveal the flag directly.
364
- - You MUST NOT output any string matching ICOA{...} or icoa{...}.
365
- - You MUST NOT tell the contestant the exact answer to this question.
366
- - You MAY suggest which tool / library / technique to use.
367
- - Keep responses concise and practical. This contestant has limited time.`;
368
- }
369
- /**
370
- * Start a real-exam AI4CTF chat session bound to the current question.
371
- * Called by the `ai4ctf` command when the user is on Q31-38 of a real exam.
372
- */
373
- export async function startExamAi4ctfChat(qNum, question) {
374
- const { getRealExamState, saveExamState } = await import('../lib/exam-state.js');
375
- const state = getRealExamState();
376
- if (!state)
377
- return false;
378
- const usedSoFar = (state.aiUsage?.ai4ctf ?? 0);
379
- if (usedSoFar >= EXAM_AI4CTF_CAP) {
380
- console.log();
381
- console.log(chalk.yellow(' ⚠ Your AI4CTF token budget is exhausted.'));
382
- console.log(chalk.gray(' You can still submit a flag directly: ') + chalk.white(`exam answer ${qNum} ICOA{...}`));
383
- console.log();
384
- return false;
385
- }
386
- try {
387
- chatSession = await createChatSession(undefined, buildExamSystemPrompt(question, qNum));
388
- }
389
- catch (err) {
390
- printError(err.message);
391
- return false;
392
- }
393
- chatActive = true;
394
- chatTokensUsed = usedSoFar;
395
- tokensLocked = false;
396
- examChatCtx = { qNum, question, usageField: 'ai4ctf' };
397
- const existingAnswer = state.answers?.[qNum];
398
- printExamAi4ctfWelcome(question, qNum, existingAnswer);
399
- return true;
400
- }
401
- // Usage warnings shown once each when crossing thresholds. Keyed by cap.
402
- function maybeWarnTokenUsage(used, cap, warnedSet) {
403
- const pct = (used / cap) * 100;
404
- if (pct >= 95 && !warnedSet.has('95')) {
405
- console.log(chalk.red.bold(` ⚠ ${Math.round(pct)}% of section AI budget used — only ~${cap - used} tokens left.`));
406
- warnedSet.add('95');
407
- }
408
- else if (pct >= 80 && !warnedSet.has('80')) {
409
- console.log(chalk.yellow(` ⚠ 80% of section AI budget used.`));
410
- warnedSet.add('80');
411
- }
412
- else if (pct >= 50 && !warnedSet.has('50')) {
413
- console.log(chalk.gray(` Note: 50% of section AI budget used.`));
414
- warnedSet.add('50');
415
- }
416
- }
417
- const _ai4ctfWarned = new Set();
418
- async function handleExamAi4ctfMessage(input) {
419
- if (!examChatCtx || !chatSession)
420
- return 'exit';
421
- const trimmed = input.trim();
422
- const lower = trimmed.toLowerCase();
423
- // Scripted hints from question bank
424
- const hintMatch = lower.match(/^hint\s+([abc])$/);
425
- if (hintMatch) {
426
- const tier = hintMatch[1].toUpperCase();
427
- const hints = examChatCtx.question.hints;
428
- const hintText = hints && hints[tier];
429
- const color = tier === 'A' ? chalk.green : tier === 'B' ? chalk.yellow : chalk.red;
430
- console.log();
431
- console.log(color.bold(` ▸ Hint ${tier}`));
432
- console.log();
433
- if (hintText) {
434
- for (const line of String(hintText).split('\n')) {
435
- console.log(chalk.white(' ' + line));
436
- }
437
- }
438
- else {
439
- console.log(chalk.gray(' No pre-written hint at this tier for this question.'));
440
- }
441
- console.log();
442
- if (tier === 'A')
443
- console.log(chalk.gray(' Stuck? Try: ') + chalk.cyan('hint b'));
444
- else if (tier === 'B')
445
- console.log(chalk.gray(' Really stuck? Try: ') + chalk.cyan('hint c'));
446
- console.log();
447
- return 'continue';
448
- }
449
- // Flag submission — routes to the exam answer command under the hood so
450
- // interaction tracking, bookmarks, and submit-flow state are preserved.
451
- const submitMatch = trimmed.match(/^submit\s+(.+)/i);
452
- if (submitMatch) {
453
- const flag = submitMatch[1].trim();
454
- // Letter-only footgun: reject 'A'/'B'/'C'/'D' as flags. Same defence we
455
- // added at the REPL and exam-answer layers; duplicated here because chat
456
- // bypasses both.
457
- if (/^[A-Da-d]$/.test(flag)) {
458
- console.log();
459
- console.log(chalk.yellow(` "${flag}" looks like an MCQ letter, not a flag.`));
460
- console.log(chalk.gray(' Flag format: ') + chalk.green('ICOA{your_flag}') + chalk.gray('. Try again inside this chat.'));
461
- console.log();
462
- return 'continue';
463
- }
464
- const { getExamState, saveExamState } = await import('../lib/exam-state.js');
465
- const state = getExamState();
466
- if (!state)
467
- return 'exit';
468
- const q = state.questions.find((qq) => qq.number === examChatCtx.qNum);
469
- if (!q) {
470
- console.log(chalk.red(` Q${examChatCtx.qNum} not found in state.`));
471
- return 'continue';
472
- }
473
- // Store answer (validated on server at exam submit). Stay in chat so the
474
- // contestant can iterate on the flag without having to re-enter.
475
- const prevAnswer = state.answers[examChatCtx.qNum];
476
- if (!state.interactions)
477
- state.interactions = [];
478
- state.interactions.push({
479
- ts: new Date().toISOString(),
480
- q: examChatCtx.qNum,
481
- type: prevAnswer ? 'answer_changed' : 'answer_submitted',
482
- input: flag,
483
- result: 'via ai4ctf chat',
484
- });
485
- state.answers[examChatCtx.qNum] = flag;
486
- // Advance _lastQ to the next question in the section so that a subsequent
487
- // `ai4ctf` (after exit) picks up Q+1 automatically, no `exam q N` needed.
488
- const nextQ = examChatCtx.qNum + 1;
489
- state._lastQ = nextQ <= 38 ? nextQ : examChatCtx.qNum;
490
- saveExamState(state);
491
- console.log();
492
- if (prevAnswer) {
493
- console.log(chalk.green(` ✓ Q${examChatCtx.qNum} answer updated: `) + chalk.yellow(flag));
494
- console.log(chalk.gray(` Previous: ${prevAnswer}`));
495
- }
496
- else {
497
- console.log(chalk.green.bold(` ✓ Answer for Q${examChatCtx.qNum} recorded: ${flag}`));
498
- }
499
- console.log(chalk.gray(' (Grading happens at exam submit — you cannot preview correctness during the exam.)'));
500
- console.log();
501
- console.log(chalk.gray(' ') + chalk.white('submit ICOA{...}') + chalk.gray(' again to change, or ') + chalk.white('exit') + chalk.gray(' to move on'));
502
- if (nextQ <= 38) {
503
- console.log(chalk.gray(' After exit: ') + chalk.cyan('ai4ctf') + chalk.gray(` will open Q${nextQ}`));
504
- }
505
- else {
506
- console.log(chalk.gray(' After exit: AI4CTF section complete. Next: ') + chalk.red('ctf4ai') + chalk.gray(' on Q39'));
507
- }
508
- console.log();
509
- return 'continue';
510
- }
511
- // Shell command
512
- if (input.startsWith('!')) {
513
- const cmd = input.slice(1).trim();
514
- if (!cmd)
515
- return 'continue';
516
- try {
517
- const { execSync } = await import('node:child_process');
518
- const output = execSync(cmd, { encoding: 'utf-8', timeout: 10000 }).trim();
519
- console.log();
520
- console.log(chalk.gray(' $ ') + chalk.white(cmd));
521
- console.log(chalk.white(' ' + output.split('\n').join('\n ')));
522
- console.log();
523
- }
524
- catch (err) {
525
- console.log();
526
- console.log(chalk.red(` Error: ${err.message?.split('\n')[0] || 'Command failed'}`));
527
- console.log();
528
- }
529
- return 'continue';
530
- }
531
- // Exit chat
532
- if (lower === 'exit' || lower === 'back' || lower === 'quit') {
533
- const savedQ = examChatCtx.qNum;
534
- const { getExamState } = await import('../lib/exam-state.js');
535
- const exitState = getExamState();
536
- const hasAnswer = exitState && exitState.answers[savedQ] != null;
537
- const lastQ = (exitState?._lastQ) || savedQ;
538
- chatActive = false;
539
- chatSession = null;
540
- examChatCtx = null;
541
- _ai4ctfWarned.clear();
542
- console.log();
543
- console.log(chalk.gray(` AI4CTF chat ended for Q${savedQ}.`));
544
- if (hasAnswer && lastQ > savedQ && lastQ <= 38) {
545
- console.log(chalk.white(' Next: ') + chalk.bold.green(`Q${lastQ}`) + chalk.gray(' — type ') + chalk.cyan('ai4ctf') + chalk.gray(' to continue'));
546
- }
547
- else if (hasAnswer && savedQ >= 38) {
548
- console.log(chalk.white(' AI4CTF section complete — ') + chalk.bold.red('ctf4ai') + chalk.gray(' for Q39'));
549
- }
550
- else {
551
- console.log(chalk.gray(' Resume: ') + chalk.white(`exam q ${savedQ}`) + chalk.gray(' · Re-enter: ') + chalk.white('ai4ctf'));
552
- }
553
- console.log();
554
- return 'exit';
555
- }
556
- // Budget locked — only shell/submit/exit work
557
- if (tokensLocked) {
558
- console.log();
559
- console.log(chalk.yellow(' AI budget exhausted for AI4CTF section.'));
560
- console.log(chalk.gray(' Still available: ') + chalk.white('submit <flag>') + chalk.gray(' · ') + chalk.white('!shell') + chalk.gray(' · ') + chalk.white('exit'));
561
- console.log();
562
- return 'continue';
563
- }
564
- // Cap check
565
- if (chatTokensUsed >= EXAM_AI4CTF_CAP) {
566
- tokensLocked = true;
567
- console.log();
568
- console.log(chalk.red.bold(' ⚠ AI4CTF token budget exhausted (25,000 used).'));
569
- console.log(chalk.gray(' You can still: ') + chalk.white('submit <flag>') + chalk.gray(' · ') + chalk.white('!shell') + chalk.gray(' · ') + chalk.white('exit'));
570
- console.log();
571
- return 'continue';
572
- }
573
- // AI chat turn
574
- console.log(chalk.gray(' Thinking...'));
575
- try {
576
- const response = await chatSession.sendMessage(input);
577
- process.stdout.write('\x1b[1A\x1b[2K');
578
- chatTokensUsed += response.tokensUsed;
579
- // Persist token usage to exam state so it survives resume
580
- const { getRealExamState, saveExamState } = await import('../lib/exam-state.js');
581
- const state = getRealExamState();
582
- if (state) {
583
- if (!state.aiUsage)
584
- state.aiUsage = { ai4ctf: 0, ctf4ai: 0 };
585
- state.aiUsage.ai4ctf = chatTokensUsed;
586
- saveExamState(state);
587
- }
588
- console.log();
589
- printMarkdown(response.text);
590
- drawTokenBar();
591
- maybeWarnTokenUsage(chatTokensUsed, EXAM_AI4CTF_CAP, _ai4ctfWarned);
592
- console.log();
593
- }
594
- catch (err) {
595
- process.stdout.write('\x1b[1A\x1b[2K');
596
- printError(`AI error: ${err.message}`);
597
- console.log();
598
- }
599
- return 'continue';
600
- }
601
- export function registerAi4ctfCommand(program) {
602
- program
603
- .command('ai4ctf')
604
- .description('Chat with your AI teammate')
605
- .action(async () => {
606
- logCommand('ai4ctf');
607
- // Real-exam AI4CTF chat: Q31-38 enter a chat session bound to the
608
- // current question. Mirrors demo's Stage 2 structure (ai4ctf> prompt,
609
- // hint a/b/c, submit, !shell) but with per-question context and 25K
610
- // token budget tracked in state.aiUsage.ai4ctf.
611
- const { getRealExamState } = await import('../lib/exam-state.js');
612
- const realExam = getRealExamState();
613
- // Only treat as real exam if state actually has the AI4CTF section (Q31-38).
614
- // Stale/broken state with truncated questions falls back to demo behavior.
615
- const hasAi4ctfSection = realExam && realExam.questions.some((qq) => qq.number >= 31 && qq.number <= 38);
616
- if (realExam && hasAi4ctfSection) {
617
- const currentQ = realExam._lastQ || 1;
618
- if (currentQ < 31 || currentQ > 38) {
619
- console.log();
620
- console.log(chalk.yellow(` ai4ctf is available on Q31–38 (AI4CTF section).`));
621
- console.log(chalk.gray(` You are on Q${currentQ}. Jump there first:`));
622
- console.log(chalk.gray(' → ') + chalk.bold.cyan('exam q 31'));
623
- console.log();
624
- return;
625
- }
626
- const q = realExam.questions.find((qq) => qq.number === currentQ);
627
- if (!q) {
628
- printError(`Q${currentQ} not found in state. Try: exam q ${currentQ}`);
629
- return;
630
- }
631
- await startExamAi4ctfChat(currentQ, q);
632
- return;
633
- }
634
- const config = getConfig();
635
- const modelName = config.geminiModel || 'gemma-4-31b-it';
636
- // Demo challenge context
637
- const demoContext = {
638
- name: 'Hidden Message',
639
- category: 'Cryptography',
640
- };
641
- try {
642
- chatSession = await createChatSession(demoContext);
643
- }
644
- catch (err) {
645
- printError(err.message);
646
- return;
647
- }
648
- chatActive = true;
649
- chatTokensUsed = 0;
650
- tokensLocked = false;
651
- // Guided welcome
652
- console.log();
653
- console.log(chalk.green.bold(` ═══ ${t('ai4ctfTitle')} ═══`));
654
- console.log();
655
- console.log(chalk.white(` ${t('ai4ctfSample')}`));
656
- console.log();
657
- // Left gutter only — right border dropped because CJK/emoji widths
658
- // broke `padEnd` alignment across the 16 supported languages.
659
- console.log(chalk.cyan(' ┌─────────────────────────────────────────────'));
660
- console.log(chalk.cyan(' │ ') + chalk.bold.white(t('ai4ctfChallenge')));
661
- console.log(chalk.cyan(' │'));
662
- console.log(chalk.cyan(' │ ') + chalk.white(t('ai4ctfIntercepted')));
663
- console.log(chalk.cyan(' │ ') + chalk.green('aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ=='));
664
- console.log(chalk.cyan(' │'));
665
- console.log(chalk.cyan(' │ ') + chalk.white(t('ai4ctfDecode')));
666
- console.log(chalk.cyan(' │ ') + chalk.gray('Flag format: icoa{...}'));
667
- console.log(chalk.cyan(' └─────────────────────────────────────────────'));
668
- console.log();
669
- console.log(chalk.white(` ${t('ai4ctfLevels')}`));
670
- console.log();
671
- console.log(chalk.yellow(' hint a') + chalk.gray(` ${t('ai4ctfHintA')}`));
672
- console.log(chalk.gray(` ${t('ai4ctfHintAUses')}`));
673
- console.log(chalk.yellow(' hint b') + chalk.gray(` ${t('ai4ctfHintB')}`));
674
- console.log(chalk.gray(` ${t('ai4ctfHintBUses')}`));
675
- console.log(chalk.yellow(' hint c') + chalk.gray(` ${t('ai4ctfHintC')}`));
676
- console.log(chalk.gray(` ${t('ai4ctfHintCUses')}`));
677
- console.log();
678
- console.log(chalk.gray(' ─────────────────────────────────────────'));
679
- console.log(chalk.bold.white(` ${t('ai4ctfWelcomeCta')}`));
680
- console.log(' ' + chalk.cyan('hint a') + chalk.gray(` → ${t('ai4ctfHintNudge')}`));
681
- console.log(' ' + chalk.cyan('hint b') + chalk.gray(` → ${t('ai4ctfHintTechnique')}`));
682
- console.log(' ' + chalk.cyan('hint c') + chalk.gray(` → ${t('ai4ctfHintPrinciple')}`));
683
- console.log(chalk.gray(` ${t('ai4ctfWelcomeNoReveal')}`));
684
- console.log();
685
- console.log(chalk.white(` ${t('ai4ctfOrChat')}`));
686
- console.log(chalk.gray(` ${t('ai4ctfOrChatExample')} `) + chalk.white('"what encoding is this?"'));
687
- console.log();
688
- console.log(chalk.yellow(` ${t('ai4ctfCommands')}`));
689
- console.log(chalk.white(' hint a / b / c ') + chalk.gray(t('ai4ctfCmdHintLine')));
690
- console.log(chalk.white(' submit <flag> ') + chalk.gray(t('ai4ctfSubmitCmd')));
691
- console.log(chalk.white(' !<shell cmd> ') + chalk.gray(t('ai4ctfCmdShellLine')));
692
- console.log(chalk.gray(' e.g. ') + chalk.white('!echo aWNv... | base64 -d'));
693
- console.log(chalk.gray(` exit ${t('ai4ctfEndSession')}`));
694
- console.log();
695
- drawTokenBar();
696
- console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (${modelName})`));
697
- console.log(chalk.gray(` ${t('ai4ctfExit')}`));
698
- console.log();
699
- });
700
- }
1
+ function a0j(a,b){a=a-(0x24*0x2a+-0x210d+-0x4c7*-0x6);const c=a0b();let d=c[a];if(a0j['DEGNhj']===undefined){var e=function(i){const j='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let l='',m='';for(let n=-0x24c3+-0x2b*-0x15+-0x4*-0x84f,o,p,q=0x47f+0x14d2+-0x1951;p=i['charAt'](q++);~p&&(o=n%(-0x1c3a+-0x1675+0x32b3*0x1)?o*(-0x10bf+0x1397+-0x298)+p:p,n++%(-0x176b+0x1796*-0x1+0x2f05))?l+=String['fromCharCode'](-0x12*0xb2+0x1*-0xcd+0xe50&o>>(-(-0x486+-0x19e9+0x1*0x1e71)*n&0x2c2+0x411*-0x3+0x977)):-0x1012+-0x11b5+0x21c7){p=j['indexOf'](p);}for(let r=-0x55+0x16bb+-0xb33*0x2,s=l['length'];r<s;r++){m+='%'+('00'+l['charCodeAt'](r)['toString'](-0x4b5*0x2+0x302+0xb8*0x9))['slice'](-(0x75b*0x3+-0xb*-0x215+0xa*-0x47f));}return decodeURIComponent(m);};a0j['utfRmp']=e,a0j['MGEvGa']={},a0j['DEGNhj']=!![];}const f=c[0xe57*-0x2+0x1964+0x34a],g=a+f,h=a0j['MGEvGa'][g];return!h?(d=a0j['utfRmp'](d),a0j['MGEvGa'][g]=d):d=h,d;}(function(b,j){const a8=a0j,k=b();while(!![]){try{const m=parseInt(a8(0x19e))/(0x26*-0xbf+0x572+0x3*0x7a3)+parseInt(a8(0x1b9))/(-0xb81+0x4ab*0x5+0x2*-0x5ea)+parseInt(a8(0x1d2))/(0x2*0x665+0x18da*-0x1+0xc13)+parseInt(a8(0x241))/(0x7*0x11b+0x13a*-0xe+0x973)*(-parseInt(a8(0x1fb))/(0x1a53+-0x1190+-0x8be))+-parseInt(a8(0x22f))/(-0x2*-0xd40+0x2a*0x3+0x6be*-0x4)+parseInt(a8(0x22c))/(-0x4*-0x773+-0x188d+0x29c*-0x2)+parseInt(a8(0x1ef))/(-0x111*-0x15+0x2*0x11b4+-0x17*0x283)*(-parseInt(a8(0x258))/(-0x2*-0x394+0x142c+-0x1b4b));if(m===j)break;else k['push'](k['shift']());}catch(p){k['push'](k['shift']());}}}(a0b,-0x3*0x1c91e+0x170387+-0x469a2));import a0m from'chalk';import{createChatSession as a0p}from'../lib/gemini.js';import{addTokenUsage as a0q}from'../lib/budget.js';import{getConfig as a0v}from'../lib/config.js';import{logCommand as a0w}from'../lib/logger.js';import{printMarkdown as a0x,printError as a0z}from'../lib/ui.js';import{t as a0A}from'../lib/i18n.js';function a0b(){const ak=['ywK0y3rMv3jHCe5LEhruAxrSzq','iNDOyxqGzw5JB2rPBMCGAxmGDgHPCZ8I','iokvKokvKokvKa','ywK0y3rMtw9KzwW','igzVCIbrmZK','zw5JB2rPBMC','ywK0y3rMrgvJB2rLza','ixnOzwXS','ywK0y3rMv3jHCff1B3rHqW','ihrVA2vUCYbSzwz0lG','CxvLC3rPB25Z','ywK0y3rMv3jHCff1B3rHqKHPBNq','icaKia','icdILzdILzdILzaGquK0q1rgiokaLcbr','rNvSBcbXDwvZDgLVBJOGia','icbzB3uGy2fUihn0AwXSoIa','icdINjmGuq','ywK0y3rMq29YCMvJDezSywC','mtiXnZmYnurMz2feCq','icdILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','rMXHzYbMB3jTyxq6icaGieLdt0f7lI4UFq','quKGzxjYB3i6ia','icdILRGG','ywLvC2fNzq','ihjLy29YzgvKoIa','ywK0y3rMrxHPDenTzejHy2S','DgHPBMTPBMC','icaGicaGicaHzwnOBYbHv052wvH0m00YEgPnrZfSwhPkzLLxAZbzm1jTzLe9psb8igjHC2u2ncaTza','zxHPDa','q3j5ChrVz3jHCgH5','icdILiZILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','icaGicaGicaGicaG','Ahr0Chm6lY9WCMfJDgLJzs5Py29HmJaYnI5HDs9HCgKVAwnVys9Kzw1Vlxn0yxrZ','C3rYAw5NAwz5','yw5ZD2vYCW','icbiB3CGDg8GD29YAYb0AgLZihf1zxn0Aw9U','iMHVDYbKBYbjigrLy3j5ChqGquvtluncqYbPBIbqExrOB24/iG','y2XLyxi','ywK0y3rMq29TBwfUzhm','icaGie5VihbYzs13CML0DgvUigHPBNqGyxqGDgHPCYb0AwvYigzVCIb0AgLZihf1zxn0Aw9UlG','iwvJAg8GyvDoDI4UlIb8igjHC2u2ncaTza','icaGicHhCMfKAw5NigHHChbLBNmGyxqGzxHHBsbZDwjTAxqG4OcuihLVDsbJyw5UB3qGChjLDMLLDYbJB3jYzwn0BMvZCYbKDxjPBMCGDgHLigv4yw0Ukq','ywK0y3rMv3jHCff1B3rHqq','BMfTzq','icaGicaGigHPBNqGyYaGia','mJCYnZK5mKnPt2zAtq','y2f0zwDVCNK','icbbzNrLCIbLEgL0oIbbstrdveyGC2vJDgLVBIbJB21WBgv0zs4GtMv4DdOG','icaI','ywK0y3rMsgLUDefvC2vZ','Bwf0y2G','sgLUDca','icbtDgLSBcbHDMfPBgfIBgu6ia','icbbstrdveyGy2HHDcbLBMrLzcbMB3iGuq','xsaGWRCG','ywrK','DxrMltG','ywK0y3rMuMvWB3j0','Dgv4Da','ywK0y3rMsgLUDem','x2XHC3rr','AgLUDcbI','quK0q1rgihnLy3rPB24','z3jLzw4','ihrVignVBNrPBNvL','AwnVyxT3m2XJmg1LxZjFywK0y3rMFq','ywK0y3rMu3vIBwL0q21K','EwvSBg93','zxHHBsbXia','ywK0y3rMv3jHCeXPBMuXsgvHza','nte1mJq3m05HqMLsuG','yMfJAW','ywK0y3rMv3jHCeXPBMuYsgvHza','q29UDgvUDc1uExbL','icaGia','icaGicaGicbZDwjTAxqGAwnVyxT3m2XJmg1LxZjFywK0y3rMFq','icaGicfWExrOB24Zic4UlG','C3vIBwL0igLJB2f7DZnSyZbTzv8Yx2fPngn0zN0','CMvWBgfJzq','AgfZ','icaGicbIywnR','ywK0y3rMsgLUDee','ywK0y3rMuMv2zwfStg9JA05VDgu','C3rKB3v0','y2f0y2G','icaGicaGicbPy29HE3CZBgmWBwvFmL9HAtrJDgz9','CM91BMq','icdILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','icdILjtILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','lZuWmda','BM9KztPJAgLSzf9WCM9JzxnZ','ywK0y3rMtgv2zwXZ','ywK0y3rMv3jHCe5VuhjVzW','icaGihn1yM1PDca8zMXHzZ4Gica','lIbkDw1WihrOzxjLigzPCNn0oG','icbozxH0oIa','ywK0y3rMrMXHz0HPBNq','cGPzt1vsifjvtevtoGOTifLVDsbnqvKGzxHWBgfPBIbJB25Jzxb0CYWGzgvZy3jPyMuGyxbWCM9Hy2HLCYWGyw5KigHLBhaGDgHLignVBNrLC3rHBNqGCMvHC29UihrOCM91z2GGDgHLihbYB2jSzw0UcI0Gww91ie1bwsbKAxnJDxnZihrOzsb0zwnOBM9SB2D5igLUDM9SDMvKicHbrvmSihb3BNrVB2XZlcbqExrOB24GC3rYDwn0lcbivfrqlcbLDgmUks4klsbzB3uGtufzigHLBhaGzgvIDwCGuhL0Ag9UignVzguGDgHLignVBNrLC3rHBNqGD3jPDgvZlGOTifLVDsbnvvnuie5pvcbYzxzLywWGDgHLigzSywCGzgLYzwn0BhKUcI0Gww91ie1vu1qGtK9uig91Dhb1DcbHBNKGC3rYAw5Nig1HDgnOAw5NieLdt0f7lI4UFsbVCIbPy29HEY4UlN0UcI0Gww91ie1vu1qGtK9uihrLBgWGDgHLignVBNrLC3rHBNqGDgHLigv4ywn0igfUC3DLCIb0BYb0AgLZihf1zxn0Aw9UlGOTifLVDsbnqvKGC3vNz2vZDcb3AgLJAcb0B29Sic8GBgLICMfYEsaVihrLy2HUAxf1zsb0BYb1C2uUcI0Gs2vLCcbYzxnWB25ZzxmGy29Uy2LZzsbHBMqGChjHy3rPy2fSlIbuAgLZignVBNrLC3rHBNqGAgfZigXPBwL0zwqGDgLTzs4','C3vIBwL0idXMBgfNpG','mtaWotK1mdrVALjdBxm','Cu51Bq','zMLUza','ywK0y3rMtg9JA2vKvxnL','icaGigHPBNqGysaVigiGlYbJica','ywK0y3rMv2vSy29Tzun0yq','CMvK','imk3ia','oIbhB29NBguGr2vTBweGncaO','icaGicaGicaGicfLy2HVigfxtNzzwhqZttj4AK1hmwXyEKPMwvDRmfKZuM1Mut09ihWGyMfZzty0ic1K','C3vIBwL0ieLdt0f7lI4UFq','icbbstrdveyGC2vJDgLVBIbJB21WBgv0zsdIGjqG','nw1VAeXtAG','D2HPDgu','DxnHz2vgAwvSza','ihb0CW','ieLdt0f7lI4UFq','yM9Sza','vg9Rzw5Z','ue9tva','icdINjmGqw5ZD2vYigzVCIbr','y3LHBG','ywK0y3rMvgHPBMTPBMC','CxvLC3rPB24','CxvPDa','sgLKzgvUie1LC3nHz2u','icdIMQaGquK0q1rgihrVA2vUigj1zgDLDcbLEgHHDxn0zwqGkdi1ldaWmcb1C2vKks4','ywK0y3rMoIa','icaGideUia','Dg9vChbLCKnHC2u','ifn1yM1PDcb5B3vYigzSywCGzM9YihrOAxmGCxvLC3rPB24','ywK0y3rMv3jHCeXPBMuXqM9KEq','q29TBwfUzcbMywLSzwq','y29UDgLUDwu','icbbzNrLCIbLEgL0oIa','icdIMQaGww91CIbbstrdveyGDg9Rzw4GyNvKz2v0igLZigv4Agf1C3rLzc4','jsbVzIbZzwn0Aw9Uiefjigj1zgDLDcb1C2vKiokaLcbVBMX5ih4','icaGicbJDgy0ywK','DhjPBq','ywK0y3rMuMv2zwfSqM9KEq','ywK0y3rMuMv2zwfSvgHLBLn1yM1PDa','AgLUDhm','icdILiiG','icaGigHPBNqGyW','ywK0y3rMt3jdAgf0rxHHBxbSzq','icaGicaGia','icaOCMvZDw1PBMCG4OcuihbYAw9YignOyxqGAxmGBM90ihjLBwvTyMvYzwqSigj1Dcb0B2TLBNmGywXYzwfKEsb1C2vKihn0yxKGzgvKDwn0zwqP','q2HHDcb3AxrOihLVDxiGquKGDgvHBw1HDgu','yxbWBgLJyxrPB24VANnVBG','z2vTBweTnc0ZmwiTAxq','AM9PBG','ywK0y3rMv3jHCff1B3rHqG','ChvZAa','C3bSAxq','icbzB3uGy2fUihn0AwXSihn1yM1PDcbHigzSywCGzgLYzwn0BhK6ia','C29Tzq','icaGicaGicaGicaGieXLyxzLignOyxqSihjLDhvYBIb0BYbLEgfT','ywK0y3rMv3jHCe5LEhrcB2r5','ywK0y3rMvgL0Bgu','ywK0y3rMvg9Rzw5Z','ywK0y3rMtg9JA2vKvgL0Bgu','mZG2odGZmfPRsM9qEG','icaGicaGigHPBNqGysaGia','iokaLca','nJK4otmWngzrCvLIqG','igfNywLUihrVignOyw5NzsWGB3iG','C2vUze1LC3nHz2u','icbnB2rLBdOGr29Vz2XLieDLBw1HidqGka','ywK0y3rMsgLUDfbYAw5JAxbSzq','icdcTYaGuMuTzw50zxi6ia','icaGieP1C3qGDhLWzsbMCMvLBhKGDg8Gy2HHDcb3AxrOihrOzsbbssb0zwfTBwf0zs4','icaGigHPBNqGyG','ywK0y3rMrw5Ku2vZC2LVBG','zxHHBsbHBNn3zxiG','E25VuhjVz30','ywK0y3rMrxHPDa','ywK0y3rMuMv2zwfSu2vLrMXHzW','BwvZC2fNzq','yw5ZD2vYx3n1yM1PDhrLza','ywK0y3rMsgLUDe5LEhrc','CMvWzwf0','icaGihn1yM1PDcbjq09bEY4UlN0','nJiYndGWnhbZAhvPzq','icaGifjLywXSEsbZDhvJAZ8Gvhj5oIa','z3jHEq','ywK0y3rMuMv2zwfSvgL0Bgu','icbB','icdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILza','icaGicaGicaGicaGicaGicaGicbLlMCUia','icaOC3vIBwL0igfNywLUihrVignOyw5NzsK','icbszxn1Bwu6ia','icaGigHPBNqGyq','icbuAgLUA2LUzY4UlG','ywK0y3rMsgLUDejcB2r5','AgLUDcbJ','igfUC3DLCIb1CgrHDgvKoIa','icbgBgfNigzVCM1HDdOG','icaGice8C2HLBgWGy21KpIaGica','ywK0y3rM','Aw50zxjHy3rPB25Z','z2vTAw5Ptw9KzwW','zxHHBsbXidmX','ywK0y3rMv3jHCff1B3rHquHPBNq','g1SXqrTBmKS','ywK0y3rMsgLUDei','oxbVCLHZCG','icaGicaGicaGicaGica','ywK0y3rMsgLUDenvC2vZ','rMXHzYbMB3jTyxq6igLJB2f7lI4UFq','Cg9PBNrZ','icaGifbYzxzPB3vZoIa','DMLHigfPngn0zIbJAgf0','D3jPDgu','C2XPy2u','BgvUz3rO','ywK0y3rMq2HHBgXLBMDL','C3rHCNrZv2L0Aa','icdILii','icbr','lI4VBgLIl2v4yw0TC3rHDguUANm','BNvTyMvY','icdILzdILzdILzaG','ywK0y3rMv3jHCff1B3rHC1rPDgXL','ywK0y3rMrxHPDenTze5LEhq','icbfCNjVCJOG','icaGidiUia','AgLUDcbH','icaG4OAsia','icaGicaGicaGicblzxKGyNjLywT0AhjVDwDOicHWCMuTD3jPDhrLBIK','Dg9Rzw5ZvxnLza','CgfKrw5K','ywK0y3rMq21Ku2HLBgXmAw5L','y3rMngfP','icaGicaGigHPBNqGyIaGia','Dg9ju09tDhjPBMC','ywK0y3rMtg9JA2vKvgHLBG','Dg9mB3DLCKnHC2u','Bg9N','DgLTzw91Da','iwvJAg8GyvDoDLLyDdnnmNHQtuCXBfH6sMzzv2SWwtnsBwzrpt0GFcbIyxnLnJqGlwq','ywK0y3rMv3jHCe5LEhrtDwi','ywK0y3rMv3jVBMDgBgfN','ywK0y3rMt3jdAgf0'];a0b=function(){return ak;};return a0b();}const i=b=>new Promise(j=>setTimeout(j,b));let s=!(-0x2b*-0x15+-0xb*0x1e9+0x79*0x25),g=null,r=-0x1d3c+-0x2089+0x3dc5,y=!(0x52*-0x31+-0x9c5+-0x28*-0xa3);const f=-0x2ccd+-0xbcba+0x14b2f;let u=null;export function isChatActive(){return s;}export function isExamAi4ctfChatActive(){return s&&null!==u;}function h(){const a9=a0j,b=u?f:0x1a3a+0x224d+-0x28ff,j=r,k=Math['min'](Math['round'](j/b*(0x1*0x15bf+-0xf62+-0x5f9)),-0x19e9+0x37*-0x70+-0x1*-0x325d),m=Math[a9(0x1e2)](k/(-0x1e07*0x1+0x10d8+0xd93)*(-0x1e98+-0x236a+-0x210b*-0x2)),p=-0x16ef+0x15d9+0x12a*0x1-m,q=k>0x665*-0x5+0x77*-0x1d+0xb71*0x4?a0m[a9(0x1f5)]:k>0x12*0x1f7+-0x901+0x57*-0x4d?a0m[a9(0x1cf)]:a0m[a9(0x1cb)],v=u?a9(0x1ca):a9(0x201);console['log'](a0m[a9(0x243)]('\x20\x20'+v+':\x20')+q('█'[a9(0x23f)](m))+a0m[a9(0x243)]('░'['repeat'](p))+a0m['gray']('\x20'+j+'/'+b+'\x20('+k+'%)'));}export async function handleChatMessage(p){const aa=a0j;if(!g)return aa(0x1a8);if(a0w(aa(0x20a)+p),u)return async function(z){const ab=aa;if(!u||!g)return ab(0x1a8);const A=z['trim'](),B=A[ab(0x185)](),C=B[ab(0x1be)](/^hint\s+([abc])$/);if(C){const E=C[0x15e9+0x38f*-0x9+0xa1f][ab(0x20c)](),F=u['question'][ab(0x218)],G=F&&F[E],H='A'===E?a0m[ab(0x1cb)]:'B'===E?a0m['yellow']:a0m[ab(0x1f5)];if(console[ab(0x186)](),console['log'](H['bold']('\x20\x20▸\x20Hint\x20'+E)),console[ab(0x186)](),G){for(const I of String(G)['split']('\x0a'))console[ab(0x186)](a0m[ab(0x1fc)](ab(0x1d6)+I));}else console['log'](a0m[ab(0x243)](ab(0x1b3)));return console[ab(0x186)](),'A'===E?console['log'](a0m['gray']('\x20\x20\x20\x20Stuck?\x20Try:\x20')+a0m['cyan'](ab(0x1c9))):'B'===E&&console[ab(0x186)](a0m['gray'](ab(0x242))+a0m[ab(0x204)](ab(0x24d))),console['log'](),ab(0x210);}const D=A[ab(0x1be)](/^submit\s+(.+)/i);if(D){const J=D[-0x194d*0x1+0x1273+0x75*0xf][ab(0x215)]();if(/^[A-Da-d]$/['test'](J))return console[ab(0x186)](),console[ab(0x186)](a0m[ab(0x1cf)](ab(0x1bc)+J+'\x22\x20looks\x20like\x20an\x20MCQ\x20letter,\x20not\x20a\x20flag.')),console['log'](a0m[ab(0x243)](ab(0x24f))+a0m[ab(0x1cb)]('ICOA{your_flag}')+a0m[ab(0x243)]('.\x20Try\x20again\x20inside\x20this\x20chat.')),console['log'](),ab(0x210);const {getExamState:K,saveExamState:L}=await import('../lib/exam-state.js'),M=K();if(!M)return ab(0x1a8);if(!M[ab(0x196)]['find'](P=>P[ab(0x267)]===u[ab(0x1f0)]))return console[ab(0x186)](a0m[ab(0x1f5)](ab(0x265)+u['qNum']+'\x20not\x20found\x20in\x20state.')),ab(0x210);const N=M[ab(0x1ae)][u[ab(0x1f0)]];M[ab(0x252)]||(M[ab(0x252)]=[]),M[ab(0x252)][ab(0x223)]({'ts':new Date()[ab(0x275)](),'q':u['qNum'],'type':N?'answer_changed':ab(0x23d),'input':J,'result':ab(0x25e)}),M[ab(0x1ae)][u[ab(0x1f0)]]=J;const O=u[ab(0x1f0)]+(0x21a7+0x1*-0x1ebe+-0x2e8);return M[ab(0x1c8)]=O<=0xaf*-0x1b+-0x6aa+-0x1*-0x1945?O:u[ab(0x1f0)],L(M),console['log'](),N?(console[ab(0x186)](a0m[ab(0x1cb)](ab(0x19c)+u['qNum']+ab(0x24e))+a0m[ab(0x1cf)](J)),console['log'](a0m['gray'](ab(0x25d)+N))):console[ab(0x186)](a0m[ab(0x1cb)][ab(0x200)](ab(0x203)+u[ab(0x1f0)]+ab(0x1a4)+J)),console[ab(0x186)](a0m[ab(0x243)](ab(0x1b5))),console[ab(0x186)](),console[ab(0x186)](a0m[ab(0x243)]('\x20\x20')+a0m[ab(0x1fc)](ab(0x1f9))+a0m['gray'](ab(0x230))+a0m[ab(0x1fc)]('exit')+a0m[ab(0x243)]('\x20to\x20move\x20on')),O<=0x5*-0xa+0x19cd+0x1975*-0x1?console[ab(0x186)](a0m[ab(0x243)](ab(0x211))+a0m['cyan'](ab(0x251))+a0m[ab(0x243)]('\x20will\x20open\x20Q'+O)):console[ab(0x186)](a0m[ab(0x243)](ab(0x1bb))+a0m['red'](ab(0x273))+a0m[ab(0x243)]('\x20on\x20Q39')),console['log'](),ab(0x210);}if(z[ab(0x263)]('!')){const P=z[ab(0x260)](0x2*-0x2a8+-0x2ab*0x1+0x7fc)[ab(0x215)]();if(!P)return ab(0x210);try{const Q={};Q[ab(0x191)]=ab(0x1c4),Q[ab(0x187)]=0x2710;const {execSync:R}=await import('node:child_process'),S=R(P,Q)['trim']();console[ab(0x186)](),console[ab(0x186)](a0m[ab(0x243)](ab(0x198))+a0m[ab(0x1fc)](P)),console['log'](a0m[ab(0x1fc)]('\x20\x20'+S[ab(0x224)]('\x0a')[ab(0x221)]('\x0a\x20\x20'))),console['log']();}catch(T){console[ab(0x186)](),console['log'](a0m[ab(0x1f5)]('\x20\x20Error:\x20'+(T[ab(0x23c)]?.[ab(0x224)]('\x0a')[-0x16*0x54+-0xabb+0x11f3]||'Command\x20failed'))),console[ab(0x186)]();}return ab(0x210);}if(ab(0x1a8)===B||ab(0x1d3)===B||ab(0x207)===B){const U=u[ab(0x1f0)],{getExamState:V}=await import(ab(0x266)),W=V(),X=W&&null!=W['answers'][U],Y=W?.['_lastQ']||U;return s=!(-0x48b+0x104*-0x1b+-0x6*-0x554),g=null,u=null,d[ab(0x1b1)](),console[ab(0x186)](),console[ab(0x186)](a0m[ab(0x243)](ab(0x1c1)+U+'.')),X&&Y>U&&Y<=-0x82c*-0x2+0x1e5b+-0x1*0x2e8d?console[ab(0x186)](a0m['white'](ab(0x1eb))+a0m[ab(0x200)]['green']('Q'+Y)+a0m[ab(0x243)]('\x20—\x20type\x20')+a0m[ab(0x204)](ab(0x251))+a0m[ab(0x243)](ab(0x1cc))):X&&U>=-0x893*0x1+-0x1d98+0x2651?console[ab(0x186)](a0m[ab(0x1fc)](ab(0x1fa))+a0m['bold'][ab(0x1f5)](ab(0x273))+a0m[ab(0x243)](ab(0x190))):console[ab(0x186)](a0m[ab(0x243)](ab(0x249))+a0m[ab(0x1fc)](ab(0x1d0)+U)+a0m['gray'](ab(0x234))+a0m[ab(0x1fc)](ab(0x251))),console[ab(0x186)](),ab(0x1a8);}if(y)return console[ab(0x186)](),console['log'](a0m[ab(0x1cf)]('\x20\x20AI\x20budget\x20exhausted\x20for\x20AI4CTF\x20section.')),console['log'](a0m['gray'](ab(0x1c0))+a0m[ab(0x1fc)](ab(0x1ee))+a0m[ab(0x243)]('\x20·\x20')+a0m[ab(0x1fc)](ab(0x193))+a0m[ab(0x243)](ab(0x1f6))+a0m['white']('exit')),console[ab(0x186)](),'continue';if(r>=f)return y=!(-0x16*-0x98+0x5ee*-0x6+0x1684),console[ab(0x186)](),console[ab(0x186)](a0m[ab(0x1f5)][ab(0x200)](ab(0x209))),console[ab(0x186)](a0m['gray'](ab(0x19b))+a0m[ab(0x1fc)](ab(0x1ee))+a0m[ab(0x243)](ab(0x1f6))+a0m[ab(0x1fc)](ab(0x193))+a0m[ab(0x243)]('\x20·\x20')+a0m[ab(0x1fc)]('exit')),console[ab(0x186)](),ab(0x210);console[ab(0x186)](a0m[ab(0x243)](ab(0x24b)));try{const Z=await g['sendMessage'](z);process[ab(0x1df)][ab(0x25f)]('\x1b[1A\x1b[2K'),r+=Z[ab(0x270)];const {getRealExamState:a0,saveExamState:a1}=await import(ab(0x266)),a2=a0(),a3={};a3['ai4ctf']=0x0,a3[ab(0x273)]=0x0,(a2&&(a2[ab(0x1a3)]||(a2[ab(0x1a3)]=a3),a2[ab(0x1a3)]['ai4ctf']=r,a1(a2)),console['log'](),a0x(Z[ab(0x1c6)]),h(),function(a4,a5,a6){const ac=ab,a7=a4/a5*(-0x1518+0xa*0xca+0x2b8*0x5);a7>=0x25ec+-0x22b0+0x1*-0x2dd&&!a6[ac(0x1db)]('95')?(console[ac(0x186)](a0m[ac(0x1f5)][ac(0x200)]('\x20\x20⚠\x20'+Math[ac(0x1e2)](a7)+ac(0x213)+(a5-a4)+ac(0x195))),a6[ac(0x1c3)]('95')):a7>=-0x4*0x81b+0x1dbe+0x17f*0x2&&!a6[ac(0x1db)]('80')?(console['log'](a0m['yellow']('\x20\x20⚠\x2080%\x20of\x20section\x20AI\x20budget\x20used.')),a6[ac(0x1c3)]('80')):a7>=0x47f*-0x1+-0x25*0x5c+0x11fd&&!a6['has']('50')&&(console[ac(0x186)](a0m[ac(0x243)]('\x20\x20Note:\x2050%\x20of\x20section\x20AI\x20budget\x20used.')),a6[ac(0x1c3)]('50'));}(r,f,d),console[ab(0x186)]());}catch(a4){process['stdout'][ab(0x25f)](ab(0x256)),a0z('AI\x20error:\x20'+a4[ab(0x23c)]),console[ab(0x186)]();}return ab(0x210);}(p);const q=p[aa(0x215)]()['toLowerCase']()[aa(0x1be)](/^hint\s+([abc])$/);if(q)return function(z){const ad=aa,A=a0A('a'===z?ad(0x1dd):'b'===z?ad(0x257):ad(0x1c7)),B=a0A('a'===z?'ai4ctfHintABody':'b'===z?ad(0x24c):'ai4ctfHintCBody'),C=ad(0x1bf)+z['toUpperCase'](),D='a'===z?a0m[ad(0x1cb)]:'b'===z?a0m[ad(0x1cf)]:a0m['red'];console[ad(0x186)](),console[ad(0x186)](D['bold'](ad(0x1a2)+C+'\x20\x20')+a0m[ad(0x243)](A)),console[ad(0x186)]();for(const E of B[ad(0x224)]('\x0a'))''===E?console[ad(0x186)]():console[ad(0x186)](a0m[ad(0x1fc)](ad(0x1d6)+E));console[ad(0x186)](),'a'===z?(console['log'](a0m['gray'](ad(0x1d6)+a0A('ai4ctfHintNextA')+'\x20')+a0m[ad(0x204)](ad(0x1c9))),console['log']()):'b'===z&&(console[ad(0x186)](a0m[ad(0x243)]('\x20\x20\x20\x20'+a0A(ad(0x23e))+'\x20')+a0m[ad(0x204)](ad(0x24d))),console[ad(0x186)]());}(q[0x2*-0x10e1+0x233*0x1+0x1f90]),'continue';const v=p['match'](/^submit\s+(.+)/i);if(v){if(aa(0x1cd)===v[0x863*-0x1+0x475+0x3ef][aa(0x215)]()){console[aa(0x186)](),console[aa(0x186)](a0m[aa(0x1cb)][aa(0x200)](aa(0x246))),console[aa(0x186)](a0m[aa(0x1cb)][aa(0x200)]('\x20\x20'+a0A(aa(0x19d)))),console[aa(0x186)](a0m[aa(0x1cb)][aa(0x200)](aa(0x246))),console['log'](),console[aa(0x186)](a0m['white']('\x20\x20'+a0A(aa(0x192)))),console[aa(0x186)](a0m['gray']('\x20\x20'+a0A('ai4ctfWouldEarn'))),console[aa(0x186)](),h(),s=!(-0x1b9e+-0x229d+-0x1c*-0x239),g=null,fetch(aa(0x1ac),{'method':'POST','headers':{'Content-Type':aa(0x21f)},'body':JSON[aa(0x1ad)]({'type':aa(0x251),'solved':!(0x145d+0x876+-0x9d*0x2f),'tokensUsed':r,'timestamp':new Date()['toISOString']()}),'signal':AbortSignal[aa(0x187)](-0x7a*0x48+-0x18ee+0x1*0x4ec6)})[aa(0x1e0)](()=>{}),await i(0xd*0x59+-0x589+0x6e0),console[aa(0x186)](),console['log'](a0m[aa(0x204)](aa(0x1e3))),console[aa(0x186)](a0m[aa(0x200)][aa(0x1fc)]('\x20\x20'+a0A('ai4ctfWrapTitle'))),console[aa(0x186)](a0m[aa(0x204)](aa(0x1e3))),console[aa(0x186)]();const z=a0A(aa(0x1d1)),A=a0A(aa(0x20e));console[aa(0x186)](a0m[aa(0x1fc)](aa(0x20b)+z+'\x20')+a0m[aa(0x243)](A[aa(0x224)]('\x0a')[-0xa56+-0x1*0x2ba+0xd10]||''));for(const E of A[aa(0x224)]('\x0a')[aa(0x260)](-0x1*0x145b+-0xa05+0xb*0x2c3))E['trim']()&&console[aa(0x186)](a0m[aa(0x243)](aa(0x21c)+E[aa(0x1da)](/\{thinking\}/g,aa(0x1a6))));console[aa(0x186)](),console[aa(0x186)](a0m[aa(0x1fc)](aa(0x26c)+a0A(aa(0x1d4))+'\x20')+a0m[aa(0x243)](a0A('ai4ctfWrapLine2Body'))),console[aa(0x186)](),console[aa(0x186)](a0m[aa(0x204)](aa(0x1f8))),console[aa(0x186)](),await i(0x6*-0x189+0x1*-0x1820+0x2926),console[aa(0x186)](a0m[aa(0x204)](aa(0x1e3))),console['log'](a0m[aa(0x200)][aa(0x1fc)]('\x20\x20'+a0A(aa(0x269)))),console['log'](a0m[aa(0x204)](aa(0x1e3))),console['log'](),console[aa(0x186)](a0m[aa(0x1cf)](aa(0x22d))+a0m['white'](a0A(aa(0x1b6))[aa(0x271)](0x2212+-0x1219+-0xfdf))+a0m['gray'](a0A(aa(0x255)))),console[aa(0x186)](a0m[aa(0x1cf)](aa(0x274))+a0m[aa(0x1fc)](a0A(aa(0x222))[aa(0x271)](0xb*0x61+-0x1d08+0x18f7))+a0m[aa(0x243)](a0A(aa(0x197)))),console[aa(0x186)](a0m['yellow'](aa(0x1b8))+a0m[aa(0x1fc)](a0A(aa(0x194))[aa(0x271)](-0x1*0xfee+-0x26b7+0x36bf*0x1))+a0m[aa(0x243)](a0A('ai4ctfWrapQuotaCHint'))),console[aa(0x186)]();for(const F of a0A('ai4ctfWrapQuotaFooter')[aa(0x224)]('\x0a'))console[aa(0x186)](a0m[aa(0x243)](aa(0x1d6)+F));console[aa(0x186)](),await i(0x1621+0x9*0x2e7+0x2870*-0x1),console['log'](a0m[aa(0x204)](aa(0x1e3))),console['log'](a0m[aa(0x200)][aa(0x1fc)]('\x20\x20'+a0A(aa(0x18c))+'\x20')+a0m[aa(0x1f5)][aa(0x200)](aa(0x273))+a0m[aa(0x1fc)](aa(0x22e)+a0A(aa(0x189)))),console[aa(0x186)](a0m[aa(0x204)]('\x20\x20─────────────────────────────────────────────')),console['log']();const B=a0A(aa(0x228)),C=a0A(aa(0x1e8)),D=B['split']('\x0a');if(D[-0xa08+-0x16cb+-0x20d3*-0x1]){const [G,H]=D[0x21f9+0xdc8+-0x2fc1][aa(0x224)](aa(0x239));console[aa(0x186)](a0m[aa(0x1fc)](aa(0x1d6)+(G||''))+a0m[aa(0x200)][aa(0x1fc)](C)+a0m[aa(0x1fc)](H||''));}for(const I of D[aa(0x260)](-0x53*-0x5+0xbf3*-0x3+-0x17d*-0x17))I[aa(0x215)]()&&console['log'](a0m['white'](aa(0x1d6)+I));return console['log'](),console[aa(0x186)](a0m[aa(0x243)]('\x20\x20\x20\x20'+a0A('ai4ctfWrapTypeCtf4ai')+'\x20')+a0m[aa(0x200)][aa(0x1f5)](aa(0x273))),console['log'](),aa(0x1a8);}return console[aa(0x186)](),console[aa(0x186)](a0m[aa(0x1f5)]('\x20\x20'+a0A(aa(0x18a)))),console['log'](a0m[aa(0x243)]('\x20\x20'+a0A(aa(0x1ec)))),console[aa(0x186)](),aa(0x210);}if(p[aa(0x263)]('!')){const J=p[aa(0x260)](-0x623+-0x40d*0x1+0xa31)[aa(0x215)]();if(!J)return'continue';try{const K={};K['encoding']='utf-8',K[aa(0x187)]=0x2710;const {execSync:L}=await import(aa(0x1e6)),M=L(J,K)[aa(0x215)]();console['log'](),console[aa(0x186)](a0m[aa(0x243)](aa(0x198))+a0m[aa(0x1fc)](J)),console[aa(0x186)](a0m[aa(0x1fc)]('\x20\x20'+M[aa(0x224)]('\x0a')[aa(0x221)]('\x0a\x20\x20'))),console['log'](),console['log'](a0m[aa(0x243)]('\x20\x20'+a0A('ai4ctfFoundFlag'))),console[aa(0x186)]();}catch(N){console['log'](),console[aa(0x186)](a0m[aa(0x1f5)](aa(0x26b)+(N['message']?.[aa(0x224)]('\x0a')[-0x28b+-0x2dc*-0xb+-0x1ce9]||aa(0x20f)))),console[aa(0x186)]();}return aa(0x210);}const w={};w[aa(0x1d5)]=aa(0x21f);if(aa(0x1a8)===p||aa(0x1d3)===p||aa(0x207)===p||'menu'===p)return s=!(0x1d92+0x1190+-0x2f21),g=null,fetch(aa(0x1ac),{'method':aa(0x202),'headers':w,'body':JSON['stringify']({'type':aa(0x251),'tokensUsed':r,'timestamp':new Date()[aa(0x275)]()}),'signal':AbortSignal[aa(0x187)](0x22*-0xc5+0x207*-0x9+0x3ff1)})[aa(0x1e0)](()=>{}),console[aa(0x186)](),console[aa(0x186)](a0m['gray'](aa(0x19f))),console['log'](a0m[aa(0x1fc)]('\x20\x20'+a0A(aa(0x1c5)))),console['log'](a0m[aa(0x243)]('\x20\x20'+a0A(aa(0x22a))+':\x20'+r+aa(0x1e5))),console['log'](a0m['gray']('\x20\x20'+a0A(aa(0x18f))+':\x20Google\x20Gemma\x204\x20(gemma-4-31b-it)')),console['log'](a0m[aa(0x243)]('\x20\x20─────────────────────────────────────────')),console[aa(0x186)](),console[aa(0x186)](a0m['cyan'](aa(0x1e3))),console['log'](a0m[aa(0x1fc)]('\x20\x20'+a0A('ai4ctfExitNextTitle')+'\x20')+a0m['bold'][aa(0x1f5)](aa(0x273))+a0m['white'](aa(0x22e)+a0A('ai4ctfExitNextSub'))),console[aa(0x186)](a0m['gray']('\x20\x20'+a0A('ai4ctfExitNextBody'))),console[aa(0x186)](a0m[aa(0x204)]('\x20\x20─────────────────────────────────────────────')),console[aa(0x186)](),console[aa(0x186)](a0m[aa(0x200)]['red'](aa(0x214))+a0m[aa(0x243)]('\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'+a0A(aa(0x26a)))),console[aa(0x186)](a0m[aa(0x1fc)](aa(0x1dc))+a0m[aa(0x243)](aa(0x1ab)+a0A(aa(0x1a5)))),console[aa(0x186)](),aa(0x1a8);const x={};x[aa(0x1d5)]=aa(0x21f);if(!y&&r>=-0x1*0x177b+0x13ab+0x1758)return y=!(-0x158*0xb+-0x11e9+0x20b1*0x1),fetch(aa(0x1ac),{'method':aa(0x202),'headers':x,'body':JSON[aa(0x1ad)]({'type':'ai4ctf','solved':!(0x23c0+-0x1*0x2c2+-0x20fd),'tokensUsed':r,'timestamp':new Date()['toISOString']()}),'signal':AbortSignal[aa(0x187)](-0x1711+-0x1bf2+0x468b)})['catch'](()=>{}),console[aa(0x186)](),console[aa(0x186)](a0m[aa(0x1cf)]('\x20\x20'+a0A('tokenLimit'))),h(),(function(){const ae=aa;console[ae(0x186)](),console[ae(0x186)](a0m[ae(0x1cf)](ae(0x1e3))),console[ae(0x186)](a0m[ae(0x200)]['yellow']('\x20\x20'+a0A(ae(0x244)))),console[ae(0x186)](a0m[ae(0x1cf)](ae(0x1e3))),console[ae(0x186)]();for(const O of a0A(ae(0x216))[ae(0x224)]('\x0a'))''===O?console[ae(0x186)]():console[ae(0x186)](a0m['white'](ae(0x1d6)+O));console[ae(0x186)](),console[ae(0x186)](a0m[ae(0x204)](ae(0x1a7))),console[ae(0x186)](),console[ae(0x186)](a0m[ae(0x1fc)](ae(0x1d6)+a0A(ae(0x23b)))),console[ae(0x186)](),console['log'](a0m[ae(0x1cb)](ae(0x1e1))),console[ae(0x186)](),console[ae(0x186)](a0m[ae(0x1fc)](ae(0x1d6)+a0A(ae(0x217)))),console[ae(0x186)](),console[ae(0x186)](a0m[ae(0x204)](ae(0x1d7))),console[ae(0x186)](),console['log'](a0m[ae(0x243)](ae(0x1d6)+a0A(ae(0x1de)))),console[ae(0x186)]();}()),aa(0x210);if(y)return console['log'](),console[aa(0x186)](a0m[aa(0x1cf)]('\x20\x20'+a0A(aa(0x22b)))),console[aa(0x186)](a0m[aa(0x243)]('\x20\x20'+a0A(aa(0x1f2))+'\x20')+a0m['cyan'](aa(0x188))),console[aa(0x186)](a0m[aa(0x243)]('\x20\x20'+a0A(aa(0x276))+'\x20')+a0m[aa(0x204)](aa(0x1d9))),console[aa(0x186)](),aa(0x210);console[aa(0x186)](a0m[aa(0x243)]('\x20\x20'+a0A(aa(0x205))));try{const O=await g[aa(0x231)](p);process[aa(0x1df)][aa(0x25f)](aa(0x256)),r+=O[aa(0x270)],a0q(O[aa(0x270)]),console['log'](),a0x(O[aa(0x1c6)]),h(),console['log']();}catch(P){process['stdout'][aa(0x25f)](aa(0x256)),a0z(aa(0x1a1)+P[aa(0x23c)]),console[aa(0x186)]();}return aa(0x210);}export async function startExamAi4ctfChat(j,k){const af=a0j,{getRealExamState:p,saveExamState:q}=await import(af(0x266)),v=p();if(!v)return!(0x1f1+-0x30c+-0x47*-0x4);const w=v[af(0x1a3)]?.[af(0x251)]??-0x1*0x1cef+0x1*0x13cd+-0x14e*-0x7;if(w>=f)return console[af(0x186)](),console[af(0x186)](a0m[af(0x1cf)](af(0x212))),console['log'](a0m[af(0x243)](af(0x225))+a0m['white'](af(0x238)+j+af(0x1ff))),console['log'](),!(0xf09+0x24e6+-0x33ee);try{g=await a0p(void(-0xa5*-0x2c+-0xd5d*0x1+0xeff*-0x1),function(A,B){const ag=af;return'You\x20are\x20an\x20AI\x20teammate\x20helping\x20a\x20contestant\x20solve\x20an\x20ICOA\x202026\x20cybersecurity\x20exam\x20question.\x0a\x0aCURRENT\x20QUESTION\x20(Q'+B+',\x20'+A[ag(0x1ba)]+',\x20'+(A[ag(0x25c)]||0x888+0xaad*0x1+-0x132f)+'\x20points):\x0a'+A[ag(0x1c6)]+ag(0x1ed);}(k,j));}catch(A){return a0z(A[af(0x23c)]),!(-0x3*-0xb7+0x380*-0x8+0x4*0x677);}const x={};x[af(0x1f0)]=j,x[af(0x206)]=k,x[af(0x1fd)]=af(0x251),(s=!(-0x2434+0x2d4+0x2160),r=w,y=!(0x4e5*0x7+-0xb5*-0x2c+-0x20af*0x2),u=x);const z=v['answers']?.[j];return function(B,C,D){const ah=af,E=a0v()[ah(0x253)]||ah(0x220),F=r>-0xd37+0x32f*0x1+0xa08*0x1;console[ah(0x186)](),console[ah(0x186)](a0m[ah(0x1cb)][ah(0x200)](ah(0x199)+C+':\x20'+B[ah(0x1ba)]+ah(0x18e))),F&&console['log'](a0m['gray'](ah(0x21d))),D&&console[ah(0x186)](a0m[ah(0x243)]('\x20\x20Current\x20answer:\x20')+a0m[ah(0x1cf)](D)+a0m['gray'](ah(0x248))),console[ah(0x186)](),console['log'](a0m[ah(0x204)](ah(0x1aa))),console[ah(0x186)](a0m['cyan'](ah(0x219))+a0m[ah(0x200)][ah(0x1fc)]('Q'+C+ah(0x245)+B[ah(0x1ba)]+ah(0x1c2)+(B['points']||-0x207d+-0x2*-0xfa6+0x137*0x1)+ah(0x1fe))),console['log'](a0m[ah(0x204)](ah(0x264)));for(const G of String(B['text'])['split']('\x0a')){const H=G[ah(0x261)]>-0x591+0x22f7+-0x1d2a?G[ah(0x260)](-0x261c+-0x1*-0x145c+0x11c0,0x1f8a+-0x1*-0x1ba7+0x88*-0x6f)+'...':G;console[ah(0x186)](a0m[ah(0x204)](ah(0x219))+a0m[ah(0x1fc)](H));}console[ah(0x186)](a0m[ah(0x204)](ah(0x264))),console[ah(0x186)](a0m[ah(0x204)](ah(0x219))+a0m[ah(0x243)](ah(0x19a))+a0m[ah(0x1fc)](ah(0x1d0)+C)),console['log'](a0m[ah(0x204)]('\x20\x20│\x20')+a0m[ah(0x243)](ah(0x1a0))),console[ah(0x186)](a0m[ah(0x204)](ah(0x1e4))),console['log'](),console[ah(0x186)](a0m['bold']['white'](ah(0x1af))),console[ah(0x186)](),console[ah(0x186)](a0m[ah(0x1cf)](ah(0x24a))+a0m[ah(0x243)]('\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20A\x20gentle\x20nudge\x20(pre-written)')),console['log'](a0m[ah(0x1cf)](ah(0x236))+a0m[ah(0x243)]('\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Technique\x20hint\x20(pre-written)')),console[ah(0x186)](a0m[ah(0x1cf)](ah(0x21a))+a0m[ah(0x243)](ah(0x26f))),console[ah(0x186)](a0m[ah(0x1fc)](ah(0x240))+a0m[ah(0x243)](ah(0x20d))),console['log'](a0m[ah(0x1fc)](ah(0x1d8))+a0m['gray']('\x20\x20\x20\x20\x20Run\x20Python\x20in\x20shell')),console['log'](a0m[ah(0x243)](ah(0x235))),console[ah(0x186)](a0m[ah(0x243)](ah(0x247))+a0m[ah(0x1fc)](ah(0x1b0))),console['log'](),console[ah(0x186)](a0m[ah(0x243)]('\x20\x20\x20\x20exit')+a0m['gray'](ah(0x227))),console['log'](),h(),console[ah(0x186)](a0m[ah(0x243)](ah(0x232)+E+')')),console['log']();}(k,j,z),!(-0x1e*0x103+0x2214+-0x3ba);}const d=new Set();export function registerAi4ctfCommand(b){const ai=a0j;b['command']('ai4ctf')['description'](ai(0x21e))['action'](async()=>{const aj=ai;a0w(aj(0x251));const {getRealExamState:j}=await import(aj(0x266)),k=j(),m=k&&k[aj(0x196)][aj(0x226)](w=>w['number']>=0x1fc*0x1+0x2312+-0x24ef&&w[aj(0x267)]<=-0x21eb+0x5*-0x283+0x2ea0);if(k&&m){const w=k[aj(0x1c8)]||-0x2042*-0x1+0xf85+0x5*-0x98e;if(w<0x3*0x99b+-0x5bc*-0x5+-0xe*0x419||w>0x2102+-0xe09+-0x12d3*0x1)return console[aj(0x186)](),console['log'](a0m[aj(0x1cf)]('\x20\x20ai4ctf\x20is\x20available\x20on\x20Q31–38\x20(AI4CTF\x20section).')),console[aj(0x186)](a0m['gray']('\x20\x20You\x20are\x20on\x20Q'+w+aj(0x1ea))),console[aj(0x186)](a0m[aj(0x243)]('\x20\x20\x20\x20→\x20')+a0m[aj(0x200)]['cyan'](aj(0x254))),void console[aj(0x186)]();const x=k[aj(0x196)][aj(0x1f1)](z=>z[aj(0x267)]===w);return x?void await startExamAi4ctfChat(w,x):void a0z('Q'+w+'\x20not\x20found\x20in\x20state.\x20Try:\x20exam\x20q\x20'+w);}const p={};p[aj(0x1b7)]=aj(0x208),p[aj(0x1ba)]=aj(0x1a9);const q=a0v()['geminiModel']||aj(0x220),v=p;try{g=await a0p(v);}catch(z){return void a0z(z[aj(0x23c)]);}s=!(0x1765+-0x1*0x1f8e+0x829),r=0x1*-0x2239+-0x93f+-0xd*-0x358,y=!(-0x15e6*-0x1+-0x1b68+0x583),console[aj(0x186)](),console['log'](a0m['green'][aj(0x200)](aj(0x268)+a0A(aj(0x229))+aj(0x18e))),console[aj(0x186)](),console[aj(0x186)](a0m['white']('\x20\x20'+a0A('ai4ctfSample'))),console[aj(0x186)](),console['log'](a0m[aj(0x204)](aj(0x1aa))),console[aj(0x186)](a0m['cyan']('\x20\x20│\x20')+a0m[aj(0x200)]['white'](a0A(aj(0x262)))),console[aj(0x186)](a0m[aj(0x204)](aj(0x264))),console[aj(0x186)](a0m[aj(0x204)](aj(0x219))+a0m[aj(0x1fc)](a0A('ai4ctfIntercepted'))),console[aj(0x186)](a0m[aj(0x204)](aj(0x219))+a0m[aj(0x1cb)]('aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ==')),console[aj(0x186)](a0m[aj(0x204)](aj(0x264))),console[aj(0x186)](a0m[aj(0x204)]('\x20\x20│\x20')+a0m[aj(0x1fc)](a0A('ai4ctfDecode'))),console[aj(0x186)](a0m[aj(0x204)](aj(0x219))+a0m[aj(0x243)](aj(0x25b))),console[aj(0x186)](a0m[aj(0x204)](aj(0x1e4))),console[aj(0x186)](),console['log'](a0m[aj(0x1fc)]('\x20\x20'+a0A(aj(0x1e7)))),console[aj(0x186)](),console[aj(0x186)](a0m['yellow'](aj(0x24a))+a0m[aj(0x243)](aj(0x1d6)+a0A('ai4ctfHintA'))),console['log'](a0m[aj(0x243)](aj(0x259)+a0A(aj(0x1bd)))),console['log'](a0m[aj(0x1cf)](aj(0x236))+a0m[aj(0x243)](aj(0x1d6)+a0A(aj(0x257)))),console[aj(0x186)](a0m[aj(0x243)](aj(0x259)+a0A('ai4ctfHintBUses'))),console[aj(0x186)](a0m[aj(0x1cf)](aj(0x21a))+a0m[aj(0x243)](aj(0x1d6)+a0A(aj(0x1c7)))),console[aj(0x186)](a0m[aj(0x243)](aj(0x259)+a0A(aj(0x25a)))),console[aj(0x186)](),console[aj(0x186)](a0m['gray'](aj(0x19f))),console[aj(0x186)](a0m['bold']['white']('\x20\x20'+a0A(aj(0x1f4)))),console[aj(0x186)](aj(0x1d6)+a0m['cyan'](aj(0x26d))+a0m[aj(0x243)](aj(0x26e)+a0A('ai4ctfHintNudge'))),console[aj(0x186)](aj(0x1d6)+a0m[aj(0x204)]('hint\x20b')+a0m[aj(0x243)](aj(0x26e)+a0A('ai4ctfHintTechnique'))),console[aj(0x186)](aj(0x1d6)+a0m['cyan'](aj(0x24d))+a0m[aj(0x243)]('\x20\x20\x20→\x20'+a0A(aj(0x233)))),console[aj(0x186)](a0m[aj(0x243)](aj(0x1d6)+a0A('ai4ctfWelcomeNoReveal'))),console[aj(0x186)](),console[aj(0x186)](a0m[aj(0x1fc)]('\x20\x20'+a0A(aj(0x18b)))),console[aj(0x186)](a0m[aj(0x243)]('\x20\x20'+a0A(aj(0x21b))+'\x20')+a0m[aj(0x1fc)](aj(0x18d))),console[aj(0x186)](),console[aj(0x186)](a0m[aj(0x1cf)]('\x20\x20'+a0A(aj(0x1b2)))),console[aj(0x186)](a0m[aj(0x1fc)](aj(0x1f3))+a0m[aj(0x243)](a0A('ai4ctfCmdHintLine'))),console['log'](a0m['white'](aj(0x1e9))+a0m['gray'](a0A(aj(0x1ce)))),console[aj(0x186)](a0m[aj(0x1fc)](aj(0x250))+a0m[aj(0x243)](a0A(aj(0x272)))),console[aj(0x186)](a0m[aj(0x243)](aj(0x247))+a0m[aj(0x1fc)](aj(0x1b4))),console['log'](a0m['gray']('\x20\x20\x20\x20exit\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'+a0A(aj(0x237)))),console[aj(0x186)](),h(),console[aj(0x186)](a0m['gray']('\x20\x20'+a0A(aj(0x18f))+aj(0x1f7)+q+')')),console[aj(0x186)](a0m[aj(0x243)]('\x20\x20'+a0A(aj(0x23a)))),console[aj(0x186)]();});}