icoa-cli 1.0.0 → 1.1.1
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/index.js +34 -3
- package/dist/lib/gemini.js +20 -14
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { registerNoteCommand } from './commands/note.js';
|
|
|
11
11
|
import { registerLogCommand } from './commands/log.js';
|
|
12
12
|
import { registerLangCommand } from './commands/lang.js';
|
|
13
13
|
import { registerSetupCommand } from './commands/setup.js';
|
|
14
|
-
import { isConnected, getConfig } from './lib/config.js';
|
|
14
|
+
import { isConnected, getConfig, saveConfig } from './lib/config.js';
|
|
15
15
|
const BANNER = `
|
|
16
16
|
${chalk.cyan('╔══════════════════════════════════════════════════════════╗')}
|
|
17
17
|
${chalk.cyan('║')} ${chalk.cyan('║')}
|
|
@@ -22,8 +22,13 @@ ${chalk.cyan('║')} ${chalk.bold.white('██║██║ ██║
|
|
|
22
22
|
${chalk.cyan('║')} ${chalk.bold.white('██║╚██████╗╚██████╔╝██║ ██║')} ${chalk.cyan('║')}
|
|
23
23
|
${chalk.cyan('║')} ${chalk.bold.white('╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝')} ${chalk.cyan('║')}
|
|
24
24
|
${chalk.cyan('║')} ${chalk.cyan('║')}
|
|
25
|
-
${chalk.cyan('║')} ${chalk.yellow('International
|
|
26
|
-
${chalk.cyan('║')} ${chalk.
|
|
25
|
+
${chalk.cyan('║')} ${chalk.yellow('International Cyber Olympiad in AI 2026')} ${chalk.cyan('║')}
|
|
26
|
+
${chalk.cyan('║')} ${chalk.bold.magenta("The World's First AI Security Olympiad")} ${chalk.cyan('║')}
|
|
27
|
+
${chalk.cyan('║')} ${chalk.cyan('║')}
|
|
28
|
+
${chalk.cyan('║')} ${chalk.green.bold('AI4CTF')} ${chalk.gray('Use AI to solve challenges')} ${chalk.cyan('║')}
|
|
29
|
+
${chalk.cyan('║')} ${chalk.red.bold('CTF4AI')} ${chalk.gray('Hack, attack & evaluate AI systems')} ${chalk.cyan('║')}
|
|
30
|
+
${chalk.cyan('║')} ${chalk.cyan('║')}
|
|
31
|
+
${chalk.cyan('║')} ${chalk.gray('CLI-Native Competition Terminal v1.1.1')} ${chalk.cyan('║')}
|
|
27
32
|
${chalk.cyan('║')} ${chalk.cyan('║')}
|
|
28
33
|
${chalk.cyan('╚══════════════════════════════════════════════════════════╝')}
|
|
29
34
|
`;
|
|
@@ -75,4 +80,30 @@ registerNoteCommand(program);
|
|
|
75
80
|
registerLogCommand(program);
|
|
76
81
|
registerLangCommand(program);
|
|
77
82
|
registerSetupCommand(program);
|
|
83
|
+
// Hidden command: switch AI model
|
|
84
|
+
program
|
|
85
|
+
.command('model', { hidden: true })
|
|
86
|
+
.argument('[name]', 'model name to switch to')
|
|
87
|
+
.action((name) => {
|
|
88
|
+
const config = getConfig();
|
|
89
|
+
const current = config.geminiModel || 'gemini-2.5-flash';
|
|
90
|
+
if (!name) {
|
|
91
|
+
console.log();
|
|
92
|
+
console.log(chalk.gray(' Current model: ') + chalk.white(current));
|
|
93
|
+
console.log();
|
|
94
|
+
console.log(chalk.gray(' Available models:'));
|
|
95
|
+
console.log(chalk.white(' icoa model gemini-2.5-flash ') + chalk.gray('Fast, free tier'));
|
|
96
|
+
console.log(chalk.white(' icoa model gemini-2.5-pro ') + chalk.gray('Strongest reasoning'));
|
|
97
|
+
console.log(chalk.white(' icoa model gemma-4-31b-it ') + chalk.gray('Open-source, free'));
|
|
98
|
+
console.log(chalk.white(' icoa model gemma-4-26b-a4b-it ') + chalk.gray('Open-source, lightweight'));
|
|
99
|
+
console.log(chalk.white(' icoa model <any-model-id> ') + chalk.gray('Custom model'));
|
|
100
|
+
console.log();
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
saveConfig({ geminiModel: name });
|
|
104
|
+
console.log();
|
|
105
|
+
console.log(chalk.green(' Model switched: ') + chalk.gray(current) + chalk.white(' -> ') + chalk.bold.white(name));
|
|
106
|
+
console.log();
|
|
107
|
+
}
|
|
108
|
+
});
|
|
78
109
|
program.parse();
|
package/dist/lib/gemini.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GoogleGenAI } from '@google/genai';
|
|
2
2
|
import { getConfig, saveConfig } from './config.js';
|
|
3
3
|
const SYSTEM_PROMPTS = {
|
|
4
4
|
A: `You are an AI assistant in a cybersecurity CTF competition called ICOA.
|
|
@@ -41,7 +41,6 @@ function filterFlagPatterns(text) {
|
|
|
41
41
|
return text.replace(/icoa\{[^}]*\}/gi, '[FLAG REDACTED]');
|
|
42
42
|
}
|
|
43
43
|
function getApiKey() {
|
|
44
|
-
// Priority: env var > config
|
|
45
44
|
const envKey = process.env.GEMINI_API_KEY;
|
|
46
45
|
if (envKey)
|
|
47
46
|
return envKey;
|
|
@@ -50,10 +49,12 @@ function getApiKey() {
|
|
|
50
49
|
return config.geminiApiKey;
|
|
51
50
|
return '';
|
|
52
51
|
}
|
|
52
|
+
function getClient(apiKey) {
|
|
53
|
+
return new GoogleGenAI({ apiKey });
|
|
54
|
+
}
|
|
53
55
|
export async function generateHint(level, question, context) {
|
|
54
56
|
let apiKey = getApiKey();
|
|
55
57
|
if (!apiKey) {
|
|
56
|
-
// Try to prompt for key interactively
|
|
57
58
|
try {
|
|
58
59
|
const { input } = await import('@inquirer/prompts');
|
|
59
60
|
console.log();
|
|
@@ -76,14 +77,15 @@ export async function generateHint(level, question, context) {
|
|
|
76
77
|
'Set GEMINI_API_KEY environment variable, or run: icoa setup');
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
const config = getConfig();
|
|
81
|
+
const modelName = config.geminiModel || 'gemini-2.5-flash';
|
|
82
|
+
const ai = getClient(apiKey);
|
|
83
|
+
const response = await ai.models.generateContent({
|
|
84
|
+
model: modelName,
|
|
85
|
+
config: { systemInstruction: buildSystemPrompt(level, context) },
|
|
86
|
+
contents: question,
|
|
83
87
|
});
|
|
84
|
-
const
|
|
85
|
-
const response = result.response;
|
|
86
|
-
const text = filterFlagPatterns(response.text());
|
|
88
|
+
const text = filterFlagPatterns(response.text ?? '');
|
|
87
89
|
const usage = response.usageMetadata;
|
|
88
90
|
const tokensUsed = (usage?.promptTokenCount || 0) + (usage?.candidatesTokenCount || 0);
|
|
89
91
|
return { text, tokensUsed };
|
|
@@ -93,15 +95,19 @@ export async function translateText(text, targetLang) {
|
|
|
93
95
|
if (!apiKey) {
|
|
94
96
|
throw new Error('Gemini API key not configured for translation.');
|
|
95
97
|
}
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
+
const config = getConfig();
|
|
99
|
+
const modelName = config.geminiModel || 'gemini-2.5-flash';
|
|
100
|
+
const ai = getClient(apiKey);
|
|
98
101
|
const prompt = `Translate the following CTF challenge description to ${targetLang}.
|
|
99
102
|
Keep all technical terms, code, commands, URLs, and flag formats in English.
|
|
100
103
|
Only translate the narrative/descriptive text.
|
|
101
104
|
|
|
102
105
|
${text}`;
|
|
103
|
-
const
|
|
104
|
-
|
|
106
|
+
const response = await ai.models.generateContent({
|
|
107
|
+
model: modelName,
|
|
108
|
+
contents: prompt,
|
|
109
|
+
});
|
|
110
|
+
return response.text ?? '';
|
|
105
111
|
}
|
|
106
112
|
export function setApiKey(key) {
|
|
107
113
|
saveConfig({ geminiApiKey: key });
|
package/dist/types/index.d.ts
CHANGED
|
@@ -97,6 +97,7 @@ export interface IcoaConfig {
|
|
|
97
97
|
competitionStartsAt: string;
|
|
98
98
|
competitionEndsAt: string;
|
|
99
99
|
geminiApiKey: string;
|
|
100
|
+
geminiModel: string;
|
|
100
101
|
}
|
|
101
102
|
export type CompetitionState = 'pre_competition' | 'demo' | 'live' | 'finished' | 'unknown';
|
|
102
103
|
export type HintLevel = 'A' | 'B' | 'C';
|
package/dist/types/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "icoa-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
],
|
|
25
25
|
"license": "SEE LICENSE IN licenses/apache-2.0.txt",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@google/
|
|
27
|
+
"@google/genai": "^1.48.0",
|
|
28
|
+
"@inquirer/prompts": "^7.5.0",
|
|
28
29
|
"chalk": "^5.4.1",
|
|
29
30
|
"cli-table3": "^0.6.5",
|
|
30
31
|
"commander": "^13.1.0",
|
|
31
|
-
"@inquirer/prompts": "^7.5.0",
|
|
32
32
|
"marked": "^15.0.7",
|
|
33
33
|
"marked-terminal": "^7.3.0",
|
|
34
34
|
"ora": "^8.2.0"
|