icoa-cli 2.19.100 → 2.19.102
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/ai4ctf.js +1 -700
- package/dist/commands/connect.js +1 -66
- package/dist/commands/ctf.js +1 -620
- package/dist/commands/ctf4ai-demo.js +1 -525
- package/dist/commands/env.js +1 -738
- package/dist/commands/exam.js +1 -2353
- package/dist/commands/files.js +1 -52
- package/dist/commands/hint.js +1 -119
- package/dist/commands/lang.js +1 -155
- package/dist/commands/log.js +1 -165
- package/dist/commands/note.js +1 -40
- package/dist/commands/ref.js +1 -68
- package/dist/commands/setup.js +1 -122
- package/dist/commands/shell.js +1 -55
- package/dist/commands/theme.js +1 -50
- package/dist/index.js +1 -225
- package/dist/lib/access.js +1 -246
- package/dist/lib/budget.js +1 -42
- package/dist/lib/colors.js +1 -21
- package/dist/lib/config.js +1 -60
- package/dist/lib/ctfd-client.js +1 -274
- package/dist/lib/demo-exam.js +1 -249
- package/dist/lib/demo-flags.js +1 -27
- package/dist/lib/demo-stats.js +1 -65
- package/dist/lib/exam-client.js +1 -57
- package/dist/lib/exam-setup.js +1 -23
- package/dist/lib/exam-state.js +1 -112
- package/dist/lib/gemini.js +1 -235
- package/dist/lib/i18n.js +1 -273
- package/dist/lib/log-sync.js +1 -110
- package/dist/lib/logger.js +1 -59
- package/dist/lib/paper-upgrade.js +1 -117
- package/dist/lib/platform.js +1 -86
- package/dist/lib/sandbox.js +1 -93
- package/dist/lib/terminal.js +1 -49
- package/dist/lib/theme.js +1 -108
- package/dist/lib/translation.js +1 -66
- package/dist/lib/ui.js +1 -80
- package/dist/lib/update-check.js +1 -102
- package/dist/postinstall.js +1 -48
- package/dist/repl.js +1 -1281
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -38
- package/package.json +6 -2
- package/translations/sw/i18n-snippet.ts +1 -0
package/dist/commands/ai4ctf.js
CHANGED
|
@@ -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(b,j){const a8=a0j,k=b();while(!![]){try{const m=-parseInt(a8(0x128))/(0x1*0x17d+0x1afe+-0x1c7a)+-parseInt(a8(0xc5))/(0x171b+-0x2*-0x4e8+0x151*-0x19)*(parseInt(a8(0x15e))/(-0x1ff3*0x1+0x32e+-0x8*-0x399))+parseInt(a8(0x1b6))/(-0x124c+-0x1528*0x1+0x8*0x4ef)*(-parseInt(a8(0x1c3))/(0x2351+0x1f7*0x1+0x2543*-0x1))+-parseInt(a8(0x15c))/(0x16*0x10d+-0x1cab+0x593)*(-parseInt(a8(0x19a))/(0x2670+-0x13*0xd0+-0x16f9))+-parseInt(a8(0x10c))/(0x3*-0xb49+0xbe5+0x15fe)*(-parseInt(a8(0x11b))/(-0x1386+-0x1daf*-0x1+-0xa20))+-parseInt(a8(0x1c2))/(-0x1ef+-0xe75*0x1+-0x3*-0x57a)*(parseInt(a8(0xe7))/(0x2263+-0x1*-0x120f+-0x3467))+parseInt(a8(0x14d))/(-0x20ae*0x1+0x66f*-0x4+0x3a76);if(m===j)break;else k['push'](k['shift']());}catch(p){k['push'](k['shift']());}}}(a0b,0x2*-0x7daa+-0x86b0+0x3ba93));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';function a0j(a,b){a=a-(-0x18d7+0x22d5+-0x940);const c=a0b();let d=c[a];if(a0j['YJSBEg']===undefined){var e=function(i){const j='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let l='',m='';for(let n=-0x1*0x24af+-0x1a49+0x136*0x34,o,p,q=0x7fc*-0x1+-0x1436+0x1c32;p=i['charAt'](q++);~p&&(o=n%(-0x1*0x2659+0x120+-0x253d*-0x1)?o*(-0x47e+-0x147+0x1*0x605)+p:p,n++%(-0x23e6+0x1*0xdf3+-0x1*-0x15f7))?l+=String['fromCharCode'](-0x159*0xe+-0x164a+0x2a27&o>>(-(0x1289+0x1b6*0xc+-0x38d*0xb)*n&0x866+-0x111d*-0x1+-0x197d)):-0xeaa+0x152f*-0x1+-0x1*-0x23d9){p=j['indexOf'](p);}for(let r=0xbf7+0x4dd+0x167*-0xc,s=l['length'];r<s;r++){m+='%'+('00'+l['charCodeAt'](r)['toString'](0x46*-0x5f+0x14dd+0x52d*0x1))['slice'](-(-0x283*0x9+-0x47f+0x1b1c));}return decodeURIComponent(m);};a0j['ilDiSt']=e,a0j['IYVhnN']={},a0j['YJSBEg']=!![];}const f=c[0xa31*-0x2+0x3*-0xc61+0x3985],g=a+f,h=a0j['IYVhnN'][g];return!h?(d=a0j['ilDiSt'](d),a0j['IYVhnN'][g]=d):d=h,d;}import{printMarkdown as a0x,printError as a0z}from'../lib/ui.js';function a0b(){const ak=['Bg9N','ywK0y3rMuMv2zwfStg9JA05VDgu','icdILiiG','yvDoDLLyDdnnmNHQtuCXBfH6sMzzv2SWwtnsBwzrpt0','AgLUDcbI','Aw50zxjHy3rPB25Z','rNvSBcbXDwvZDgLVBJOGia','mtf1s0zsDw0','ifn1yM1PDcb5B3vYigzSywCGzM9YihrOAxmGCxvLC3rPB24','icaGifn0DwnRpYbuCNK6ia','ywK0y3rMrxHPDe5LEhruAxrSzq','icbbzNrLCIbLEgL0oIbbstrdveyGC2vJDgLVBIbJB21WBgv0zs4GtMv4DdOG','ywK0y3rMsgLUDei','ywK0y3rMvgL0Bgu','imk3ia','ChvZAa','g1SXqrTBmKS','iMHVDYbKBYbjigrLy3j5ChqGquvtluncqYbPBIbqExrOB24/iG','Dg9ju09tDhjPBMC','lI4U','zw5JB2rPBMC','rMXHzYbMB3jTyxq6igLJB2f7lI4UFq','igfNywLUihrVignOyw5NzsWGB3iG','ywK0y3rMuMv2zwfSqM9KEq','icaGia','icaGigHPBNqGysaVigiGlYbJica','icbr','vg9Rzw5Z','icbfCNjVCJOG','BwLU','icaGicaGicaGicaGica','D2HPDgu','C3vIBwL0igLJB2f7DZnSyZbTzv8Yx2fPngn0zN0','ywK0y3rMrMXHz0HPBNq','icaGicaGicbPy29HE3CZBgmWBwvFmL9HAtrJDgz9','ywrK','ywK0y3rMrxHPDenTze5LEhq','ywK0y3rMu3vIBwL0q21K','icdcTYaGuMuTzw50zxi6ia','q2HHDcb3AxrOihLVDxiGquKGDgvHBw1HDgu','BNvTyMvY','ywK0y3rMt3jdAgf0','CM91BMq','icaGigHPBNqGyG','mtC2mdGWvez3tg1J','ywK0y3rMt3jdAgf0rxHHBxbSzq','zMLUza','icdILii','DhjPBq','BMfTzq','ywK0y3rMrxHPDe5LEhrcB2r5','Cu51Bq','ig5VDcbMB3vUzcbPBIbZDgf0zs4Gvhj5oIbLEgfTiheG','icbdDxjYzw50igfUC3DLCJOG','ywK0y3rMrw5Ku2vZC2LVBG','ywK0y3rMq21KsgLUDeXPBMu','y29UDgLUDwu','C2XPy2u','CMvK','ovv4zMzxyG','ue9tva','icaOCMvZDw1PBMCG4OcuihbYAw9YignOyxqGAxmGBM90ihjLBwvTyMvYzwqSigj1Dcb0B2TLBNmGywXYzwfKEsb1C2vKihn0yxKGzgvKDwn0zwqP','ywK0y3rM','y3LHBG','iIbSB29RCYbSAwTLigfUie1dusbSzxr0zxiSig5VDcbHigzSywCU','zgvZy3jPChrPB24','C3bSAxq','ywK0y3rMsgLUDfrLy2HUAxf1zq','ywK0y3rMv3jHCff1B3rHrM9VDgvY','q29TBwfUzcbMywLSzwq','ywK0y3rMtg9JA2vKvgHLBG','ywK0y3rMq21Ku2HLBgXmAw5L','mtu3mteYufH2zNPn','icaGihn1yM1PDca8zMXHzZ4Gica','icaGiokgKIa','ig9UifeZoq','z3jLzw4','ywK0y3rMtw9KzwW','ww91igfYzsbHBIbbssb0zwfTBwf0zsbOzwXWAw5NigeGy29UDgvZDgfUDcbZB2X2zsbHBIbjq09bidiWmJyGy3LIzxjZzwn1CML0EsbLEgfTihf1zxn0Aw9UlGOkq1vsuKvovcbrvuvtveLptIaOuq','icbgBgfNigzVCM1HDdOG','C3rKB3v0','DgLTzw91Da','ieLdt0f7lI4UFq','icaGicaGicaGicaGicaGicaGicbLlMCUia','BM9KztPJAgLSzf9WCM9JzxnZ','icdILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','ywK0y3rMv3jHCff1B3rHquHPBNq','icbbssbIDwrNzxqGzxHOyxvZDgvKigzVCIbbstrdveyGC2vJDgLVBI4','CMvWBgfJzq','ig5VDcbMB3vUzcbPBIbZDgf0zs4','ywK0y3rMsgLUDencB2r5','z2vTBweTnc0ZmwiTAxq','y2f0y2G','Bwf0y2G','ywK0y3rMsgLUDee','cIaG','yw5ZD2vYx3n1yM1PDhrLza','AgfZ','C2vUze1LC3nHz2u','BwvUDq','icaI','icdILRGGsgLUDca','icaGicaGicaGicaGieXLyxzLignOyxqSihjLDhvYBIb0BYbLEgfT','oIbhB29NBguGr2vTBweGncaO','ywK0y3rMrxHPDe5LEhrtDwi','icbzB3uGyxjLig9Uife','CxvLC3rPB25Z','quKGzxjYB3i6ia','icaGie5VihbYzs13CML0DgvUigHPBNqGyxqGDgHPCYb0AwvYigzVCIb0AgLZihf1zxn0Aw9UlG','ntK0nJCWoeXMAvnyDW','icbbstrdveyGC2vJDgLVBIbJB21WBgv0zsdIGjqG','DgHPBMTPBMC','BwvZC2fNzq','DgvZDa','y2XLyxi','ywLvC2fNzq','z2vTAw5Ptw9KzwW','C3rHCNrZv2L0Aa','yw5ZD2vYCW','sunpqxT5B3vYx2zSywD9','ywK0y3rMv3jHCff1B3rHqG','ywK0y3rMvgHPBMTPBMC','ywK0y3rMv3jHCeXPBMuYqM9KEq','ywK0y3rMv3jHCe5VuhjVzW','mtuYota0uuT0yKvp','ixnOzwXS','mtaYndq0wenpywzf','sgLUDca','ihrVA2vUCYbSzwz0lG','AgLUDcbJ','icbozxH0oIa','icaGicbsDw4GuhL0Ag9UigLUihnOzwXS','ywn0Aw9U','ywK0y3rMsgLUDem','icaGigHPBNqGyq','BgvUz3rO','ywK0y3rMuMvWB3j0','iwvJAg8GyvDoDI4UlIb8igjHC2u2ncaTza','cGPzt1vsifjvtevtoGOTifLVDsbnqvKGzxHWBgfPBIbJB25Jzxb0CYWGzgvZy3jPyMuGyxbWCM9Hy2HLCYWGyw5KigHLBhaGDgHLignVBNrLC3rHBNqGCMvHC29UihrOCM91z2GGDgHLihbYB2jSzw0UcI0Gww91ie1bwsbKAxnJDxnZihrOzsb0zwnOBM9SB2D5igLUDM9SDMvKicHbrvmSihb3BNrVB2XZlcbqExrOB24GC3rYDwn0lcbivfrqlcbLDgmUks4klsbzB3uGtufzigHLBhaGzgvIDwCGuhL0Ag9UignVzguGDgHLignVBNrLC3rHBNqGD3jPDgvZlGOTifLVDsbnvvnuie5pvcbYzxzLywWGDgHLigzSywCGzgLYzwn0BhKUcI0Gww91ie1vu1qGtK9uig91Dhb1DcbHBNKGC3rYAw5Nig1HDgnOAw5NieLdt0f7lI4UFsbVCIbPy29HEY4UlN0UcI0Gww91ie1vu1qGtK9uihrLBgWGDgHLignVBNrLC3rHBNqGDgHLigv4ywn0igfUC3DLCIb0BYb0AgLZihf1zxn0Aw9UlGOTifLVDsbnqvKGC3vNz2vZDcb3AgLJAcb0B29Sic8GBgLICMfYEsaVihrLy2HUAxf1zsb0BYb1C2uUcI0Gs2vLCcbYzxnWB25ZzxmGy29Uy2LZzsbHBMqGChjHy3rPy2fSlIbuAgLZignVBNrLC3rHBNqGAgfZigXPBwL0zwqGDgLTzs4','icaGideUia','ihb0CW','ywK0y3rMuMv2zwfSvgHLBLn1yM1PDa','icaGicaGigHPBNqGysaGia','Dgv4Da','icaGigv4Axq','ywK0y3rMq2HHBgXLBMDL','q29UDgvUDc1uExbL','C29Tzq','ywK0y3rMv3jHCeXPBMuXsgvHza','rMXHzYbMB3jTyxq6icaGieLdt0f7lI4UFq','AgLUDhm','Dg9Rzw5ZvxnLza','DxrMltG','icbiB3CGDg8GD29YAYb0AgLZihf1zxn0Aw9U','ywK0y3rMrgvJB2rLza','ywK0y3rMv3jVBMDgBgfN','E25VuhjVz30','sgLKzgvUie1LC3nHz2u','icaGicfWExrOB24Zic4UlG','icdILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','icaGicaGicaGicbuzwnOBMLXDwuGAgLUDcaOChjLlxDYAxr0zw4P','icaGidiUia','y3rMngfP','ywK0y3rMrgvJB2rL','CxvLC3rPB24','icdILRGG','AM9PBG','icbzB3uGy2fUihn0AwXSoIa','icboB3rLoIa1mcuGB2yGC2vJDgLVBIbbssbIDwrNzxqGDxnLzc4','ywK0y3rMtg9JA2vKvgL0Bgu','ywK0y3rMsgLUDfbYAw5JAxbSzq','ywK0y3rMsgLUDefcB2r5','zxHPDa','CxvPDa','yxbWBgLJyxrPB24VANnVBG','xsaGWRCG','icaGicaGigHPBNqGyYaGia','Dg9vChbLCKnHC2u','iwvJAg8GyvDoDLLyDdnnmNHQtuCXBfH6sMzzv2SWwtnsBwzrpt0GFcbIyxnLnJqGlwq','lI4VBgLIl2v4yw0TC3rHDguUANm','icbbzNrLCIbLEgL0oIa','icbHAtrJDgyGAxmGyxzHAwXHyMXLig9UifeZmEkaKZm4icHbstrdveyGC2vJDgLVBIKU','ywK0y3rMsgLUDefvC2vZ','ywK0y3rMsgLUDe51zgDL','icaGigHPBNqGyW','icaGifjLywXSEsbZDhvJAZ8Gvhj5oIa','ndLwtM1pveW','icbtDgLSBcbHDMfPBgfIBgu6ia','icdIMQaGquK0q1rgihrVA2vUigj1zgDLDcbLEgHHDxn0zwqGkdi1ldaWmcb1C2vKks4','icdILzdILzdILzaG','iokaLcb0ExbLia','ywK0y3rMq29YCMvJDezSywC','icdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILzdILza','EwvSBg93','ywK0y3rMv2vSy29Tzu5VuMv2zwfS','icaGicbJDgy0ywK','AgLUDcbH','Ahr0Chm6lY9WCMfJDgLJzs5Py29HmJaYnI5HDs9HCgKVAwnVys9Kzw1Vlxn0yxrZ','Dg9mB3DLCKnHC2u','icaKia','ywK0y3rMsgLUDejcB2r5','iokvKokvKokvKa','ywK0y3rMv3jHCfrPDgXL','yw5ZD2vYx2nOyw5Nzwq','icbzB3uGy2fUihn0AwXSihn1yM1PDcbHigzSywCGzgLYzwn0BhK6ia','ywK0y3rMv3jHCeXPBMuYsgvHza','DxnHz2vgAwvSza','igzVCIbrmZK','C3rYAw5NAwz5','CMvWzwf0','icaG4OAsia','DMLHigfPngn0zIbJAgf0','ywK0y3rMv291BgrfyxjU','ihrVignVBNrPBNvL','nejls1brwG','AwnVyxT3m2XJmg1LxZjFywK0y3rMFq','y29TBwfUza','icaOC3vIBwL0igfNywLUihrVignOyw5NzsK','yM9Sza','icaGicaGicbZDwjTAxqGAwnVyxT3m2XJmg1LxZjFywK0y3rMFq','y2f0zwDVCNK','ywK0y3rMv3jHCeXPBMuXqM9KEq','zxHHBsbXia','ywK0y3rMu2fTCgXL','icdILiZILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILidILia','ywK0y3rMv3jHCff1B3rHqq','mta1mZm1mhbkDuXXDW','nde1mZu1zKnxr2fL','icbnB2rLBdOGr29Vz2XLieDLBw1HidqGka','icbuAgLUA2LUzY4UlG','icdIMQaG','ywK0y3rMvg9Rzw5Z','ywK0y3rMtgv2zwXZ','igfUC3DLCIb1CgrHDgvKoIa','ywK0y3rMsgLUDe5LEhrc','iokaLca','mtj2qNrJBMe','icaGice8C2HLBgWGy21KpIaGica','icdIMQaGodaLig9MihnLy3rPB24GquKGyNvKz2v0ihvZzwqU','ywK0y3rMsgLUDenvC2vZ','ywK0y3rMv3jHCe5LEhruAxrSzq','iNDOyxqGzw5JB2rPBMCGAxmGDgHPCZ8I','ywK0y3rMrM91BMrgBgfN','Cg9PBNrZ','icaGicaGicaGicaG','ihjLy29YzgvKoIa','icaGifbYzxzPB3vZoIa','ywK0y3rMv3jHCe5LEhrtDwi','Dg9Rzw5mAw1PDa','ywK0y3rMsgLUDe5LEhrb','D3jPDgu','z3jHEq','CgfKrw5K','icaGicaGicaGicfLy2HVigfxtNzzwhqZttj4AK1hmwXyEKPMwvDRmfKZuM1Mut09ihWGyMfZzty0ic1K','C3vIBwL0idXMBgfNpG','zxHHBsbXidmX','icaGicaGicaGia','icdINjmGuq','icaGicbIywnR','x2XHC3rr','icdILzdILzdILzaGquK0q1rgiokaLcbr','yMfJAW','ihbVAw50CYK6cG'];a0b=function(){return ak;};return a0b();}import{t as a0A}from'../lib/i18n.js';const i=b=>new Promise(j=>setTimeout(j,b));let s=!(0x1cf*-0x2+-0xf72+0x1311),g=null,r=0x1e8e+0x25cc+-0x445a,y=!(0x120+0x11a5+-0x4b1*0x4);const f=-0x1676+-0x663+0x5*0x194d;let u=null;export function isChatActive(){return s;}export function isExamAi4ctfChatActive(){return s&&null!==u;}function h(){const a9=a0j,b=u?f:-0x23e6+0x1*0xdf3+-0x1*-0x297b,j=r,k=Math[a9(0xfd)](Math['round'](j/b*(-0x159*0xe+-0x164a+0x298c)),0x1289+0x1b6*0xc+-0x26ad*0x1),m=Math['round'](k/(0x866+-0x111d*-0x1+-0x191f)*(-0xeaa+0x152f*-0x1+-0x1*-0x23ed)),p=0xbf7+0x4dd+0x43*-0x40-m,q=k>0x46*-0x5f+0x14dd+0x56d*0x1?a0m[a9(0x11a)]:k>-0x283*0x9+-0x47f+0x1b4c?a0m[a9(0x1a1)]:a0m[a9(0x12c)],v=u?'AI4CTF\x20section':a9(0xfb);console[a9(0xe0)](a0m[a9(0xd4)]('\x20\x20'+v+':\x20')+q('█'[a9(0x1b1)](m))+a0m[a9(0xd4)]('░'['repeat'](p))+a0m[a9(0xd4)]('\x20'+j+'/'+b+'\x20('+k+'%)'));}export async function handleChatMessage(p){const aa=a0j;if(!g)return aa(0x18c);if(a0w('ai4ctf:\x20'+p),u)return async function(z){const ab=aa;if(!u||!g)return'exit';const A=z[ab(0x110)](),B=A[ab(0x1a6)](),C=B['match'](/^hint\s+([abc])$/);if(C){const E=C[0xa31*-0x2+0x3*-0xc61+0x3986][ab(0x191)](),F=u[ab(0x184)][ab(0x176)],G=F&&F[E],H='A'===E?a0m[ab(0x12c)]:'B'===E?a0m['yellow']:a0m[ab(0x11a)];if(console[ab(0xe0)](),console[ab(0xe0)](H[ab(0x1ba)](ab(0x145)+E)),console[ab(0xe0)](),G){for(const I of String(G)['split']('\x0a'))console[ab(0xe0)](a0m['white'](ab(0xf8)+I));}else console['log'](a0m[ab(0xd4)](ab(0x14c)));return console[ab(0xe0)](),'A'===E?console['log'](a0m[ab(0xd4)](ab(0xe9))+a0m[ab(0x11f)](ab(0xe4))):'B'===E&&console['log'](a0m['gray'](ab(0x199))+a0m[ab(0x11f)]('hint\x20c')),console['log'](),'continue';}const D=A[ab(0x13d)](/^submit\s+(.+)/i);if(D){const J=D[-0x961+-0x7ef*-0x2+-0x67c][ab(0x110)]();if(/^[A-Da-d]$/[ab(0x151)](J))return console['log'](),console[ab(0xe0)](a0m[ab(0x1a1)](ab(0x144)+J+ab(0x120))),console[ab(0xe0)](a0m['gray'](ab(0x12f))+a0m['green'](ab(0x157))+a0m[ab(0xd4)]('.\x20Try\x20again\x20inside\x20this\x20chat.')),console['log'](),ab(0x118);const {getExamState:K,saveExamState:L}=await import(ab(0x193)),M=K();if(!M)return'exit';if(!M[ab(0x14a)]['find'](P=>P['number']===u[ab(0x113)]))return console[ab(0xe0)](a0m[ab(0x11a)](ab(0xfa)+u[ab(0x113)]+ab(0x139))),ab(0x118);const N=M[ab(0x156)][u[ab(0x113)]];M[ab(0xe5)]||(M[ab(0xe5)]=[]),M[ab(0xe5)][ab(0xef)]({'ts':new Date()[ab(0xf2)](),'q':u[ab(0x113)],'type':N?ab(0x1ab):ab(0x140),'input':J,'result':ab(0x1b3)}),M['answers'][u[ab(0x113)]]=J;const O=u[ab(0x113)]+(-0x4fd*0x6+0x76a+0x1685);return M['_lastQ']=O<=0x597+-0xb1c*-0x2+0x1ba9*-0x1?O:u['qNum'],L(M),console[ab(0xe0)](),N?(console[ab(0xe0)](a0m['green'](ab(0xda)+u[ab(0x113)]+ab(0xc2))+a0m['yellow'](J)),console[ab(0xe0)](a0m['gray'](ab(0xcf)+N))):console['log'](a0m[ab(0x12c)][ab(0x1ba)]('\x20\x20✓\x20Answer\x20for\x20Q'+u['qNum']+ab(0xce)+J)),console['log'](a0m[ab(0xd4)]('\x20\x20\x20\x20(Grading\x20happens\x20at\x20exam\x20submit\x20—\x20you\x20cannot\x20preview\x20correctness\x20during\x20the\x20exam.)')),console[ab(0xe0)](),console['log'](a0m['gray']('\x20\x20')+a0m[ab(0xff)]('submit\x20ICOA{...}')+a0m[ab(0xd4)](ab(0xf6))+a0m[ab(0xff)](ab(0x18c))+a0m[ab(0xd4)]('\x20to\x20move\x20on')),O<=0x239d+0x2cf+-0x2646?console[ab(0xe0)](a0m[ab(0xd4)](ab(0x194))+a0m[ab(0x11f)](ab(0x11e))+a0m[ab(0xd4)]('\x20will\x20open\x20Q'+O)):console[ab(0xe0)](a0m['gray'](ab(0xeb))+a0m[ab(0x11a)](ab(0x182))+a0m[ab(0xd4)](ab(0x12b))),console['log'](),ab(0x118);}if(z[ab(0x155)]('!')){const P=z[ab(0x119)](0x7*-0x33c+-0x1989+0x302e)['trim']();if(!P)return'continue';try{const Q={};Q['encoding']='utf-8',Q[ab(0x131)]=0x2710;const {execSync:R}=await import('node:child_process'),S=R(P,Q)[ab(0x110)]();console['log'](),console[ab(0xe0)](a0m[ab(0xd4)]('\x20\x20$\x20')+a0m[ab(0xff)](P)),console[ab(0xe0)](a0m[ab(0xff)]('\x20\x20'+S[ab(0x122)]('\x0a')[ab(0x186)]('\x0a\x20\x20'))),console[ab(0xe0)]();}catch(T){console[ab(0xe0)](),console['log'](a0m[ab(0x11a)](ab(0xfc)+(T[ab(0x150)]?.[ab(0x122)]('\x0a')[-0x1*0x25b1+-0x1a21*-0x1+-0xb90*-0x1]||ab(0x125)))),console[ab(0xe0)]();}return ab(0x118);}if(ab(0x18c)===B||ab(0xde)===B||'quit'===B){const U=u[ab(0x113)],{getExamState:V}=await import(ab(0x193)),W=V(),X=W&&null!=W['answers'][U],Y=W?.[ab(0xdc)]||U;return s=!(0x2112+-0x13e*0x16+0x1*-0x5bd),g=null,u=null,d[ab(0x152)](),console[ab(0xe0)](),console['log'](a0m['gray']('\x20\x20AI4CTF\x20chat\x20ended\x20for\x20Q'+U+'.')),X&&Y>U&&Y<=-0x1769+-0x1743+0x2ed2?console[ab(0xe0)](a0m['white'](ab(0x162))+a0m['bold'][ab(0x12c)]('Q'+Y)+a0m[ab(0xd4)](ab(0x19e))+a0m[ab(0x11f)](ab(0x11e))+a0m[ab(0xd4)](ab(0x1b5))):X&&U>=-0x395+-0x1e97+0x2252?console[ab(0xe0)](a0m[ab(0xff)](ab(0x14e))+a0m['bold']['red'](ab(0x182))+a0m[ab(0xd4)](ab(0x1af))):console[ab(0xe0)](a0m['gray']('\x20\x20Resume:\x20')+a0m[ab(0xff)](ab(0x1be)+U)+a0m[ab(0xd4)](ab(0x106))+a0m['white'](ab(0x11e))),console['log'](),'exit';}if(y)return console[ab(0xe0)](),console[ab(0xe0)](a0m[ab(0x1a1)](ab(0x137))),console['log'](a0m[ab(0xd4)](ab(0x19b))+a0m['white'](ab(0xd7))+a0m[ab(0xd4)]('\x20·\x20')+a0m[ab(0xff)](ab(0x15d))+a0m[ab(0xd4)](ab(0xee))+a0m[ab(0xff)](ab(0x18c))),console['log'](),'continue';if(r>=f)return y=!(0x42e*0x5+-0x16d5+0x2d*0xb),console[ab(0xe0)](),console['log'](a0m[ab(0x11a)][ab(0x1ba)](ab(0x19c))),console[ab(0xe0)](a0m['gray'](ab(0x187))+a0m[ab(0xff)]('submit\x20<flag>')+a0m['gray'](ab(0xee))+a0m['white']('!shell')+a0m[ab(0xd4)](ab(0xee))+a0m[ab(0xff)](ab(0x18c))),console[ab(0xe0)](),'continue';console['log'](a0m[ab(0xd4)](ab(0xbe)));try{const Z=await g[ab(0x142)](z);process[ab(0x130)][ab(0xd3)]('\x1b[1A\x1b[2K'),r+=Z[ab(0x177)];const {getRealExamState:a0,saveExamState:a1}=await import(ab(0x193)),a2=a0(),a3={};a3['ai4ctf']=0x0,a3['ctf4ai']=0x0,(a2&&(a2[ab(0x153)]||(a2[ab(0x153)]=a3),a2[ab(0x153)]['ai4ctf']=r,a1(a2)),console[ab(0xe0)](),a0x(Z[ab(0x16f)]),h(),function(a4,a5,a6){const ac=ab,a7=a4/a5*(0x1fe5+-0xf1*0x1d+0x434*-0x1);a7>=0x2083+0x25cd*0x1+0x45f1*-0x1&&!a6['has']('95')?(console[ac(0xe0)](a0m['red'][ac(0x1ba)](ac(0xbf)+Math[ac(0x10a)](a7)+'%\x20of\x20section\x20AI\x20budget\x20used\x20—\x20only\x20~'+(a5-a4)+ac(0x160))),a6[ac(0x103)]('95')):a7>=-0xb88+0x2*0x665+-0x1*0xf2&&!a6['has']('80')?(console[ac(0xe0)](a0m[ac(0x1a1)](ac(0xc7))),a6[ac(0x103)]('80')):a7>=0x1a5*0x10+0x16ea+-0x3108&&!a6[ac(0x141)]('50')&&(console[ac(0xe0)](a0m[ac(0xd4)](ac(0x188))),a6[ac(0x103)]('50'));}(r,f,d),console['log']());}catch(a4){process['stdout']['write'](ab(0xf0)),a0z(ab(0x14b)+a4['message']),console['log']();}return ab(0x118);}(p);const q=p[aa(0x110)]()[aa(0x1a6)]()['match'](/^hint\s+([abc])$/);if(q)return function(z){const ad=aa,A=a0A('a'===z?ad(0x13e):'b'===z?ad(0xec):ad(0x165)),B=a0A('a'===z?ad(0x18b):'b'===z?ad(0x1a8):ad(0x13a)),C=ad(0x15f)+z[ad(0x191)](),D='a'===z?a0m[ad(0x12c)]:'b'===z?a0m[ad(0x1a1)]:a0m['red'];console[ad(0xe0)](),console[ad(0xe0)](D[ad(0x1ba)](ad(0x185)+C+'\x20\x20')+a0m[ad(0xd4)](A)),console['log']();for(const E of B[ad(0x122)]('\x0a'))''===E?console[ad(0xe0)]():console[ad(0xe0)](a0m[ad(0xff)](ad(0xf8)+E));console[ad(0xe0)](),'a'===z?(console['log'](a0m['gray'](ad(0xf8)+a0A(ad(0xd2))+'\x20')+a0m[ad(0x11f)](ad(0xe4))),console['log']()):'b'===z&&(console[ad(0xe0)](a0m['gray'](ad(0xf8)+a0A(ad(0xc3))+'\x20')+a0m['cyan'](ad(0x161))),console[ad(0xe0)]());}(q[-0x1aa6+0x6b9*0x2+0x15*0xa1]),'continue';const v=p[aa(0x13d)](/^submit\s+(.+)/i);if(v){if(aa(0x1b7)===v[-0x7*0x379+0x17a4+0x2b*0x4][aa(0x110)]()){console['log'](),console[aa(0xe0)](a0m[aa(0x12c)]['bold'](aa(0x1a0))),console[aa(0xe0)](a0m[aa(0x12c)][aa(0x1ba)]('\x20\x20'+a0A(aa(0x19f)))),console[aa(0xe0)](a0m['green'][aa(0x1ba)]('\x20\x20════════════════════════════════════')),console[aa(0xe0)](),console[aa(0xe0)](a0m['white']('\x20\x20'+a0A(aa(0x17a)))),console[aa(0xe0)](a0m[aa(0xd4)]('\x20\x20'+a0A(aa(0x1b4)))),console[aa(0xe0)](),h(),s=!(0x23dd+0x14bb+-0x3897),g=null,fetch(aa(0x1a5),{'method':'POST','headers':{'Content-Type':aa(0x18e)},'body':JSON['stringify']({'type':aa(0x11e),'solved':!(0x6*0x62a+-0x10c8+-0x35e*0x6),'tokensUsed':r,'timestamp':new Date()[aa(0xf2)]()}),'signal':AbortSignal[aa(0x131)](0x100e+0x1*-0x7ae+-0x18*-0x77)})[aa(0x13c)](()=>{}),await i(-0x4*-0x5a6+0x177d+-0x1*0x2839),console['log'](),console[aa(0xe0)](a0m[aa(0x11f)](aa(0x17f))),console['log'](a0m[aa(0x1ba)][aa(0xff)]('\x20\x20'+a0A(aa(0x1aa)))),console['log'](a0m['cyan'](aa(0x17f))),console[aa(0xe0)]();const z=a0A(aa(0x174)),A=a0A(aa(0x1bd));console[aa(0xe0)](a0m['white'](aa(0x16b)+z+'\x20')+a0m['gray'](A[aa(0x122)]('\x0a')[-0xdfc+-0x5*-0x9f+0xae1]||''));for(const E of A[aa(0x122)]('\x0a')['slice'](0x1*0x3fd+-0x11*-0x93+0x495*-0x3))E['trim']()&&console[aa(0xe0)](a0m[aa(0xd4)]('\x20\x20\x20\x20\x20\x20\x20'+E[aa(0x138)](/\{thinking\}/g,aa(0x14f))));console[aa(0xe0)](),console[aa(0xe0)](a0m[aa(0xff)](aa(0x181)+a0A(aa(0x1ad))+'\x20')+a0m[aa(0xd4)](a0A(aa(0x15a)))),console[aa(0xe0)](),console[aa(0xe0)](a0m[aa(0x11f)](aa(0xd6))),console['log'](),await i(0x4da+0x3*-0xbd5+-0x7b1*-0x5),console[aa(0xe0)](a0m[aa(0x11f)](aa(0x17f))),console['log'](a0m['bold']['white']('\x20\x20'+a0A('ai4ctfWrapQuotasTitle'))),console[aa(0xe0)](a0m[aa(0x11f)](aa(0x17f))),console[aa(0xe0)](),console[aa(0xe0)](a0m[aa(0x1a1)](aa(0x16e))+a0m['white'](a0A(aa(0x1c1))[aa(0xd5)](-0xe78+0x4*-0x47+0x9*0x1be))+a0m['gray'](a0A(aa(0x136)))),console[aa(0xe0)](a0m[aa(0x1a1)]('\x20\x20\x20\x20\x20\x20\x20hint\x20b\x20\x20\x20')+a0m[aa(0xff)](a0A(aa(0x158))['padEnd'](-0x1dbe+-0xa01+0x27d9*0x1))+a0m['gray'](a0A('ai4ctfWrapQuotaBHint'))),console[aa(0xe0)](a0m[aa(0x1a1)](aa(0x190))+a0m['white'](a0A('ai4ctfWrapQuotaC')[aa(0xd5)](-0x38d+0x18*-0x23+0x47*0x19))+a0m[aa(0xd4)](a0A('ai4ctfWrapQuotaCHint'))),console[aa(0xe0)]();for(const F of a0A(aa(0x124))[aa(0x122)]('\x0a'))console[aa(0xe0)](a0m[aa(0xd4)](aa(0xf8)+F));console[aa(0xe0)](),await i(-0x24bf+0x236+0x2a59),console['log'](a0m[aa(0x11f)](aa(0x17f))),console['log'](a0m[aa(0x1ba)]['white']('\x20\x20'+a0A(aa(0xc9))+'\x20')+a0m['red'][aa(0x1ba)](aa(0x182))+a0m[aa(0xff)](aa(0xc4)+a0A(aa(0xd0)))),console[aa(0xe0)](a0m[aa(0x11f)](aa(0x17f))),console['log']();const B=a0A('ai4ctfWrapNextBody'),C=a0A(aa(0x15b)),D=B['split']('\x0a');if(D[0x26c9*-0x1+0x2*-0xf33+0x452f]){const [G,H]=D[-0x50c+0x1*0x1871+-0x1*0x1365][aa(0x122)](aa(0x17c));console[aa(0xe0)](a0m[aa(0xff)]('\x20\x20\x20\x20'+(G||''))+a0m['bold'][aa(0xff)](C)+a0m[aa(0xff)](H||''));}for(const I of D['slice'](-0x3*-0x932+0x3a9+-0x1f3e))I[aa(0x110)]()&&console['log'](a0m[aa(0xff)]('\x20\x20\x20\x20'+I));return console['log'](),console[aa(0xe0)](a0m[aa(0xd4)](aa(0xf8)+a0A('ai4ctfWrapTypeCtf4ai')+'\x20')+a0m[aa(0x1ba)][aa(0x11a)](aa(0x182))),console[aa(0xe0)](),'exit';}return console[aa(0xe0)](),console[aa(0xe0)](a0m['red']('\x20\x20'+a0A(aa(0x17b)))),console['log'](a0m[aa(0xd4)]('\x20\x20'+a0A(aa(0x101)))),console[aa(0xe0)](),aa(0x118);}if(p[aa(0x155)]('!')){const J=p['slice'](0xa0d*0x1+0x4*-0x25b+-0xa0)['trim']();if(!J)return aa(0x118);try{const K={};K[aa(0xf4)]=aa(0x178),K[aa(0x131)]=0x2710;const {execSync:L}=await import(aa(0x134)),M=L(J,K)[aa(0x110)]();console[aa(0xe0)](),console[aa(0xe0)](a0m[aa(0xd4)](aa(0x1a7))+a0m['white'](J)),console[aa(0xe0)](a0m[aa(0xff)]('\x20\x20'+M[aa(0x122)]('\x0a')[aa(0x186)](aa(0x13f)))),console[aa(0xe0)](),console[aa(0xe0)](a0m[aa(0xd4)]('\x20\x20'+a0A(aa(0xcb)))),console[aa(0xe0)]();}catch(N){console[aa(0xe0)](),console['log'](a0m[aa(0x11a)](aa(0xfc)+(N[aa(0x150)]?.[aa(0x122)]('\x0a')[-0xf*-0xdb+-0xed1*-0x2+-0x7*0x611]||aa(0x125)))),console[aa(0xe0)]();}return'continue';}const w={};w[aa(0x172)]=aa(0x18e);if(aa(0x18c)===p||aa(0xde)===p||aa(0x18d)===p||aa(0x143)===p)return s=!(-0x1fe+-0xab9*0x3+0x222a),g=null,fetch(aa(0x1a5),{'method':aa(0x11c),'headers':w,'body':JSON[aa(0x1b0)]({'type':aa(0x11e),'tokensUsed':r,'timestamp':new Date()[aa(0xf2)]()}),'signal':AbortSignal[aa(0x131)](0x1029+0x9d*-0x6+0x70d)})[aa(0x13c)](()=>{}),console['log'](),console[aa(0xe0)](a0m[aa(0xd4)](aa(0x135))),console[aa(0xe0)](a0m[aa(0xff)]('\x20\x20'+a0A(aa(0x168)))),console[aa(0xe0)](a0m[aa(0xd4)]('\x20\x20'+a0A(aa(0xc0))+':\x20'+r+'/5000')),console[aa(0xe0)](a0m[aa(0xd4)]('\x20\x20'+a0A(aa(0x12d))+':\x20Google\x20Gemma\x204\x20(gemma-4-31b-it)')),console[aa(0xe0)](a0m[aa(0xd4)](aa(0x135))),console[aa(0xe0)](),console['log'](a0m['cyan'](aa(0x17f))),console['log'](a0m[aa(0xff)]('\x20\x20'+a0A(aa(0xea))+'\x20')+a0m[aa(0x1ba)][aa(0x11a)](aa(0x182))+a0m[aa(0xff)](aa(0xc4)+a0A(aa(0x148)))),console['log'](a0m['gray']('\x20\x20'+a0A(aa(0x112)))),console[aa(0xe0)](a0m[aa(0x11f)](aa(0x17f))),console['log'](),console[aa(0xe0)](a0m[aa(0x1ba)]['red'](aa(0x1a3))+a0m[aa(0xd4)](aa(0xd9)+a0A(aa(0x104)))),console[aa(0xe0)](a0m['white'](aa(0xdb))+a0m[aa(0xd4)](aa(0xcd)+a0A('ai4ctfExitCmdBack'))),console[aa(0xe0)](),aa(0x18c);const x={};x[aa(0x172)]=aa(0x18e);if(!y&&r>=-0x241*-0x5+0x5da+0x1*0x269)return y=!(-0x219f+-0x4a*-0x59+0x7e5),fetch(aa(0x1a5),{'method':aa(0x11c),'headers':x,'body':JSON['stringify']({'type':aa(0x11e),'solved':!(-0x2*0x59+-0x1996+-0x1*-0x1a49),'tokensUsed':r,'timestamp':new Date()[aa(0xf2)]()}),'signal':AbortSignal[aa(0x131)](-0x19c7*0x1+-0x1*-0x19c3+-0x1a1*-0xc)})[aa(0x13c)](()=>{}),console[aa(0xe0)](),console['log'](a0m[aa(0x1a1)]('\x20\x20'+a0A(aa(0xd1)))),h(),(function(){const ae=aa;console['log'](),console[ae(0xe0)](a0m[ae(0x1a1)](ae(0x17f))),console[ae(0xe0)](a0m[ae(0x1ba)][ae(0x1a1)]('\x20\x20'+a0A('ai4ctfRevealTitle'))),console[ae(0xe0)](a0m[ae(0x1a1)](ae(0x17f))),console[ae(0xe0)]();for(const O of a0A(ae(0xf7))[ae(0x122)]('\x0a'))''===O?console[ae(0xe0)]():console[ae(0xe0)](a0m[ae(0xff)](ae(0xf8)+O));console[ae(0xe0)](),console[ae(0xe0)](a0m[ae(0x11f)]('\x20\x20\x20\x20\x20\x20\x20\x20!echo\x20aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ==\x20|\x20base64\x20-d')),console[ae(0xe0)](),console[ae(0xe0)](a0m['white'](ae(0xf8)+a0A('ai4ctfRevealSeeFlag'))),console[ae(0xe0)](),console[ae(0xe0)](a0m[ae(0x12c)](ae(0x102))),console['log'](),console[ae(0xe0)](a0m[ae(0xff)](ae(0xf8)+a0A(ae(0x16d)))),console['log'](),console[ae(0xe0)](a0m[ae(0x11f)](ae(0x1bb))),console['log'](),console[ae(0xe0)](a0m[ae(0xd4)](ae(0xf8)+a0A(ae(0xe1)))),console[ae(0xe0)]();}()),aa(0x118);if(y)return console[aa(0xe0)](),console[aa(0xe0)](a0m[aa(0x1a1)]('\x20\x20'+a0A(aa(0x189)))),console[aa(0xe0)](a0m[aa(0xd4)]('\x20\x20'+a0A('ai4ctfLockedUse')+'\x20')+a0m[aa(0x11f)](aa(0x192))),console[aa(0xe0)](a0m[aa(0xd4)]('\x20\x20'+a0A(aa(0x126))+'\x20')+a0m[aa(0x11f)](aa(0x100))),console[aa(0xe0)](),'continue';console[aa(0xe0)](a0m['gray']('\x20\x20'+a0A(aa(0x159))));try{const O=await g[aa(0x142)](p);process[aa(0x130)][aa(0xd3)]('\x1b[1A\x1b[2K'),r+=O[aa(0x177)],a0q(O['tokensUsed']),console['log'](),a0x(O[aa(0x16f)]),h(),console[aa(0xe0)]();}catch(P){process['stdout'][aa(0xd3)](aa(0xf0)),a0z(aa(0x14b)+P[aa(0x150)]),console[aa(0xe0)]();}return'continue';}export async function startExamAi4ctfChat(j,k){const af=a0j,{getRealExamState:p,saveExamState:q}=await import(af(0x193)),v=p();if(!v)return!(-0x1a61+0x2d*-0xb3+0x1*0x39d9);const w=v[af(0x153)]?.['ai4ctf']??0x1004+-0x733+-0x8d1*0x1;if(w>=f)return console[af(0xe0)](),console[af(0xe0)](a0m[af(0x1a1)]('\x20\x20⚠\x20Your\x20AI4CTF\x20token\x20budget\x20is\x20exhausted.')),console[af(0xe0)](a0m['gray'](af(0x1ac))+a0m['white']('exam\x20answer\x20'+j+af(0x132))),console[af(0xe0)](),!(0x8e5+0x19db*-0x1+0x1*0x10f7);try{g=await a0p(void(-0x6a9+-0x8*0x2cf+-0x1d21*-0x1),function(A,B){const ag=af;return ag(0x12e)+B+',\x20'+A[ag(0x1bc)]+',\x20'+(A['points']||0x2c5*0xc+0x6+0x58a*-0x6)+ag(0xdf)+A[ag(0x16f)]+ag(0x16a);}(k,j));}catch(A){return a0z(A['message']),!(0x9e+-0xe75*0x2+0x1c4d);}const x={};x[af(0x113)]=j,x[af(0x184)]=k,x[af(0x1ae)]='ai4ctf',(s=!(0x1ea3+0x3a9*0x1+-0x2*0x1126),r=w,y=!(0xb83+-0x1*0x84a+0x4*-0xce),u=x);const z=v['answers']?.[j];return function(B,C,D){const ah=af,E=a0v()[ah(0x154)]||'gemma-4-31b-it',F=r>0x1367+-0x2*-0x94c+-0x25ff;console[ah(0xe0)](),console[ah(0xe0)](a0m[ah(0x12c)][ah(0x1ba)](ah(0xdd)+C+':\x20'+B[ah(0x1bc)]+'\x20═══')),F&&console[ah(0xe0)](a0m[ah(0xd4)](ah(0x11d))),D&&console[ah(0xe0)](a0m[ah(0xd4)](ah(0x115))+a0m[ah(0x1a1)](D)+a0m['gray'](ah(0x1b9))),console[ah(0xe0)](),console[ah(0xe0)](a0m[ah(0x11f)](ah(0x1c0))),console['log'](a0m[ah(0x11f)](ah(0xe2))+a0m[ah(0x1ba)][ah(0xff)]('Q'+C+'\x20\x20['+B['category']+ah(0x18f)+(B[ah(0xcc)]||-0x8*-0x3d1+-0xb13+-0xc7*0x19)+ah(0x16c))),console[ah(0xe0)](a0m[ah(0x11f)](ah(0x10f)));for(const G of String(B[ah(0x16f)])[ah(0x122)]('\x0a')){const H=G[ah(0x167)]>-0x8*-0x14e+0x70a+0x1*-0x113e?G[ah(0x119)](0x2b*-0xc7+-0x252e+0xf*0x4b5,-0x1dd4+-0x195*0x1+0x1fa2)+ah(0xf3):G;console[ah(0xe0)](a0m[ah(0x11f)](ah(0xe2))+a0m[ah(0xff)](H));}console[ah(0xe0)](a0m['cyan'](ah(0x10f))),console[ah(0xe0)](a0m[ah(0x11f)](ah(0xe2))+a0m[ah(0xd4)](ah(0xe6))+a0m[ah(0xff)](ah(0x1be)+C)),console['log'](a0m[ah(0x11f)]('\x20\x20│\x20')+a0m['gray'](ah(0x175))),console[ah(0xe0)](a0m[ah(0x11f)]('\x20\x20└─────────────────────────────────────────────')),console[ah(0xe0)](),console['log'](a0m[ah(0x1ba)][ah(0xff)](ah(0x179))),console[ah(0xe0)](),console[ah(0xe0)](a0m[ah(0x1a1)](ah(0x166))+a0m[ah(0xd4)]('\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20A\x20gentle\x20nudge\x20(pre-written)')),console[ah(0xe0)](a0m[ah(0x1a1)](ah(0x10b))+a0m[ah(0xd4)](ah(0x180))),console[ah(0xe0)](a0m[ah(0x1a1)](ah(0x198))+a0m[ah(0xd4)]('\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Key\x20breakthrough\x20(pre-written)')),console[ah(0xe0)](a0m[ah(0xff)]('\x20\x20\x20\x20submit\x20ICOA{...}')+a0m[ah(0xd4)](ah(0xe8))),console[ah(0xe0)](a0m['white'](ah(0x17e))+a0m[ah(0xd4)](ah(0x163))),console[ah(0xe0)](a0m['gray']('\x20\x20\x20\x20Just\x20type\x20freely\x20to\x20chat\x20with\x20the\x20AI\x20teammate.')),console[ah(0xe0)](a0m[ah(0xd4)](ah(0x133))+a0m[ah(0xff)](ah(0xf1))),console['log'](),console[ah(0xe0)](a0m[ah(0xd4)](ah(0x170))+a0m[ah(0xd4)](ah(0x146))),console[ah(0xe0)](),h(),console[ah(0xe0)](a0m[ah(0xd4)](ah(0x1c4)+E+')')),console[ah(0xe0)]();}(k,j,z),!(0x174b*-0x1+0xcc5+0xa86);}const d=new Set();export function registerAi4ctfCommand(b){const ai=a0j;b[ai(0x1b8)]('ai4ctf')[ai(0x121)](ai(0x107))[ai(0x164)](async()=>{const aj=ai;a0w('ai4ctf');const {getRealExamState:j}=await import(aj(0x193)),k=j(),m=k&&k[aj(0x14a)][aj(0x173)](w=>w['number']>=-0x75*0x3+-0x11*-0x97+0x17*-0x5f&&w['number']<=-0x474+0x20b5+0x5*-0x59f);if(k&&m){const w=k[aj(0xdc)]||-0x118d*0x1+0x575*0x2+0x2*0x352;if(w<0x3*0x1a1+-0x8a4+0x2*0x1f0||w>0x13a*-0x17+-0x2*-0xa8a+0x4*0x1d2)return console[aj(0xe0)](),console[aj(0xe0)](a0m[aj(0x1a1)](aj(0x195))),console['log'](a0m[aj(0xd4)](aj(0x149)+w+'.\x20Jump\x20there\x20first:')),console[aj(0xe0)](a0m[aj(0xd4)](aj(0x12a))+a0m[aj(0x1ba)]['cyan'](aj(0xd8))),void console[aj(0xe0)]();const x=k[aj(0x14a)][aj(0x10e)](z=>z[aj(0x108)]===w);return x?void await startExamAi4ctfChat(w,x):void a0z('Q'+w+aj(0x114)+w);}const p={};p[aj(0x111)]=aj(0x17d),p[aj(0x1bc)]='Cryptography';const q=a0v()[aj(0x154)]||aj(0x13b),v=p;try{g=await a0p(v);}catch(z){return void a0z(z[aj(0x150)]);}s=!(-0xe1a+-0x197c+0x2796),r=0xdb+-0x1b28+-0x1a4d*-0x1,y=!(-0x2600+0x2577+0x3*0x2e),console['log'](),console['log'](a0m[aj(0x12c)][aj(0x1ba)](aj(0x19d)+a0A(aj(0xed))+aj(0x1a9))),console[aj(0xe0)](),console['log'](a0m[aj(0xff)]('\x20\x20'+a0A(aj(0x1bf)))),console[aj(0xe0)](),console[aj(0xe0)](a0m['cyan'](aj(0x1c0))),console[aj(0xe0)](a0m['cyan'](aj(0xe2))+a0m[aj(0x1ba)][aj(0xff)](a0A(aj(0x171)))),console[aj(0xe0)](a0m[aj(0x11f)](aj(0x10f))),console['log'](a0m[aj(0x11f)]('\x20\x20│\x20')+a0m['white'](a0A('ai4ctfIntercepted'))),console[aj(0xe0)](a0m[aj(0x11f)](aj(0xe2))+a0m['green'](aj(0xe3))),console[aj(0xe0)](a0m['cyan']('\x20\x20│')),console[aj(0xe0)](a0m[aj(0x11f)](aj(0xe2))+a0m[aj(0xff)](a0A(aj(0x183)))),console[aj(0xe0)](a0m[aj(0x11f)](aj(0xe2))+a0m[aj(0xd4)](aj(0xf5))),console[aj(0xe0)](a0m['cyan']('\x20\x20└─────────────────────────────────────────────')),console[aj(0xe0)](),console[aj(0xe0)](a0m[aj(0xff)]('\x20\x20'+a0A(aj(0xc1)))),console[aj(0xe0)](),console[aj(0xe0)](a0m[aj(0x1a1)](aj(0x166))+a0m[aj(0xd4)](aj(0xf8)+a0A('ai4ctfHintA'))),console[aj(0xe0)](a0m[aj(0xd4)](aj(0xfe)+a0A(aj(0x196)))),console['log'](a0m[aj(0x1a1)](aj(0x10b))+a0m[aj(0xd4)](aj(0xf8)+a0A(aj(0xec)))),console['log'](a0m[aj(0xd4)](aj(0xfe)+a0A('ai4ctfHintBUses'))),console['log'](a0m['yellow'](aj(0x198))+a0m[aj(0xd4)](aj(0xf8)+a0A(aj(0x165)))),console[aj(0xe0)](a0m[aj(0xd4)](aj(0xfe)+a0A(aj(0xc8)))),console[aj(0xe0)](),console['log'](a0m['gray'](aj(0x135))),console['log'](a0m['bold'][aj(0xff)]('\x20\x20'+a0A('ai4ctfWelcomeCta'))),console[aj(0xe0)]('\x20\x20\x20\x20'+a0m[aj(0x11f)](aj(0x1a4))+a0m[aj(0xd4)](aj(0x1b2)+a0A(aj(0x197)))),console[aj(0xe0)]('\x20\x20\x20\x20'+a0m[aj(0x11f)](aj(0xe4))+a0m['gray'](aj(0x1b2)+a0A(aj(0x123)))),console[aj(0xe0)]('\x20\x20\x20\x20'+a0m[aj(0x11f)](aj(0x161))+a0m[aj(0xd4)](aj(0x1b2)+a0A(aj(0x18a)))),console[aj(0xe0)](a0m[aj(0xd4)](aj(0xf8)+a0A(aj(0x1a2)))),console['log'](),console['log'](a0m[aj(0xff)]('\x20\x20'+a0A(aj(0x109)))),console[aj(0xe0)](a0m[aj(0xd4)]('\x20\x20'+a0A(aj(0x10d))+'\x20')+a0m[aj(0xff)](aj(0xca))),console['log'](),console[aj(0xe0)](a0m[aj(0x1a1)]('\x20\x20'+a0A('ai4ctfCommands'))),console[aj(0xe0)](a0m[aj(0xff)](aj(0xf9))+a0m[aj(0xd4)](a0A(aj(0x117)))),console[aj(0xe0)](a0m[aj(0xff)](aj(0x129))+a0m[aj(0xd4)](a0A(aj(0x105)))),console[aj(0xe0)](a0m[aj(0xff)](aj(0xc6))+a0m['gray'](a0A(aj(0x127)))),console['log'](a0m[aj(0xd4)]('\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20e.g.\x20')+a0m[aj(0xff)](aj(0x169))),console['log'](a0m[aj(0xd4)]('\x20\x20\x20\x20exit\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'+a0A(aj(0x116)))),console[aj(0xe0)](),h(),console[aj(0xe0)](a0m[aj(0xd4)]('\x20\x20'+a0A(aj(0x12d))+aj(0x147)+q+')')),console['log'](a0m[aj(0xd4)]('\x20\x20'+a0A('ai4ctfExit'))),console['log']();});}
|