icoa-cli 2.19.77 → 2.19.78

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.
Files changed (2) hide show
  1. package/dist/lib/gemini.js +46 -10
  2. package/package.json +1 -1
@@ -1,6 +1,24 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
2
  import chalk from 'chalk';
3
+ import { readFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
3
6
  import { getConfig, saveConfig } from './config.js';
7
+ import { getRealExamState } from './exam-state.js';
8
+ const __dirname_gemini = dirname(fileURLToPath(import.meta.url));
9
+ let _cachedVersion = null;
10
+ function getCliVersion() {
11
+ if (_cachedVersion)
12
+ return _cachedVersion;
13
+ try {
14
+ const pkg = JSON.parse(readFileSync(join(__dirname_gemini, '..', '..', 'package.json'), 'utf-8'));
15
+ _cachedVersion = pkg.version || 'unknown';
16
+ }
17
+ catch {
18
+ _cachedVersion = 'unknown';
19
+ }
20
+ return _cachedVersion;
21
+ }
4
22
  const SYSTEM_PROMPTS = {
5
23
  A: `You are an AI assistant in a cybersecurity CTF competition called ICOA.
6
24
  You are providing Level A (General Guidance) to a competitor.
@@ -170,24 +188,42 @@ export async function createChatSession(context, customSystemPrompt) {
170
188
  return {
171
189
  async sendMessage(msg) {
172
190
  messages.push({ role: 'user', text: msg });
191
+ // Attach exam token if contestant is in a real exam — grants full
192
+ // 2048 maxTokens and higher rate limit. Demo/anonymous users rely on
193
+ // the User-Agent gate on the server side.
194
+ const realExam = getRealExamState();
195
+ const payload = {
196
+ systemPrompt,
197
+ messages,
198
+ model: modelName,
199
+ maxTokens: 2048,
200
+ deviceFingerprint: fp,
201
+ };
202
+ if (realExam?.session?.token) {
203
+ payload.examToken = realExam.session.token;
204
+ }
173
205
  const res = await fetch(`${serverUrl}/api/icoa/ai/chat`, {
174
206
  method: 'POST',
175
- headers: { 'Content-Type': 'application/json' },
176
- body: JSON.stringify({
177
- systemPrompt,
178
- messages,
179
- model: modelName,
180
- maxTokens: 2048,
181
- deviceFingerprint: fp,
182
- }),
207
+ headers: {
208
+ 'Content-Type': 'application/json',
209
+ 'User-Agent': `icoa-cli/${getCliVersion()}`,
210
+ },
211
+ body: JSON.stringify(payload),
183
212
  signal: AbortSignal.timeout(60_000),
184
213
  });
185
214
  if (!res.ok) {
186
215
  const err = await res.json().catch(() => ({ message: 'AI proxy error' }));
216
+ const msg = err.message || `AI proxy returned ${res.status}`;
217
+ if (res.status === 401) {
218
+ throw new Error(chalk.yellow('⚠ ') + 'Exam token expired. Re-enter via `exam <token>`.');
219
+ }
220
+ if (res.status === 403) {
221
+ throw new Error(chalk.yellow('⚠ ') + msg);
222
+ }
187
223
  if (res.status === 429) {
188
- throw new Error(chalk.yellow('⏳ ') + (err.message || 'Too many requests. Please wait a moment and try again.'));
224
+ throw new Error(chalk.yellow('⏳ ') + msg);
189
225
  }
190
- throw new Error(err.message || `AI proxy returned ${res.status}`);
226
+ throw new Error(msg);
191
227
  }
192
228
  const json = await res.json();
193
229
  const text = filterFlagPatterns(json.data?.text || '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.77",
3
+ "version": "2.19.78",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {