icoa-cli 2.19.12 → 2.19.14
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 +39 -38
- package/dist/commands/ctf.js +21 -6
- package/dist/commands/ctf4ai-demo.js +46 -45
- package/dist/commands/exam.js +35 -34
- package/dist/lib/ctfd-client.d.ts +1 -0
- package/dist/lib/ctfd-client.js +31 -1
- package/dist/lib/gemini.js +7 -6
- package/dist/lib/i18n.d.ts +20 -0
- package/dist/lib/i18n.js +56 -13
- package/package.json +2 -2
package/dist/commands/ai4ctf.js
CHANGED
|
@@ -4,6 +4,7 @@ import { addTokenUsage } from '../lib/budget.js';
|
|
|
4
4
|
import { getConfig } from '../lib/config.js';
|
|
5
5
|
import { logCommand } from '../lib/logger.js';
|
|
6
6
|
import { printMarkdown, printError } from '../lib/ui.js';
|
|
7
|
+
import { t } from '../lib/i18n.js';
|
|
7
8
|
function getChallengeContext() {
|
|
8
9
|
const config = getConfig();
|
|
9
10
|
if (config.currentChallengeName && config.currentChallengeCategory) {
|
|
@@ -40,11 +41,11 @@ export async function handleChatMessage(input) {
|
|
|
40
41
|
if (flag === DEMO_FLAG) {
|
|
41
42
|
console.log();
|
|
42
43
|
console.log(chalk.green.bold(' ════════════════════════════════════'));
|
|
43
|
-
console.log(chalk.green.bold(
|
|
44
|
+
console.log(chalk.green.bold(` ${t('ai4ctfCorrectFlag')}`));
|
|
44
45
|
console.log(chalk.green.bold(' ════════════════════════════════════'));
|
|
45
46
|
console.log();
|
|
46
|
-
console.log(chalk.white(
|
|
47
|
-
console.log(chalk.gray(
|
|
47
|
+
console.log(chalk.white(` ${t('ai4ctfDecoded')}`));
|
|
48
|
+
console.log(chalk.gray(` ${t('ai4ctfWouldEarn')}`));
|
|
48
49
|
console.log();
|
|
49
50
|
drawTokenBar();
|
|
50
51
|
chatActive = false;
|
|
@@ -55,14 +56,14 @@ export async function handleChatMessage(input) {
|
|
|
55
56
|
body: JSON.stringify({ type: 'ai4ctf', solved: true, tokensUsed: chatTokensUsed, timestamp: new Date().toISOString() }),
|
|
56
57
|
signal: AbortSignal.timeout(5000),
|
|
57
58
|
}).catch(() => { });
|
|
58
|
-
console.log(chalk.white(
|
|
59
|
+
console.log(chalk.white(` ${t('ai4ctfNext')}`));
|
|
59
60
|
console.log();
|
|
60
61
|
return 'exit';
|
|
61
62
|
}
|
|
62
63
|
else {
|
|
63
64
|
console.log();
|
|
64
|
-
console.log(chalk.red(
|
|
65
|
-
console.log(chalk.gray(
|
|
65
|
+
console.log(chalk.red(` ${t('ai4ctfWrongFlag')}`));
|
|
66
|
+
console.log(chalk.gray(` ${t('ai4ctfFlagHint')}`));
|
|
66
67
|
console.log();
|
|
67
68
|
return 'continue';
|
|
68
69
|
}
|
|
@@ -79,7 +80,7 @@ export async function handleChatMessage(input) {
|
|
|
79
80
|
console.log(chalk.gray(' $ ') + chalk.white(cmd));
|
|
80
81
|
console.log(chalk.white(' ' + output.split('\n').join('\n ')));
|
|
81
82
|
console.log();
|
|
82
|
-
console.log(chalk.gray(
|
|
83
|
+
console.log(chalk.gray(` ${t('ai4ctfFoundFlag')}`));
|
|
83
84
|
console.log();
|
|
84
85
|
}
|
|
85
86
|
catch (err) {
|
|
@@ -101,12 +102,12 @@ export async function handleChatMessage(input) {
|
|
|
101
102
|
}).catch(() => { });
|
|
102
103
|
console.log();
|
|
103
104
|
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
104
|
-
console.log(chalk.white(
|
|
105
|
-
console.log(chalk.gray(`
|
|
106
|
-
console.log(chalk.gray(`
|
|
105
|
+
console.log(chalk.white(` ${t('ai4ctfReport')}`));
|
|
106
|
+
console.log(chalk.gray(` ${t('ai4ctfTokens')}: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
|
|
107
|
+
console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (gemma-4-31b-it)`));
|
|
107
108
|
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
108
109
|
console.log();
|
|
109
|
-
console.log(chalk.white(
|
|
110
|
+
console.log(chalk.white(` ${t('ai4ctfNext')}`));
|
|
110
111
|
console.log();
|
|
111
112
|
return 'exit';
|
|
112
113
|
}
|
|
@@ -114,21 +115,21 @@ export async function handleChatMessage(input) {
|
|
|
114
115
|
chatActive = false;
|
|
115
116
|
chatSession = null;
|
|
116
117
|
console.log();
|
|
117
|
-
console.log(chalk.yellow(
|
|
118
|
+
console.log(chalk.yellow(` ${t('tokenLimit')}`));
|
|
118
119
|
drawTokenBar();
|
|
119
120
|
console.log();
|
|
120
121
|
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
121
|
-
console.log(chalk.white(
|
|
122
|
-
console.log(chalk.gray(`
|
|
123
|
-
console.log(chalk.gray(`
|
|
122
|
+
console.log(chalk.white(` ${t('ai4ctfReport')}`));
|
|
123
|
+
console.log(chalk.gray(` ${t('ai4ctfTokens')}: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
|
|
124
|
+
console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (gemma-4-31b-it)`));
|
|
124
125
|
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
125
126
|
console.log();
|
|
126
|
-
console.log(chalk.white(
|
|
127
|
+
console.log(chalk.white(` ${t('ai4ctfNext')}`));
|
|
127
128
|
console.log();
|
|
128
129
|
return 'exit';
|
|
129
130
|
}
|
|
130
131
|
logCommand(`ai4ctf: ${input}`);
|
|
131
|
-
console.log(chalk.gray(
|
|
132
|
+
console.log(chalk.gray(` ${t('ai4ctfThinking')}`));
|
|
132
133
|
try {
|
|
133
134
|
const response = await chatSession.sendMessage(input);
|
|
134
135
|
process.stdout.write('\x1b[1A\x1b[2K');
|
|
@@ -170,42 +171,42 @@ export function registerAi4ctfCommand(program) {
|
|
|
170
171
|
chatTokensUsed = 0;
|
|
171
172
|
// Guided welcome
|
|
172
173
|
console.log();
|
|
173
|
-
console.log(chalk.green.bold(
|
|
174
|
+
console.log(chalk.green.bold(` ═══ ${t('ai4ctfTitle')} ═══`));
|
|
174
175
|
console.log();
|
|
175
|
-
console.log(chalk.white(
|
|
176
|
+
console.log(chalk.white(` ${t('ai4ctfSample')}`));
|
|
176
177
|
console.log();
|
|
177
178
|
console.log(chalk.cyan(' ┌─────────────────────────────────────────────────┐'));
|
|
178
|
-
console.log(chalk.cyan(' │') + chalk.bold.white(
|
|
179
|
+
console.log(chalk.cyan(' │') + chalk.bold.white(` ${t('ai4ctfChallenge')}`.padEnd(50)) + chalk.cyan('│'));
|
|
179
180
|
console.log(chalk.cyan(' │') + chalk.white(' ') + chalk.cyan('│'));
|
|
180
|
-
console.log(chalk.cyan(' │') + chalk.white(
|
|
181
|
+
console.log(chalk.cyan(' │') + chalk.white(` ${t('ai4ctfIntercepted')}`.padEnd(50)) + chalk.cyan('│'));
|
|
181
182
|
console.log(chalk.cyan(' │') + chalk.green(' aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== ') + chalk.cyan('│'));
|
|
182
183
|
console.log(chalk.cyan(' │') + chalk.white(' ') + chalk.cyan('│'));
|
|
183
|
-
console.log(chalk.cyan(' │') + chalk.white(
|
|
184
|
+
console.log(chalk.cyan(' │') + chalk.white(` ${t('ai4ctfDecode')}`.padEnd(50)) + chalk.cyan('│'));
|
|
184
185
|
console.log(chalk.cyan(' │') + chalk.gray(' Flag format: icoa{...} ') + chalk.cyan('│'));
|
|
185
186
|
console.log(chalk.cyan(' └─────────────────────────────────────────────────┘'));
|
|
186
187
|
console.log();
|
|
187
|
-
console.log(chalk.white(
|
|
188
|
+
console.log(chalk.white(` ${t('ai4ctfLevels')}`));
|
|
188
189
|
console.log();
|
|
189
|
-
console.log(chalk.yellow(' hint a') + chalk.gray(
|
|
190
|
-
console.log(chalk.gray(
|
|
191
|
-
console.log(chalk.yellow(' hint b') + chalk.gray(
|
|
192
|
-
console.log(chalk.gray(
|
|
193
|
-
console.log(chalk.yellow(' hint c') + chalk.gray(
|
|
194
|
-
console.log(chalk.gray(
|
|
190
|
+
console.log(chalk.yellow(' hint a') + chalk.gray(` ${t('ai4ctfHintA')}`));
|
|
191
|
+
console.log(chalk.gray(` ${t('ai4ctfHintAUses')}`));
|
|
192
|
+
console.log(chalk.yellow(' hint b') + chalk.gray(` ${t('ai4ctfHintB')}`));
|
|
193
|
+
console.log(chalk.gray(` ${t('ai4ctfHintBUses')}`));
|
|
194
|
+
console.log(chalk.yellow(' hint c') + chalk.gray(` ${t('ai4ctfHintC')}`));
|
|
195
|
+
console.log(chalk.gray(` ${t('ai4ctfHintCUses')}`));
|
|
195
196
|
console.log();
|
|
196
197
|
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
197
|
-
console.log(chalk.white(
|
|
198
|
-
console.log(chalk.gray(
|
|
199
|
-
console.log(chalk.gray(
|
|
198
|
+
console.log(chalk.white(` ${t('ai4ctfTryNow')}`));
|
|
199
|
+
console.log(chalk.gray(` ${t('ai4ctfExample')}`));
|
|
200
|
+
console.log(chalk.gray(` ${t('ai4ctfFreeChat')}`));
|
|
200
201
|
console.log();
|
|
201
|
-
console.log(chalk.yellow(
|
|
202
|
-
console.log(chalk.white(' submit <flag>') + chalk.gray(
|
|
203
|
-
console.log(chalk.white(' !<command>') + chalk.gray(
|
|
204
|
-
console.log(chalk.gray(
|
|
202
|
+
console.log(chalk.yellow(` ${t('ai4ctfCommands')}`));
|
|
203
|
+
console.log(chalk.white(' submit <flag>') + chalk.gray(` ${t('ai4ctfSubmitCmd')}`));
|
|
204
|
+
console.log(chalk.white(' !<command>') + chalk.gray(` ${t('ai4ctfShellCmd')}`));
|
|
205
|
+
console.log(chalk.gray(` exit ${t('ai4ctfEndSession')}`));
|
|
205
206
|
console.log();
|
|
206
207
|
drawTokenBar();
|
|
207
|
-
console.log(chalk.gray(`
|
|
208
|
-
console.log(chalk.gray(
|
|
208
|
+
console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (${modelName})`));
|
|
209
|
+
console.log(chalk.gray(` ${t('ai4ctfExit')}`));
|
|
209
210
|
console.log();
|
|
210
211
|
});
|
|
211
212
|
}
|
package/dist/commands/ctf.js
CHANGED
|
@@ -176,12 +176,27 @@ export function registerCtfCommands(program) {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
catch (err) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
if (sessionCookie) {
|
|
180
|
+
// Session login succeeded but API test failed — save anyway
|
|
181
|
+
spinner2.succeed('Connected (session mode — limited API)');
|
|
182
|
+
saveConfig({
|
|
183
|
+
ctfdUrl: url,
|
|
184
|
+
token: sessionCookie,
|
|
185
|
+
userName: username,
|
|
186
|
+
sessionCookie: sessionCookie,
|
|
187
|
+
});
|
|
188
|
+
console.log();
|
|
189
|
+
printKeyValue('User', username);
|
|
190
|
+
printWarning('API access limited. Some features may not work.');
|
|
191
|
+
printSuccess('Connection saved.');
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
spinner2.fail('Connection test failed');
|
|
195
|
+
printError(err.message);
|
|
196
|
+
saveConfig({ ctfdUrl: url });
|
|
197
|
+
console.log();
|
|
198
|
+
printInfo('Connection saved. Try: join ' + url);
|
|
199
|
+
}
|
|
185
200
|
}
|
|
186
201
|
});
|
|
187
202
|
// ─── icoa ctf logout ───
|
|
@@ -2,47 +2,48 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { logCommand } from '../lib/logger.js';
|
|
3
3
|
import { printError } from '../lib/ui.js';
|
|
4
4
|
import { getConfig } from '../lib/config.js';
|
|
5
|
+
import { t } from '../lib/i18n.js';
|
|
5
6
|
function printDemoReport(ctf4aiSolved, ctf4aiTokens) {
|
|
6
7
|
const config = getConfig();
|
|
7
8
|
const modelName = config.geminiModel || 'gemma-4-31b-it';
|
|
8
9
|
console.log();
|
|
9
10
|
console.log(chalk.cyan(' ═══════════════════════════════════════════════'));
|
|
10
|
-
console.log(chalk.bold.white(
|
|
11
|
+
console.log(chalk.bold.white(` ${t('reportTitle')}`));
|
|
11
12
|
console.log(chalk.cyan(' ═══════════════════════════════════════════════'));
|
|
12
13
|
console.log();
|
|
13
|
-
console.log(chalk.white(
|
|
14
|
-
console.log(chalk.green(
|
|
14
|
+
console.log(chalk.white(` ${t('reportStage1')}`));
|
|
15
|
+
console.log(chalk.green(` ${t('reportCompleted')}`));
|
|
15
16
|
console.log();
|
|
16
|
-
console.log(chalk.white(
|
|
17
|
-
console.log(chalk.green(
|
|
18
|
-
console.log(chalk.gray(
|
|
19
|
-
console.log(chalk.gray(
|
|
17
|
+
console.log(chalk.white(` ${t('reportStage2')}`));
|
|
18
|
+
console.log(chalk.green(` ${t('reportExperienced')}`));
|
|
19
|
+
console.log(chalk.gray(` ${t('reportStage2Sub')}`));
|
|
20
|
+
console.log(chalk.gray(` ${t('reportStage2Hints')}`));
|
|
20
21
|
console.log();
|
|
21
|
-
console.log(chalk.white(
|
|
22
|
+
console.log(chalk.white(` ${t('reportStage3')}`));
|
|
22
23
|
if (ctf4aiSolved) {
|
|
23
|
-
console.log(chalk.green(
|
|
24
|
+
console.log(chalk.green(` ${t('reportSolved')}`));
|
|
24
25
|
}
|
|
25
26
|
else {
|
|
26
|
-
console.log(chalk.yellow(
|
|
27
|
+
console.log(chalk.yellow(` ${t('reportNotSolved')}`));
|
|
27
28
|
}
|
|
28
|
-
console.log(chalk.gray(`
|
|
29
|
+
console.log(chalk.gray(` ${t('ai4ctfTokens')}: ${ctf4aiTokens}/${CTF4AI_TOKEN_LIMIT}`));
|
|
29
30
|
console.log();
|
|
30
31
|
console.log(chalk.cyan(' ─────────────────────────────────────────────'));
|
|
31
32
|
console.log();
|
|
32
|
-
console.log(chalk.bold.white(
|
|
33
|
-
console.log(chalk.gray(
|
|
34
|
-
console.log(chalk.gray(
|
|
35
|
-
console.log(chalk.gray(
|
|
36
|
-
console.log(chalk.gray(
|
|
33
|
+
console.log(chalk.bold.white(` ${t('reportRecommend')}`));
|
|
34
|
+
console.log(chalk.gray(` · ${t('reportRec1')}`));
|
|
35
|
+
console.log(chalk.gray(` · ${t('reportRec2')}`));
|
|
36
|
+
console.log(chalk.gray(` · ${t('reportRec3')}`));
|
|
37
|
+
console.log(chalk.gray(` · ${t('reportRec4')}`));
|
|
37
38
|
console.log();
|
|
38
|
-
console.log(chalk.gray(`
|
|
39
|
+
console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (${modelName})`));
|
|
39
40
|
console.log();
|
|
40
41
|
console.log(chalk.cyan(' ─────────────────────────────────────────────'));
|
|
41
42
|
console.log();
|
|
42
|
-
console.log(chalk.white(
|
|
43
|
-
console.log(chalk.white(' nations') + chalk.gray(
|
|
44
|
-
console.log(chalk.white(' about') + chalk.gray(
|
|
45
|
-
console.log(chalk.white(' demo') + chalk.gray(
|
|
43
|
+
console.log(chalk.white(` ${t('reportReady')}`));
|
|
44
|
+
console.log(chalk.white(' nations') + chalk.gray(` ${t('reportNations')}`));
|
|
45
|
+
console.log(chalk.white(' about') + chalk.gray(` ${t('reportAbout')}`));
|
|
46
|
+
console.log(chalk.white(' demo') + chalk.gray(` ${t('reportDemo')}`));
|
|
46
47
|
console.log();
|
|
47
48
|
console.log(chalk.yellow(' ICOA 2026 · Sydney, Australia · Jun 27 - Jul 2'));
|
|
48
49
|
console.log(chalk.cyan.underline(' https://icoa2026.au'));
|
|
@@ -80,14 +81,14 @@ export async function handleCtf4aiMessage(input) {
|
|
|
80
81
|
signal: AbortSignal.timeout(5000),
|
|
81
82
|
}).catch(() => { });
|
|
82
83
|
console.log();
|
|
83
|
-
console.log(chalk.gray(
|
|
84
|
+
console.log(chalk.gray(` ${t('ctf4aiEnded')}`));
|
|
84
85
|
printDemoReport(false, ctf4aiTokens);
|
|
85
86
|
return 'exit';
|
|
86
87
|
}
|
|
87
88
|
if (ctf4aiTokens >= CTF4AI_TOKEN_LIMIT) {
|
|
88
89
|
console.log();
|
|
89
|
-
console.log(chalk.yellow(
|
|
90
|
-
console.log(chalk.white(
|
|
90
|
+
console.log(chalk.yellow(` ${t('tokenLimit')}`));
|
|
91
|
+
console.log(chalk.white(` ${t('ctf4aiHeld')}`));
|
|
91
92
|
ctf4aiActive = false;
|
|
92
93
|
ctf4aiSession = null;
|
|
93
94
|
fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
|
|
@@ -101,7 +102,7 @@ export async function handleCtf4aiMessage(input) {
|
|
|
101
102
|
}
|
|
102
103
|
logCommand(`ctf4ai: ${input}`);
|
|
103
104
|
try {
|
|
104
|
-
console.log(chalk.gray(
|
|
105
|
+
console.log(chalk.gray(` ${t('ctf4aiThinking')}`));
|
|
105
106
|
const { text, tokensUsed } = await ctf4aiSession.sendMessage(input);
|
|
106
107
|
ctf4aiTokens += tokensUsed;
|
|
107
108
|
// Clear "Thinking..." line
|
|
@@ -117,11 +118,11 @@ export async function handleCtf4aiMessage(input) {
|
|
|
117
118
|
if (strictMatch || spelledMatch) {
|
|
118
119
|
console.log();
|
|
119
120
|
console.log(chalk.green.bold(' ════════════════════════════════════'));
|
|
120
|
-
console.log(chalk.green.bold(
|
|
121
|
+
console.log(chalk.green.bold(` ${t('ctf4aiSuccess')}`));
|
|
121
122
|
console.log(chalk.green.bold(' ════════════════════════════════════'));
|
|
122
123
|
console.log();
|
|
123
|
-
console.log(chalk.white(
|
|
124
|
-
console.log(chalk.gray(
|
|
124
|
+
console.log(chalk.white(` ${t('ctf4aiDefense')}`));
|
|
125
|
+
console.log(chalk.gray(` ${t('ctf4aiInjection')}`));
|
|
125
126
|
ctf4aiActive = false;
|
|
126
127
|
ctf4aiSession = null;
|
|
127
128
|
fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
|
|
@@ -147,34 +148,34 @@ export function registerCtf4aiDemoCommand(program) {
|
|
|
147
148
|
.action(async () => {
|
|
148
149
|
logCommand('ctf4ai');
|
|
149
150
|
if (ctf4aiActive) {
|
|
150
|
-
console.log(chalk.gray(
|
|
151
|
+
console.log(chalk.gray(` ${t('ctf4aiAlready')}`));
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
153
154
|
const config = getConfig();
|
|
154
155
|
const modelName = config.geminiModel || 'gemma-4-31b-it';
|
|
155
156
|
console.log();
|
|
156
|
-
console.log(chalk.red.bold(
|
|
157
|
+
console.log(chalk.red.bold(` ═══ ${t('ctf4aiTitle')} ═══`));
|
|
157
158
|
console.log();
|
|
158
|
-
console.log(chalk.white(
|
|
159
|
+
console.log(chalk.white(` ${t('ctf4aiChallenge')}`));
|
|
159
160
|
console.log();
|
|
160
|
-
console.log(chalk.gray(
|
|
161
|
-
console.log(chalk.gray(
|
|
161
|
+
console.log(chalk.gray(` ${t('ctf4aiIntro1')}`));
|
|
162
|
+
console.log(chalk.gray(` ${t('ctf4aiIntro2')}`));
|
|
162
163
|
console.log();
|
|
163
164
|
console.log(chalk.gray(' ┌─────────────────────────────────────────────────┐'));
|
|
164
|
-
console.log(chalk.gray(' │') + chalk.white(
|
|
165
|
-
console.log(chalk.gray(' │') + chalk.white(
|
|
165
|
+
console.log(chalk.gray(' │') + chalk.white(` ${t('ctf4aiRule')}`.padEnd(50)) + chalk.gray('│'));
|
|
166
|
+
console.log(chalk.gray(' │') + chalk.white(` ${t('ctf4aiMission')}`.padEnd(50)) + chalk.gray('│'));
|
|
166
167
|
console.log(chalk.gray(' │') + chalk.white(' ') + chalk.gray('│'));
|
|
167
|
-
console.log(chalk.gray(' │') + chalk.yellow(
|
|
168
|
-
console.log(chalk.gray(' │') + chalk.gray(
|
|
169
|
-
console.log(chalk.gray(' │') + chalk.gray(
|
|
170
|
-
console.log(chalk.gray(' │') + chalk.gray(
|
|
171
|
-
console.log(chalk.gray(' │') + chalk.gray(
|
|
168
|
+
console.log(chalk.gray(' │') + chalk.yellow(` ${t('ctf4aiIdeas')}`.padEnd(50)) + chalk.gray('│'));
|
|
169
|
+
console.log(chalk.gray(' │') + chalk.gray(` · ${t('ctf4aiIdea1')}`.padEnd(50)) + chalk.gray('│'));
|
|
170
|
+
console.log(chalk.gray(' │') + chalk.gray(` · ${t('ctf4aiIdea2')}`.padEnd(50)) + chalk.gray('│'));
|
|
171
|
+
console.log(chalk.gray(' │') + chalk.gray(` · ${t('ctf4aiIdea3')}`.padEnd(50)) + chalk.gray('│'));
|
|
172
|
+
console.log(chalk.gray(' │') + chalk.gray(` · ${t('ctf4aiIdea4')}`.padEnd(50)) + chalk.gray('│'));
|
|
172
173
|
console.log(chalk.gray(' └─────────────────────────────────────────────────┘'));
|
|
173
174
|
console.log();
|
|
174
|
-
console.log(chalk.gray(`
|
|
175
|
-
console.log(chalk.gray(
|
|
176
|
-
console.log(chalk.gray(`
|
|
177
|
-
console.log(chalk.gray(
|
|
175
|
+
console.log(chalk.gray(` ${t('ctf4aiBudget')}: ~${Math.round(CTF4AI_TOKEN_LIMIT / 4)} words (${CTF4AI_TOKEN_LIMIT} tokens)`));
|
|
176
|
+
console.log(chalk.gray(` ${t('ctf4aiRelaxed')}`));
|
|
177
|
+
console.log(chalk.gray(` ${t('ai4ctfModel')}: Google Gemma 4 (${modelName})`));
|
|
178
|
+
console.log(chalk.gray(` ${t('ctf4aiQuit')}`));
|
|
178
179
|
console.log();
|
|
179
180
|
try {
|
|
180
181
|
// Create chat with restrictive system prompt
|
|
@@ -196,7 +197,7 @@ export function registerCtf4aiDemoCommand(program) {
|
|
|
196
197
|
};
|
|
197
198
|
ctf4aiActive = true;
|
|
198
199
|
ctf4aiTokens = 0;
|
|
199
|
-
console.log(chalk.red(' ctf4ai> ') + chalk.gray('
|
|
200
|
+
console.log(chalk.red(' ctf4ai> ') + chalk.gray(`${t('ctf4aiPrompt')}`));
|
|
200
201
|
console.log();
|
|
201
202
|
}
|
|
202
203
|
catch (err) {
|
package/dist/commands/exam.js
CHANGED
|
@@ -29,14 +29,17 @@ function checkTimeRemaining() {
|
|
|
29
29
|
return true;
|
|
30
30
|
if (new Date() >= deadline) {
|
|
31
31
|
const state = getExamState();
|
|
32
|
-
if (state
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
if (state) {
|
|
33
|
+
const answered = Object.keys(state.answers).length;
|
|
34
|
+
if (state.session.examId === 'demo-free' || answered === 0) {
|
|
35
|
+
// Demo or unanswered expired exam — auto-clear to avoid deadlock
|
|
36
|
+
clearExamState();
|
|
37
|
+
printWarning('Exam expired and cleared.');
|
|
38
|
+
printInfo('Type: demo to start again.');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
printError('Time expired! Use "exam submit" to submit your answers.');
|
|
42
|
+
}
|
|
40
43
|
}
|
|
41
44
|
return false;
|
|
42
45
|
}
|
|
@@ -639,12 +642,15 @@ export function registerExamCommand(program) {
|
|
|
639
642
|
const total = state.session.questionCount;
|
|
640
643
|
const unanswered = total - answered;
|
|
641
644
|
if (answered === 0) {
|
|
642
|
-
|
|
643
|
-
|
|
645
|
+
const deadline = getExamDeadline();
|
|
646
|
+
const expired = deadline && new Date() >= deadline;
|
|
647
|
+
if (state.session.examId === 'demo-free' || expired) {
|
|
644
648
|
clearExamState();
|
|
645
|
-
|
|
649
|
+
printWarning('No answers to submit. Exam cleared.');
|
|
650
|
+
printInfo('Type: demo to start again.');
|
|
646
651
|
}
|
|
647
652
|
else {
|
|
653
|
+
printWarning('No answers to submit.');
|
|
648
654
|
printInfo('Answer some questions first: exam q 1');
|
|
649
655
|
}
|
|
650
656
|
return;
|
|
@@ -739,36 +745,31 @@ export function registerExamCommand(program) {
|
|
|
739
745
|
}
|
|
740
746
|
console.log();
|
|
741
747
|
// ─── What is CTF + Dual-track introduction ───
|
|
742
|
-
console.log(chalk.white(
|
|
748
|
+
console.log(chalk.white(` ${t('theoryDone')}`));
|
|
749
|
+
console.log(chalk.white(` ${t('theoryDone2')}`));
|
|
743
750
|
console.log();
|
|
744
|
-
console.log(chalk.yellow(
|
|
745
|
-
console.log(chalk.gray(
|
|
746
|
-
console.log(chalk.gray(
|
|
747
|
-
console.log(chalk.
|
|
748
|
-
console.log(chalk.gray(
|
|
751
|
+
console.log(chalk.yellow(` ${t('didYouKnow')}`));
|
|
752
|
+
console.log(chalk.gray(` ${t('ctfFlags1')}`));
|
|
753
|
+
console.log(chalk.gray(` ${t('ctfFlags2')}`));
|
|
754
|
+
console.log(chalk.green(' icoa{example_flag_here}'));
|
|
755
|
+
console.log(chalk.gray(` ${t('ctfFlags3')}`));
|
|
749
756
|
console.log();
|
|
750
|
-
console.log(chalk.white(
|
|
757
|
+
console.log(chalk.white(` ${t('twoTracks')}`));
|
|
751
758
|
console.log();
|
|
752
|
-
console.log(chalk.green.bold(' AI4CTF') + chalk.white(
|
|
753
|
-
console.log(chalk.gray(
|
|
754
|
-
console.log(chalk.gray(' and work together to crack cybersecurity puzzles.'));
|
|
759
|
+
console.log(chalk.green.bold(' AI4CTF') + chalk.white(` — ${t('ai4ctfDesc')}`));
|
|
760
|
+
console.log(chalk.gray(` ${t('ai4ctfSub')}`));
|
|
755
761
|
console.log();
|
|
756
|
-
console.log(chalk.red.bold(' CTF4AI') + chalk.white(
|
|
757
|
-
console.log(chalk.gray(
|
|
758
|
-
console.log(chalk.gray(' This is a real skill used to test AI security.'));
|
|
762
|
+
console.log(chalk.red.bold(' CTF4AI') + chalk.white(` — ${t('ctf4aiDesc')}`));
|
|
763
|
+
console.log(chalk.gray(` ${t('ctf4aiSub')}`));
|
|
759
764
|
console.log();
|
|
760
765
|
console.log(chalk.cyan(' ─────────────────────────────────────────────'));
|
|
761
|
-
console.log(chalk.
|
|
762
|
-
console.log(chalk.
|
|
763
|
-
console.log(chalk.
|
|
764
|
-
console.log();
|
|
765
|
-
console.log(chalk.white(' Ready to try? Type a command:'));
|
|
766
|
-
console.log(chalk.green.bold(' ai4ctf') + chalk.gray(' Chat with AI teammate (start here!)'));
|
|
767
|
-
console.log(chalk.red.bold(' ctf4ai') + chalk.gray(' Trick the AI — make it say "koala"'));
|
|
766
|
+
console.log(chalk.white(` ${t('readyToTry')}`));
|
|
767
|
+
console.log(chalk.green.bold(' ai4ctf') + chalk.gray(` ${t('ai4ctfDesc')}`));
|
|
768
|
+
console.log(chalk.red.bold(' ctf4ai') + chalk.gray(` ${t('ctf4aiDesc')}`));
|
|
768
769
|
console.log();
|
|
769
|
-
console.log(chalk.gray(
|
|
770
|
-
console.log(chalk.white(' nations') + chalk.gray(
|
|
771
|
-
console.log(chalk.white(' exam AU') + chalk.gray(
|
|
770
|
+
console.log(chalk.gray(` ${t('forNational')}`));
|
|
771
|
+
console.log(chalk.white(' nations') + chalk.gray(` ${t('viewRegions')}`));
|
|
772
|
+
console.log(chalk.white(' exam AU') + chalk.gray(` ${t('enterExam')}`));
|
|
772
773
|
console.log();
|
|
773
774
|
}
|
|
774
775
|
catch (err) {
|
|
@@ -9,6 +9,7 @@ export declare class CTFdClient {
|
|
|
9
9
|
fetchCsrfNonce(): Promise<string>;
|
|
10
10
|
private request;
|
|
11
11
|
testConnection(): Promise<CTFdUser>;
|
|
12
|
+
private testConnectionViaProfile;
|
|
12
13
|
getChallenges(): Promise<CTFdChallengeListItem[]>;
|
|
13
14
|
getChallenge(id: number): Promise<CTFdChallenge>;
|
|
14
15
|
submitFlag(challengeId: number, submission: string): Promise<CTFdAttemptResponse['data']>;
|
package/dist/lib/ctfd-client.js
CHANGED
|
@@ -69,7 +69,37 @@ export class CTFdClient {
|
|
|
69
69
|
return json.data;
|
|
70
70
|
}
|
|
71
71
|
async testConnection() {
|
|
72
|
-
|
|
72
|
+
try {
|
|
73
|
+
return await this.request('GET', '/users/me');
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
// Session mode fallback: API may return 403, try scraping profile page
|
|
77
|
+
if (this.sessionCookie && err.message?.includes('403')) {
|
|
78
|
+
return this.testConnectionViaProfile();
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async testConnectionViaProfile() {
|
|
84
|
+
const res = await fetch(`${this.baseUrl}/settings`, {
|
|
85
|
+
headers: { Cookie: this.sessionCookie },
|
|
86
|
+
});
|
|
87
|
+
if (!res.ok)
|
|
88
|
+
throw new Error('Session expired or invalid.');
|
|
89
|
+
const html = await res.text();
|
|
90
|
+
// Extract user name from settings page
|
|
91
|
+
const nameMatch = html.match(/name="name"[^>]*value="([^"]+)"/) ||
|
|
92
|
+
html.match(/<input[^>]*id="name"[^>]*value="([^"]+)"/);
|
|
93
|
+
const name = nameMatch?.[1] || 'User';
|
|
94
|
+
// Extract user ID from page
|
|
95
|
+
const idMatch = html.match(/user_id['":\s]+(\d+)/) ||
|
|
96
|
+
html.match(/userId['":\s]+(\d+)/);
|
|
97
|
+
const id = idMatch ? parseInt(idMatch[1], 10) : 0;
|
|
98
|
+
// Update CSRF nonce from settings page
|
|
99
|
+
const csrfMatch = html.match(/csrfNonce['":\s]+['"]([^'"]+)['"]/);
|
|
100
|
+
if (csrfMatch)
|
|
101
|
+
this.csrfNonce = csrfMatch[1];
|
|
102
|
+
return { id, name, score: 0, team_id: 0, country: '' };
|
|
73
103
|
}
|
|
74
104
|
async getChallenges() {
|
|
75
105
|
return this.request('GET', '/challenges');
|
package/dist/lib/gemini.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GoogleGenAI } from '@google/genai';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import { getConfig, saveConfig } from './config.js';
|
|
3
4
|
const SYSTEM_PROMPTS = {
|
|
4
5
|
A: `You are an AI assistant in a cybersecurity CTF competition called ICOA.
|
|
@@ -60,14 +61,14 @@ export async function generateHint(level, question, context) {
|
|
|
60
61
|
try {
|
|
61
62
|
const { input } = await import('@inquirer/prompts');
|
|
62
63
|
console.log();
|
|
63
|
-
console.log(' Gemini API key not configured.');
|
|
64
|
-
console.log(' Get one free at: https://aistudio.google.com/apikey');
|
|
64
|
+
console.log(chalk.yellow(' Gemini API key not configured.'));
|
|
65
|
+
console.log(chalk.gray(' Get one free at: ') + chalk.cyan('https://aistudio.google.com/apikey'));
|
|
65
66
|
console.log();
|
|
66
67
|
apiKey = await input({ message: 'Enter your Gemini API Key:' });
|
|
67
68
|
if (apiKey.trim()) {
|
|
68
69
|
apiKey = apiKey.trim();
|
|
69
70
|
saveConfig({ geminiApiKey: apiKey });
|
|
70
|
-
console.log(' Key saved for future use.');
|
|
71
|
+
console.log(chalk.green(' Key saved for future use.'));
|
|
71
72
|
console.log();
|
|
72
73
|
}
|
|
73
74
|
else {
|
|
@@ -146,14 +147,14 @@ export async function createChatSession(context) {
|
|
|
146
147
|
try {
|
|
147
148
|
const { input } = await import('@inquirer/prompts');
|
|
148
149
|
console.log();
|
|
149
|
-
console.log(' Gemini API key not configured.');
|
|
150
|
-
console.log(' Get one free at: https://aistudio.google.com/apikey');
|
|
150
|
+
console.log(chalk.yellow(' Gemini API key not configured.'));
|
|
151
|
+
console.log(chalk.gray(' Get one free at: ') + chalk.cyan('https://aistudio.google.com/apikey'));
|
|
151
152
|
console.log();
|
|
152
153
|
apiKey = await input({ message: 'Enter your Gemini API Key:' });
|
|
153
154
|
if (apiKey.trim()) {
|
|
154
155
|
apiKey = apiKey.trim();
|
|
155
156
|
saveConfig({ geminiApiKey: apiKey });
|
|
156
|
-
console.log(' Key saved for future use.');
|
|
157
|
+
console.log(chalk.green(' Key saved for future use.'));
|
|
157
158
|
console.log();
|
|
158
159
|
}
|
|
159
160
|
else {
|
package/dist/lib/i18n.d.ts
CHANGED
|
@@ -119,6 +119,26 @@ declare const EN: {
|
|
|
119
119
|
continueTry: string;
|
|
120
120
|
continueChat: string;
|
|
121
121
|
continueAfter: string;
|
|
122
|
+
ai4ctfCommands: string;
|
|
123
|
+
ai4ctfSubmitCmd: string;
|
|
124
|
+
ai4ctfShellCmd: string;
|
|
125
|
+
ai4ctfEndSession: string;
|
|
126
|
+
ai4ctfCorrectFlag: string;
|
|
127
|
+
ai4ctfDecoded: string;
|
|
128
|
+
ai4ctfWouldEarn: string;
|
|
129
|
+
ai4ctfWrongFlag: string;
|
|
130
|
+
ai4ctfFlagHint: string;
|
|
131
|
+
ai4ctfFoundFlag: string;
|
|
132
|
+
ai4ctfThinking: string;
|
|
133
|
+
ctf4aiRelaxed: string;
|
|
134
|
+
ctf4aiEnded: string;
|
|
135
|
+
ctf4aiAlready: string;
|
|
136
|
+
ctf4aiPrompt: string;
|
|
137
|
+
ctf4aiThinking: string;
|
|
138
|
+
readyToTry: string;
|
|
139
|
+
forNational: string;
|
|
140
|
+
viewRegions: string;
|
|
141
|
+
enterExam: string;
|
|
122
142
|
};
|
|
123
143
|
export declare function t(key: keyof Strings): string;
|
|
124
144
|
export declare function hasFullTranslation(lang: string): boolean;
|