icoa-cli 2.19.503 → 2.19.505

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 +1 @@
1
- export function localized(r,t){if(!t.startsWith("zh")||!r._zh)return r;const e=r._zh,n={...r};for(const r of Object.keys(e))void 0!==e[r]&&(n[r]=e[r]);return n}export function isValidCurriculum(r){if(!r||"object"!=typeof r)return!1;const t=r;return"string"==typeof t.id&&"number"==typeof t.totalCards&&Array.isArray(t.cards)&&Array.isArray(t.modules)}import{readFileSync as r,writeFileSync as t,mkdirSync as e,existsSync as n,statSync as a,utimesSync as c}from"node:fs";import{join as i}from"node:path";import{homedir as o}from"node:os";import{getDeviceFingerprint as u}from"./access.js";import{getCliVersion as s}from"./version.js";import{httpFetch as l}from"./http.js";import{parseHometask as d}from"./learn-hometask.js";import{parseFaqs as m,parseReplies as p}from"./learn-replies.js";const f=i(o(),".icoa","learn-cache");export const LEARN_SERVER="https://practice.icoa2026.au";function _(r){const t=r.replace(/[^A-Za-z0-9_-]/g,"_");return i(f,`${t}.json`)}function g(r){const t=r.replace(/[^A-Za-z0-9_-]/g,"_");return i(f,`${t}.etag`)}let h=null;export function getLastCurriculumFailure(){return h}export async function loadCurriculumById(i,o,u=!1){"ctf4eai-12"===i&&(i="LEARNDEMO01");const l=function(t){const e=_(t);if(!n(e))return null;try{const t=a(e),n=Date.now()-t.mtimeMs;return{curriculum:JSON.parse(r(e,"utf-8")),ageMs:n}}catch{return null}}(i);if(!u&&l&&l.ageMs<6048e5&&isValidCurriculum(l.curriculum))return l.curriculum;h=null;const d=await async function(t,e){const a=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(t),c={},i=function(t){const e=g(t);if(!n(e))return null;try{return r(e,"utf-8").trim()||null}catch{return null}}(t);i&&(c["If-None-Match"]=i),e&&(c["X-Learn-Token"]=e.toUpperCase()),c["X-ICOA-Client"]=s();try{const r=await fetch(a,{headers:c,signal:AbortSignal.timeout(6e4)});if(304===r.status)return{kind:"unchanged"};if(404===r.status)return{kind:"notfound"};if(!r.ok)return{kind:"error"};const t=await r.json();if(!t.success||!t.data)return{kind:"error"};if(!isValidCurriculum(t.data))return{kind:"error"};const e=r.headers.get("etag")||r.headers.get("ETag")||void 0;return{kind:"fresh",curriculum:t.data,etag:e||void 0}}catch{return{kind:"error"}}}(i,o);return"fresh"===d.kind?(function(r,n,a){try{e(f,{recursive:!0}),t(_(r),JSON.stringify(n)),a&&t(g(r),a)}catch{}}(i,d.curriculum,d.etag),d.curriculum):"unchanged"===d.kind&&l&&isValidCurriculum(l.curriculum)?(function(r){try{const t=_(r);if(n(t)){const r=Date.now()/1e3;c(t,r,r)}}catch{}}(i),l.curriculum):l&&isValidCurriculum(l.curriculum)?l.curriculum:(h="notfound"===d.kind?"notfound":"offline",null)}export function validateResultShape(r){return{curriculumId:String(r.curriculum_id??""),status:String(r.status??""),validUntil:String(r.valid_until??""),aiEnabled:1===r.ai_enabled||!0===r.ai_enabled,gated:1===r.gated||!0===r.gated,replies:p(r.replies),hometask:d(r.hometask)}}export async function validateEAToken(r){const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const e=await l(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.toUpperCase(),device_fingerprint:u()}),signal:AbortSignal.timeout(8e3)});if(!e.ok){const r=await e.json().catch(()=>({}));return{ok:!1,message:r.message||`HTTP ${e.status}`,code:r.code}}const n=await e.json();return n.success&&n.data?{ok:!0,...validateResultShape(n.data)}:{ok:!1,message:n.message||"Validation failed"}}catch(r){return{ok:!1,message:`Network error: ${r instanceof Error?r.message:String(r)}`}}}export async function syncProgress(r,t,e){if("LEARNDEMO01"===r.toUpperCase())return;const n=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+r.toUpperCase();try{await l(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({card_number:t.card_number,event_type:t.event_type,mcq_answer:t.mcq_answer,mcq_correct:t.mcq_correct?1:0,check_answer:t.check_answer,check_correct:t.check_correct?1:0,time_on_card_ms:t.time_on_card_ms,device_fingerprint:u(),reply_ack:e&&e.length>0?e:void 0}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function fetchCardFaqs(r){const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/faq/"+encodeURIComponent(r.toUpperCase());try{const r=await l(t,{signal:AbortSignal.timeout(5e3)});if(!r.ok)return[];const e=await r.json();return e.success&&e.data?m(e.data.faqs):[]}catch{return[]}}export async function syncCardFeedback(r,t){const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/feedback/"+r.toUpperCase();try{return(await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({curriculum_id:t.curriculum_id,card_number:t.card_number,feedback_text:t.feedback_text,time_on_card_ms:t.time_on_card_ms}),signal:AbortSignal.timeout(5e3)})).ok}catch{return!1}}
1
+ export function localized(r,e){if(!e.startsWith("zh")||!r._zh)return r;const t=r._zh,n={...r};for(const r of Object.keys(t))void 0!==t[r]&&(n[r]=t[r]);return n}export function isValidCurriculum(r){if(!r||"object"!=typeof r)return!1;const e=r;return"string"==typeof e.id&&"number"==typeof e.totalCards&&Array.isArray(e.cards)&&Array.isArray(e.modules)}import{readFileSync as r,writeFileSync as e,mkdirSync as t,existsSync as n,statSync as a,utimesSync as c}from"node:fs";import{join as i}from"node:path";import{homedir as o}from"node:os";import{getDeviceFingerprint as s}from"./access.js";import{getCliVersion as u}from"./version.js";import{httpFetch as l}from"./http.js";import{parseHometask as d}from"./learn-hometask.js";import{parseFaqs as p,parseReplies as m}from"./learn-replies.js";const f=i(o(),".icoa","learn-cache");export const LEARN_SERVER="https://practice.icoa2026.au";function g(r){const e=r.replace(/[^A-Za-z0-9_-]/g,"_");return i(f,`${e}.json`)}function _(r){const e=r.replace(/[^A-Za-z0-9_-]/g,"_");return i(f,`${e}.etag`)}let h=null;export function getLastCurriculumFailure(){return h}export async function loadCurriculumById(i,o,s=!1){"ctf4eai-12"===i&&(i="LEARNDEMO01");const l=function(e){const t=g(e);if(!n(t))return null;try{const e=a(t),n=Date.now()-e.mtimeMs;return{curriculum:JSON.parse(r(t,"utf-8")),ageMs:n}}catch{return null}}(i);if(!s&&l&&l.ageMs<6048e5&&isValidCurriculum(l.curriculum))return l.curriculum;h=null;const d=await async function(e,t){const a=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(e),c={},i=function(e){const t=_(e);if(!n(t))return null;try{return r(t,"utf-8").trim()||null}catch{return null}}(e);i&&(c["If-None-Match"]=i),t&&(c["X-Learn-Token"]=t.toUpperCase()),c["X-ICOA-Client"]=u();try{const r=await fetch(a,{headers:c,signal:AbortSignal.timeout(6e4)});if(304===r.status)return{kind:"unchanged"};if(404===r.status)return{kind:"notfound"};if(!r.ok)return{kind:"error"};const e=await r.json();if(!e.success||!e.data)return{kind:"error"};if(!isValidCurriculum(e.data))return{kind:"error"};const t=r.headers.get("etag")||r.headers.get("ETag")||void 0;return{kind:"fresh",curriculum:e.data,etag:t||void 0}}catch{return{kind:"error"}}}(i,o);return"fresh"===d.kind?(function(r,n,a){try{t(f,{recursive:!0}),e(g(r),JSON.stringify(n)),a&&e(_(r),a)}catch{}}(i,d.curriculum,d.etag),d.curriculum):"unchanged"===d.kind&&l&&isValidCurriculum(l.curriculum)?(function(r){try{const e=g(r);if(n(e)){const r=Date.now()/1e3;c(e,r,r)}}catch{}}(i),l.curriculum):l&&isValidCurriculum(l.curriculum)?l.curriculum:(h="notfound"===d.kind?"notfound":"offline",null)}export function validateResultShape(r){return{curriculumId:String(r.curriculum_id??""),status:String(r.status??""),validUntil:String(r.valid_until??""),aiEnabled:1===r.ai_enabled||!0===r.ai_enabled,gated:1===r.gated||!0===r.gated,replies:m(r.replies),hometask:d(r.hometask)}}export async function validateEAToken(r){const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const t=await l(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.toUpperCase(),device_fingerprint:s()}),signal:AbortSignal.timeout(8e3)});if(!t.ok){const r=await t.json().catch(()=>({}));return{ok:!1,message:r.message||`HTTP ${t.status}`,code:r.code}}const n=await t.json();return n.success&&n.data?{ok:!0,...validateResultShape(n.data)}:{ok:!1,message:n.message||"Validation failed"}}catch(r){return{ok:!1,message:`Network error: ${r instanceof Error?r.message:String(r)}`}}}export async function syncProgress(r,e,t){if("LEARNDEMO01"===r.toUpperCase())return;const n=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+r.toUpperCase();try{await l(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({card_number:e.card_number,event_type:e.event_type,mcq_answer:e.mcq_answer,mcq_correct:e.mcq_correct?1:0,check_answer:e.check_answer,check_correct:e.check_correct?1:0,time_on_card_ms:e.time_on_card_ms,device_fingerprint:s(),reply_ack:t&&t.length>0?t:void 0}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function fetchServerProgress(r){if("LEARNDEMO01"===r.toUpperCase())return null;const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+r.toUpperCase();try{const r=await l(e,{signal:AbortSignal.timeout(5e3)});if(!r.ok)return null;const t=await r.json();return t?.success&&Array.isArray(t.data?.events)?t.data.events:null}catch{return null}}export async function fetchCardFaqs(r){const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/faq/"+encodeURIComponent(r.toUpperCase());try{const r=await l(e,{signal:AbortSignal.timeout(5e3)});if(!r.ok)return[];const t=await r.json();return t.success&&t.data?p(t.data.faqs):[]}catch{return[]}}export async function syncCardFeedback(r,e){const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/feedback/"+r.toUpperCase();try{return(await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({curriculum_id:e.curriculum_id,card_number:e.card_number,feedback_text:e.feedback_text,time_on_card_ms:e.time_on_card_ms}),signal:AbortSignal.timeout(5e3)})).ok}catch{return!1}}
@@ -1 +1 @@
1
- import{join as t}from"node:path";import{getIcoaDir as e,getInstallSalt as n}from"./config.js";import{deriveFileKey as o,readSignedFile as r,writeSignedFile as s,recordVerdict as c}from"./secure-store.js";const a="learn-state";function i(){return t(e(),"learn-state.json")}function l(){return o(n(),a)}const u="learn-sessions";export const CONTINUE_LIST_MAX=5;function p(){return t(e(),"learn-sessions.json")}function d(){return o(n(),u)}function f(){const{data:t,verdict:e}=r(p(),d());if(c(u,e),!t||"object"!=typeof t)return{};const n={};for(const[e,o]of Object.entries(t))o&&"object"==typeof o&&"string"==typeof o.token&&"string"==typeof o.curriculumId&&(n[e]=o);return n}export function listLearnSessions(){const t=loadLearnState(),e=[];t?.token&&t.curriculumId&&e.push(t);for(const[n,o]of Object.entries(f()))n!==t?.token&&e.push(o);return e.sort((t,e)=>(e.lastSeenAt??"").localeCompare(t.lastSeenAt??""))}export function activateSession(t){const e=loadLearnState(),n=f();e?.token&&e.curriculumId&&e.token!==t&&(n[e.token]=e);const o=n[t]??null;return function(t){const e=Object.keys(t);if(e.length>20){const n=e.sort((e,n)=>(t[e].lastSeenAt??"").localeCompare(t[n].lastSeenAt??"")).slice(0,e.length-20);for(const e of n)delete t[e]}s(p(),t,d())}(n),o}export function loadLearnState(){const{data:t,verdict:e}=r(i(),l());return c(a,e),t}export function saveLearnState(t){t.lastSeenAt=(new Date).toISOString(),s(i(),t,l())}export function newLearnState(t,e,n){const o=(new Date).toISOString();return{token:t,curriculumId:e,currentCard:1,totalCards:n,startedAt:o,lastSeenAt:o,streakDays:1,longestStreak:1,cardsCompleted:[],mcqResults:{},checkResults:{},practicalsCompleted:[],bookmarks:[],achievements:[],totalSecondsActive:0}}export function updateStreak(t){const e=new Date(t.lastSeenAt),n=new Date,o=Math.floor(e.getTime()/864e5),r=Math.floor(n.getTime()/864e5)-o;0!==r&&(1===r?(t.streakDays+=1,t.streakDays>t.longestStreak&&(t.longestStreak=t.streakDays)):t.streakDays=1)}export function markCardComplete(t,e){t.cardsCompleted.includes(e)||t.cardsCompleted.push(e)}export function recordMCQ(t,e,n){t.mcqResults[String(e)]=n}export function recordCheck(t,e,n){t.checkResults||(t.checkResults={}),t.checkResults[String(e)]=n}export function markPracticalComplete(t,e){t.practicalsCompleted.includes(e)||t.practicalsCompleted.push(e)}export function addAchievement(t,e){t.achievements.includes(e)||t.achievements.push(e)}
1
+ import{join as e}from"node:path";import{getIcoaDir as t,getInstallSalt as n}from"./config.js";import{deriveFileKey as r,readSignedFile as s,writeSignedFile as o,recordVerdict as c}from"./secure-store.js";const a="learn-state";function i(){return e(t(),"learn-state.json")}function u(){return r(n(),a)}const l="learn-sessions";export const CONTINUE_LIST_MAX=5;function m(){return e(t(),"learn-sessions.json")}function d(){return r(n(),l)}function p(){const{data:e,verdict:t}=s(m(),d());if(c(l,t),!e||"object"!=typeof e)return{};const n={};for(const[t,r]of Object.entries(e))r&&"object"==typeof r&&"string"==typeof r.token&&"string"==typeof r.curriculumId&&(n[t]=r);return n}export function listLearnSessions(){const e=loadLearnState(),t=[];e?.token&&e.curriculumId&&t.push(e);for(const[n,r]of Object.entries(p()))n!==e?.token&&t.push(r);return t.sort((e,t)=>(t.lastSeenAt??"").localeCompare(e.lastSeenAt??""))}export function activateSession(e){const t=loadLearnState(),n=p();t?.token&&t.curriculumId&&t.token!==e&&(n[t.token]=t);const r=n[e]??null;return function(e){const t=Object.keys(e);if(t.length>20){const n=t.sort((t,n)=>(e[t].lastSeenAt??"").localeCompare(e[n].lastSeenAt??"")).slice(0,t.length-20);for(const t of n)delete e[t]}o(m(),e,d())}(n),r}export function loadLearnState(){const{data:e,verdict:t}=s(i(),u());return c(a,t),e}export function saveLearnState(e){e.lastSeenAt=(new Date).toISOString(),o(i(),e,u())}export function newLearnState(e,t,n){const r=(new Date).toISOString();return{token:e,curriculumId:t,currentCard:1,totalCards:n,startedAt:r,lastSeenAt:r,streakDays:1,longestStreak:1,cardsCompleted:[],mcqResults:{},checkResults:{},practicalsCompleted:[],bookmarks:[],achievements:[],totalSecondsActive:0}}export function stateFromServerEvents(e,t,n,r){const s=newLearnState(e,t,n),o=new Set;let c="";for(const e of r){const t=Number(e.card_number);!Number.isInteger(t)||t<1||t>n||("check_answered"!==e.event_type||"y"!==e.check_answer&&"n"!==e.check_answer?"mcq_answered"===e.event_type&&e.mcq_answer&&/^[ABCD]$/.test(e.mcq_answer)?(s.mcqResults[String(t)]={answer:e.mcq_answer,correct:!!e.mcq_correct,submittedAt:e.timestamp},o.add(t)):"viewed"===e.event_type?o.add(t):"practical_done"===e.event_type?(s.practicalsCompleted.push(t),o.add(t)):"bookmarked"===e.event_type&&s.bookmarks.push(t):(s.checkResults[String(t)]={answer:e.check_answer,correct:!!e.check_correct,submittedAt:e.timestamp},o.add(t)),e.timestamp&&(!c||e.timestamp<c)&&(c=e.timestamp))}return 0===o.size?null:(s.cardsCompleted=[...o].sort((e,t)=>e-t),s.currentCard=Math.min(n,Math.max(...o)+1),c&&(s.startedAt=c),s)}export function updateStreak(e){const t=new Date(e.lastSeenAt),n=new Date,r=Math.floor(t.getTime()/864e5),s=Math.floor(n.getTime()/864e5)-r;0!==s&&(1===s?(e.streakDays+=1,e.streakDays>e.longestStreak&&(e.longestStreak=e.streakDays)):e.streakDays=1)}export function markCardComplete(e,t){e.cardsCompleted.includes(t)||e.cardsCompleted.push(t)}export function recordMCQ(e,t,n){e.mcqResults[String(t)]=n}export function recordCheck(e,t,n){e.checkResults||(e.checkResults={}),e.checkResults[String(t)]=n}export function markPracticalComplete(e,t){e.practicalsCompleted.includes(t)||e.practicalsCompleted.push(t)}export function addAchievement(e,t){e.achievements.includes(t)||e.achievements.push(t)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.503",
3
+ "version": "2.19.505",
4
4
  "description": "ICOA CLI — The world's first CLI-native cyber & AI security olympiad terminal: AI4CTF (Day 1), CTF4AI (Day 2), VLA4CTF (Pioneer Round — embodied AI)",
5
5
  "type": "module",
6
6
  "bin": {