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 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 Cybersecurity Olympiad 2026')} ${chalk.cyan('║')}
26
- ${chalk.cyan('║')} ${chalk.gray('CLI-Native CTF Competition Terminal v1.0.0')} ${chalk.cyan('║')}
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();
@@ -1,4 +1,4 @@
1
- import { GoogleGenerativeAI } from '@google/generative-ai';
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 genAI = new GoogleGenerativeAI(apiKey);
80
- const model = genAI.getGenerativeModel({
81
- model: 'gemini-2.5-pro-preview-05-06',
82
- systemInstruction: buildSystemPrompt(level, context),
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 result = await model.generateContent(question);
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 genAI = new GoogleGenerativeAI(apiKey);
97
- const model = genAI.getGenerativeModel({ model: 'gemini-2.5-pro-preview-05-06' });
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 result = await model.generateContent(prompt);
104
- return result.response.text();
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 });
@@ -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';
@@ -25,5 +25,6 @@ export const DEFAULT_CONFIG = {
25
25
  competitionStartsAt: '',
26
26
  competitionEndsAt: '',
27
27
  geminiApiKey: '',
28
+ geminiModel: 'gemini-2.5-flash',
28
29
  };
29
30
  export const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko', 'es'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "1.0.0",
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/generative-ai": "^0.24.0",
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"