icoa-cli 2.19.65 → 2.19.67
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
CHANGED
|
@@ -610,7 +610,10 @@ export function registerAi4ctfCommand(program) {
|
|
|
610
610
|
// token budget tracked in state.aiUsage.ai4ctf.
|
|
611
611
|
const { getRealExamState } = await import('../lib/exam-state.js');
|
|
612
612
|
const realExam = getRealExamState();
|
|
613
|
-
if (
|
|
613
|
+
// Only treat as real exam if state actually has the AI4CTF section (Q31-38).
|
|
614
|
+
// Stale/broken state with truncated questions falls back to demo behavior.
|
|
615
|
+
const hasAi4ctfSection = realExam && realExam.questions.some((qq) => qq.number >= 31 && qq.number <= 38);
|
|
616
|
+
if (realExam && hasAi4ctfSection) {
|
|
614
617
|
const currentQ = realExam._lastQ || 1;
|
|
615
618
|
if (currentQ < 31 || currentQ > 38) {
|
|
616
619
|
console.log();
|
|
@@ -460,7 +460,10 @@ export function registerCtf4aiDemoCommand(program) {
|
|
|
460
460
|
// tracked in state.aiUsage.ctf4ai.
|
|
461
461
|
const { getRealExamState } = await import('../lib/exam-state.js');
|
|
462
462
|
const realExam = getRealExamState();
|
|
463
|
-
if (
|
|
463
|
+
// Only treat as real exam if state actually has the CTF4AI section (Q39-40).
|
|
464
|
+
// Stale/broken state with truncated questions falls back to demo behavior.
|
|
465
|
+
const hasCtf4aiSection = realExam && realExam.questions.some((qq) => qq.number >= 39 && qq.number <= 40);
|
|
466
|
+
if (realExam && hasCtf4aiSection) {
|
|
464
467
|
const currentQ = realExam._lastQ || 1;
|
|
465
468
|
if (currentQ < 39) {
|
|
466
469
|
console.log();
|
package/dist/lib/exam-state.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExamState } from '../types/index.js';
|
|
2
|
-
export declare function getExamState(): ExamState | null;
|
|
3
|
-
export declare function getDemoState(): ExamState | null;
|
|
4
2
|
export declare function getRealExamState(): ExamState | null;
|
|
3
|
+
export declare function getDemoState(): ExamState | null;
|
|
4
|
+
export declare function getExamState(): ExamState | null;
|
|
5
5
|
export declare function saveExamState(state: ExamState): void;
|
|
6
6
|
export declare function clearExamState(examId?: string): void;
|
|
7
7
|
export declare function isExamActive(): boolean;
|
package/dist/lib/exam-state.js
CHANGED
|
@@ -12,28 +12,8 @@ function demoStateFile() {
|
|
|
12
12
|
function resolveStateFile(examId) {
|
|
13
13
|
return examId === 'demo-free' ? demoStateFile() : stateFile();
|
|
14
14
|
}
|
|
15
|
-
export function
|
|
16
|
-
|
|
17
|
-
const files = [stateFile(), demoStateFile()];
|
|
18
|
-
let latest = null;
|
|
19
|
-
let latestTime = 0;
|
|
20
|
-
for (const f of files) {
|
|
21
|
-
if (!existsSync(f))
|
|
22
|
-
continue;
|
|
23
|
-
try {
|
|
24
|
-
const state = JSON.parse(readFileSync(f, 'utf-8'));
|
|
25
|
-
const t = new Date(state.session.confirmedAt || state.session.startedAt).getTime();
|
|
26
|
-
if (t > latestTime) {
|
|
27
|
-
latest = state;
|
|
28
|
-
latestTime = t;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
catch { }
|
|
32
|
-
}
|
|
33
|
-
return latest;
|
|
34
|
-
}
|
|
35
|
-
export function getDemoState() {
|
|
36
|
-
const f = demoStateFile();
|
|
15
|
+
export function getRealExamState() {
|
|
16
|
+
const f = stateFile();
|
|
37
17
|
if (!existsSync(f))
|
|
38
18
|
return null;
|
|
39
19
|
try {
|
|
@@ -43,8 +23,8 @@ export function getDemoState() {
|
|
|
43
23
|
return null;
|
|
44
24
|
}
|
|
45
25
|
}
|
|
46
|
-
export function
|
|
47
|
-
const f =
|
|
26
|
+
export function getDemoState() {
|
|
27
|
+
const f = demoStateFile();
|
|
48
28
|
if (!existsSync(f))
|
|
49
29
|
return null;
|
|
50
30
|
try {
|
|
@@ -54,6 +34,18 @@ export function getRealExamState() {
|
|
|
54
34
|
return null;
|
|
55
35
|
}
|
|
56
36
|
}
|
|
37
|
+
export function getExamState() {
|
|
38
|
+
// Real exam ALWAYS takes priority over demo when both exist.
|
|
39
|
+
// Reasoning: real exam is token-gated, has a timer, and represents a serious
|
|
40
|
+
// commitment. Demo is casual practice. If a real exam is in progress, all
|
|
41
|
+
// shared commands (exam q N, exam answer, ai4ctf, ctf4ai) must target the
|
|
42
|
+
// real exam — never silently fall back to demo questions, even if demo was
|
|
43
|
+
// run more recently. (Demo→Exam→Finals progression principle, exam.md §0)
|
|
44
|
+
const real = getRealExamState();
|
|
45
|
+
if (real)
|
|
46
|
+
return real;
|
|
47
|
+
return getDemoState();
|
|
48
|
+
}
|
|
57
49
|
export function saveExamState(state) {
|
|
58
50
|
const f = resolveStateFile(state.session.examId);
|
|
59
51
|
writeFileSync(f, JSON.stringify(state, null, 2));
|