icoa-cli 2.19.14 → 2.19.15
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/exam.js +33 -5
- package/dist/lib/access.d.ts +1 -0
- package/dist/lib/access.js +1 -1
- package/dist/lib/demo-exam.js +1 -1
- package/package.json +1 -1
package/dist/commands/exam.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getExamState, saveExamState, clearExamState, getExamDeadline } from '..
|
|
|
6
6
|
import { logCommand } from '../lib/logger.js';
|
|
7
7
|
import { printSuccess, printError, printWarning, printInfo, printTable, printHeader, printKeyValue, createSpinner, formatCountdown, } from '../lib/ui.js';
|
|
8
8
|
import { t } from '../lib/i18n.js';
|
|
9
|
+
import { getDeviceFingerprint } from '../lib/access.js';
|
|
9
10
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
10
11
|
function drawProgress(percent, label) {
|
|
11
12
|
const width = 30;
|
|
@@ -779,12 +780,35 @@ export function registerExamCommand(program) {
|
|
|
779
780
|
return;
|
|
780
781
|
}
|
|
781
782
|
// Real exam: submit to server
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
783
|
+
// Token-based exam: use direct fetch with exam token
|
|
784
|
+
// CTFd-based exam: use ExamClient
|
|
785
|
+
const examToken = state.session.token;
|
|
785
786
|
try {
|
|
786
787
|
drawProgress(0, 'Uploading answers...');
|
|
787
|
-
|
|
788
|
+
let result;
|
|
789
|
+
if (examToken) {
|
|
790
|
+
// Submit via exam token
|
|
791
|
+
const config = getConfig();
|
|
792
|
+
const serverUrl = config.ctfdUrl || 'https://practice.icoa2026.au';
|
|
793
|
+
const res = await fetch(`${serverUrl}/api/icoa/exams/${state.session.examId}/submit`, {
|
|
794
|
+
method: 'POST',
|
|
795
|
+
headers: { 'Content-Type': 'application/json' },
|
|
796
|
+
body: JSON.stringify({ token: examToken, answers: state.answers }),
|
|
797
|
+
signal: AbortSignal.timeout(10000),
|
|
798
|
+
});
|
|
799
|
+
if (!res.ok) {
|
|
800
|
+
const err = await res.json().catch(() => ({ message: 'Submit failed' }));
|
|
801
|
+
throw new Error(err.message || 'Submit failed');
|
|
802
|
+
}
|
|
803
|
+
const json = await res.json();
|
|
804
|
+
result = json.data;
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
const client = requireExamConnection();
|
|
808
|
+
if (!client)
|
|
809
|
+
return;
|
|
810
|
+
result = await client.submitExam(state.session.examId, state.answers);
|
|
811
|
+
}
|
|
788
812
|
drawProgress(50, 'Grading...');
|
|
789
813
|
await sleep(300);
|
|
790
814
|
drawProgress(100, 'Complete!');
|
|
@@ -859,7 +883,11 @@ export function registerExamCommand(program) {
|
|
|
859
883
|
const res = await fetch(`${serverUrl}/api/icoa/exam-token`, {
|
|
860
884
|
method: 'POST',
|
|
861
885
|
headers: { 'Content-Type': 'application/json' },
|
|
862
|
-
body: JSON.stringify({
|
|
886
|
+
body: JSON.stringify({
|
|
887
|
+
token: code.trim(),
|
|
888
|
+
deviceHash: getDeviceFingerprint(),
|
|
889
|
+
lang: config.language || 'en',
|
|
890
|
+
}),
|
|
863
891
|
signal: AbortSignal.timeout(10000),
|
|
864
892
|
});
|
|
865
893
|
if (!res.ok) {
|
package/dist/lib/access.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare function isFirstRunOrUpgrade(currentVersion: string): boolean;
|
|
|
2
2
|
export declare function markVersionSeen(version: string): void;
|
|
3
3
|
export declare function validateToken(token: string): boolean;
|
|
4
4
|
export declare function isActivated(): boolean;
|
|
5
|
+
export declare function getDeviceFingerprint(): string;
|
|
5
6
|
export type ActivateResult = 'ok' | 'invalid' | 'already_bound';
|
|
6
7
|
export declare function activateToken(token: string): ActivateResult;
|
|
7
8
|
export declare function isDeviceMatch(): boolean;
|
package/dist/lib/access.js
CHANGED
|
@@ -127,7 +127,7 @@ export function isActivated() {
|
|
|
127
127
|
const config = getConfig();
|
|
128
128
|
return !!(config.accessToken && TOKEN_HASHES.has(hashToken(config.accessToken)));
|
|
129
129
|
}
|
|
130
|
-
function getDeviceFingerprint() {
|
|
130
|
+
export function getDeviceFingerprint() {
|
|
131
131
|
const parts = [hostname(), platform(), arch()];
|
|
132
132
|
// Add hardware UUID where possible
|
|
133
133
|
try {
|
package/dist/lib/demo-exam.js
CHANGED
|
@@ -98,7 +98,7 @@ export function getLocalizedDemoQuestions() {
|
|
|
98
98
|
return DEMO_QUESTIONS;
|
|
99
99
|
try {
|
|
100
100
|
const data = JSON.parse(readFileSync(path, 'utf-8'));
|
|
101
|
-
if (Array.isArray(data) && data.length
|
|
101
|
+
if (Array.isArray(data) && data.length >= DEMO_QUESTIONS.length)
|
|
102
102
|
return data;
|
|
103
103
|
}
|
|
104
104
|
catch { }
|