icoa-cli 2.19.68 → 2.19.70

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.
@@ -17,7 +17,19 @@ export function getRealExamState() {
17
17
  if (!existsSync(f))
18
18
  return null;
19
19
  try {
20
- return JSON.parse(readFileSync(f, 'utf-8'));
20
+ const state = JSON.parse(readFileSync(f, 'utf-8'));
21
+ // Pre-v2.19.45 versions stored demo state in exam-state.json with
22
+ // examId='demo-free'. After v2.19.45 demo moved to demo-state.json.
23
+ // Ignore stale demo-tagged content found in the real-exam file —
24
+ // it's contamination from an old install. Also auto-clean the file.
25
+ if (state?.session?.examId === 'demo-free') {
26
+ try {
27
+ unlinkSync(f);
28
+ }
29
+ catch { }
30
+ return null;
31
+ }
32
+ return state;
21
33
  }
22
34
  catch {
23
35
  return null;
package/dist/repl.js CHANGED
@@ -101,6 +101,23 @@ export async function startRepl(program, resumeMode) {
101
101
  const connected = isConnected();
102
102
  const realExit = process.exit.bind(process);
103
103
  const activated = isActivated();
104
+ // Auto-cleanup: clear demo state once per version upgrade.
105
+ // Demo is free practice with no time pressure, no scoring, no real loss
106
+ // for users. Old versions may have left demo state in incompatible formats
107
+ // (pre-v2.19.45 shared state, pre-v2.19.67 timestamp confusion, etc.).
108
+ // Real exam state is NEVER auto-cleared — it may contain in-progress answers.
109
+ if (config.demoCleanedForVersion !== VERSION) {
110
+ try {
111
+ const { existsSync, unlinkSync } = await import('node:fs');
112
+ const { join } = await import('node:path');
113
+ const { getIcoaDir } = await import('./lib/config.js');
114
+ const demoFile = join(getIcoaDir(), 'demo-state.json');
115
+ if (existsSync(demoFile))
116
+ unlinkSync(demoFile);
117
+ }
118
+ catch { }
119
+ saveConfig({ demoCleanedForVersion: VERSION });
120
+ }
104
121
  // ─── Mode selection (every launch) ───
105
122
  const { select: selectMode, confirm: confirmMode } = await import('@inquirer/prompts');
106
123
  const savedMode = config.mode || '';
@@ -101,6 +101,7 @@ export interface IcoaConfig {
101
101
  accessToken: string;
102
102
  deviceFingerprint: string;
103
103
  lastVersion: string;
104
+ demoCleanedForVersion?: string;
104
105
  sessionCookie: string;
105
106
  country: string;
106
107
  mode: IcoaMode | '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.68",
3
+ "version": "2.19.70",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {