icoa-cli 2.16.17 → 2.17.0

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.
@@ -1,9 +1,9 @@
1
1
  import chalk from 'chalk';
2
2
  import { createChatSession } from '../lib/gemini.js';
3
- import { isTokenCapReached, addTokenUsage, getTokenUsage } from '../lib/budget.js';
3
+ import { addTokenUsage } from '../lib/budget.js';
4
4
  import { getConfig } from '../lib/config.js';
5
5
  import { logCommand } from '../lib/logger.js';
6
- import { printMarkdown, printError, printInfo } from '../lib/ui.js';
6
+ import { printMarkdown, printError } from '../lib/ui.js';
7
7
  function getChallengeContext() {
8
8
  const config = getConfig();
9
9
  if (config.currentChallengeName && config.currentChallengeCategory) {
@@ -14,9 +14,21 @@ function getChallengeContext() {
14
14
  // Chat state — shared with REPL
15
15
  let chatActive = false;
16
16
  let chatSession = null;
17
+ let chatTokensUsed = 0;
18
+ const DEMO_TOKEN_CAP = 5000;
17
19
  export function isChatActive() {
18
20
  return chatActive;
19
21
  }
22
+ function drawTokenBar() {
23
+ const cap = DEMO_TOKEN_CAP;
24
+ const used = chatTokensUsed;
25
+ const pct = Math.min(Math.round((used / cap) * 100), 100);
26
+ const width = 20;
27
+ const filled = Math.round((pct / 100) * width);
28
+ const empty = width - filled;
29
+ const color = pct > 80 ? chalk.red : pct > 50 ? chalk.yellow : chalk.green;
30
+ console.log(chalk.gray(' Tokens: ') + color('█'.repeat(filled)) + chalk.gray('░'.repeat(empty)) + chalk.gray(` ${used}/${cap} (${pct}%)`));
31
+ }
20
32
  export async function handleChatMessage(input) {
21
33
  if (!chatSession)
22
34
  return 'exit';
@@ -24,14 +36,31 @@ export async function handleChatMessage(input) {
24
36
  chatActive = false;
25
37
  chatSession = null;
26
38
  console.log();
27
- printInfo('Returning to ICOA terminal.');
39
+ console.log(chalk.gray(' ─────────────────────────────────────────'));
40
+ console.log(chalk.white(' AI4CTF Session Report'));
41
+ console.log(chalk.gray(` Tokens used: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
42
+ console.log(chalk.gray(` AI Model: Google Gemma 4 (gemma-4-31b-it)`));
43
+ console.log(chalk.gray(' ─────────────────────────────────────────'));
44
+ console.log();
45
+ console.log(chalk.white(' Next: try ') + chalk.bold.red('ctf4ai') + chalk.white(' — trick the AI into saying "koala"'));
28
46
  console.log();
29
47
  return 'exit';
30
48
  }
31
- if (isTokenCapReached()) {
49
+ if (chatTokensUsed >= DEMO_TOKEN_CAP) {
32
50
  chatActive = false;
33
51
  chatSession = null;
34
- printError('Token budget exhausted. Exiting chat mode.');
52
+ console.log();
53
+ console.log(chalk.yellow(' Token limit reached!'));
54
+ drawTokenBar();
55
+ console.log();
56
+ console.log(chalk.gray(' ─────────────────────────────────────────'));
57
+ console.log(chalk.white(' AI4CTF Session Report'));
58
+ console.log(chalk.gray(` Tokens used: ${chatTokensUsed}/${DEMO_TOKEN_CAP}`));
59
+ console.log(chalk.gray(` AI Model: Google Gemma 4 (gemma-4-31b-it)`));
60
+ console.log(chalk.gray(' ─────────────────────────────────────────'));
61
+ console.log();
62
+ console.log(chalk.white(' Next: try ') + chalk.bold.red('ctf4ai') + chalk.white(' — trick the AI into saying "koala"'));
63
+ console.log();
35
64
  return 'exit';
36
65
  }
37
66
  logCommand(`ai4ctf: ${input}`);
@@ -39,11 +68,11 @@ export async function handleChatMessage(input) {
39
68
  try {
40
69
  const response = await chatSession.sendMessage(input);
41
70
  process.stdout.write('\x1b[1A\x1b[2K');
71
+ chatTokensUsed += response.tokensUsed;
42
72
  addTokenUsage(response.tokensUsed);
43
- const updated = getTokenUsage();
44
73
  console.log();
45
74
  printMarkdown(response.text);
46
- console.log(chalk.gray(` [${response.tokensUsed.toLocaleString()} tokens | ${updated.used.toLocaleString()}/${updated.cap.toLocaleString()} total]`));
75
+ drawTokenBar();
47
76
  console.log();
48
77
  }
49
78
  catch (err) {
@@ -59,32 +88,55 @@ export function registerAi4ctfCommand(program) {
59
88
  .description('Chat with your AI teammate')
60
89
  .action(async () => {
61
90
  logCommand('ai4ctf');
62
- if (isTokenCapReached()) {
63
- printError('Token budget exhausted. No more AI interactions available.');
64
- return;
65
- }
66
- const context = getChallengeContext();
67
- const tokenState = getTokenUsage();
91
+ const config = getConfig();
92
+ const modelName = config.geminiModel || 'gemma-4-31b-it';
93
+ // Demo challenge context
94
+ const demoContext = {
95
+ name: 'Hidden Message',
96
+ category: 'Cryptography',
97
+ };
68
98
  try {
69
- chatSession = await createChatSession(context);
99
+ chatSession = await createChatSession(demoContext);
70
100
  }
71
101
  catch (err) {
72
102
  printError(err.message);
73
103
  return;
74
104
  }
75
105
  chatActive = true;
76
- // Welcome banner
106
+ chatTokensUsed = 0;
107
+ // Guided welcome
77
108
  console.log();
78
- console.log(chalk.magenta(' ┌─────────────────────────────────────────┐'));
79
- console.log(chalk.magenta(' │') + chalk.bold.white(' AI Teammate — Chat Mode') + chalk.magenta(' │'));
80
- if (context) {
81
- const ctxStr = ` Challenge: ${context.name} (${context.category})`;
82
- console.log(chalk.magenta(' ') + chalk.gray(ctxStr.padEnd(41)) + chalk.magenta('│'));
83
- }
84
- const tokenStr = ` Tokens: ${tokenState.used.toLocaleString()}/${tokenState.cap.toLocaleString()}`;
85
- console.log(chalk.magenta(' │') + chalk.gray(tokenStr.padEnd(41)) + chalk.magenta('│'));
86
- console.log(chalk.magenta(' │') + chalk.gray(" Type 'exit' to return".padEnd(41)) + chalk.magenta('│'));
87
- console.log(chalk.magenta(' └─────────────────────────────────────────┘'));
109
+ console.log(chalk.green.bold(' ═══ AI4CTF Demo — AI as Your Teammate ═══'));
110
+ console.log();
111
+ console.log(chalk.white(' Here\'s a sample CTF challenge:'));
112
+ console.log();
113
+ console.log(chalk.cyan(' ┌─────────────────────────────────────────────────┐'));
114
+ console.log(chalk.cyan(' │') + chalk.bold.white(' Challenge: Hidden Message [Cryptography] ') + chalk.cyan('│'));
115
+ console.log(chalk.cyan(' │') + chalk.white(' ') + chalk.cyan('│'));
116
+ console.log(chalk.cyan(' │') + chalk.white(' You intercepted this encoded text: ') + chalk.cyan('│'));
117
+ console.log(chalk.cyan(' │') + chalk.green(' aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ== ') + chalk.cyan('│'));
118
+ console.log(chalk.cyan(' ') + chalk.white(' ') + chalk.cyan('│'));
119
+ console.log(chalk.cyan(' │') + chalk.white(' Can you decode it to find the flag? ') + chalk.cyan('│'));
120
+ console.log(chalk.cyan(' │') + chalk.gray(' Flag format: icoa{...} ') + chalk.cyan('│'));
121
+ console.log(chalk.cyan(' └─────────────────────────────────────────────────┘'));
122
+ console.log();
123
+ console.log(chalk.white(' In competition, you get AI help at 3 levels:'));
124
+ console.log();
125
+ console.log(chalk.yellow(' hint a') + chalk.gray(' General guidance — "What type of encoding is this?"'));
126
+ console.log(chalk.gray(' 50 uses per competition. Safe to use freely.'));
127
+ console.log(chalk.yellow(' hint b') + chalk.gray(' Deep analysis — "How do I decode Base64?"'));
128
+ console.log(chalk.gray(' 10 uses. Use when you\'re stuck.'));
129
+ console.log(chalk.yellow(' hint c') + chalk.gray(' Critical assist — Nearly gives you the answer'));
130
+ console.log(chalk.gray(' 2 uses only. Last resort!'));
131
+ console.log();
132
+ console.log(chalk.gray(' ─────────────────────────────────────────'));
133
+ console.log(chalk.white(' Try now: ask the AI anything about the challenge above.'));
134
+ console.log(chalk.gray(' Example: "What encoding is aWNvYXt3M2xjMG1lXzJfYWk0Y3RmfQ==?"'));
135
+ console.log(chalk.gray(' Or just chat freely! You can also try hint a, hint b, hint c.'));
136
+ console.log();
137
+ drawTokenBar();
138
+ console.log(chalk.gray(` AI Model: Google Gemma 4 (${modelName})`));
139
+ console.log(chalk.gray(' Type "exit" when done.'));
88
140
  console.log();
89
141
  });
90
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.16.17",
3
+ "version": "2.17.0",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {