icoa-cli 2.19.602 → 2.19.603

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
- import{getDeviceFingerprint as e}from"./access.js";import{LEARN_SERVER as t}from"./learn-curricula.js";import{httpFetch as a}from"./http.js";import{getConfig as r}from"./config.js";function o(e,t){const a=(r().language||"en").toLowerCase().startsWith("zh");return 502===e||503===e||504===e?a?"服务这次没能在窗口内答复 — 你的内容已安全存在服务器上。稍等一会再试即可。":"The server did not answer in time — your work is safe on the server. Wait a moment and try again.":t||`HTTP ${e}`}export async function submitPqs(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/submit";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(18e4)}),n=await t.json().catch(()=>({}));if(!t.ok||!n.success||!n.data)return{ok:!1,message:o(t.status,n.message),code:n.code};const s=n.data;return{ok:!0,score:s.score,tier:s.tier,manipulation:s.manipulation,feedback:s.feedback,improve:s.improve,submissionIndex:s.submission_index,submissionsLeft:s.submissions_left,rank:s.rank,entrants:s.entrants,inTop3:s.in_top3,turns:s.turns,spent:s.spent,cap:s.cap}}catch(e){return{ok:!1,message:e instanceof Error&&"TimeoutError"===e.name?"PQS grading timed out — your conversation is safe on the server; try /submit again in a minute.":`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewPqsTop(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/view";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,top3:n.data.top3,myRank:n.data.my_rank,entrants:n.data.entrants}:{ok:!1,message:o(t.status,n.message),code:n.code,myRank:n.rank,entrants:n.entrants}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function fetchMyScores(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/myscores";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_lo:a.cardLo,card_hi:a.cardHi,device_fingerprint:e()}),signal:AbortSignal.timeout(8e3)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,scores:n.data.scores??[]}:{ok:!1,message:o(t.status,n.message),code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewHonors(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/honors";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),curriculum_id:a.curriculumId,card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,board:n.data.board,mine:n.data.mine??null,nickname:n.data.nickname??null,totals:n.data.totals,chapters:n.data.chapters,floor:n.data.floor,phaseBoard:n.data.phaseBoard??null,practiceBoard:n.data.practiceBoard??null}:{ok:!1,message:o(t.status,n.message),code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function setNickname(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/nickname";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),nickname:a.nickname,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,nickname:n.data.nickname}:{ok:!1,message:o(t.status,n.message),code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function recallCoach(r){const n=t.replace(/\/$/,"")+"/api/icoa/learn/recall";try{const t=await a(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),device_fingerprint:e(),phase:r.phase,slot:r.slot,problem_title:r.problemTitle,statement:r.statement,code:r.code,level:r.level,lang:r.lang}),signal:AbortSignal.timeout(55e3)}),s=await t.json().catch(()=>({}));return t.ok&&s.success&&s.data?.hint?{ok:!0,hint:s.data.hint,level:s.data.level,spent:s.data.spent,cap:s.data.cap}:{ok:!1,message:o(t.status,s.message),code:s.code}}catch(e){return{ok:!1,message:e instanceof Error&&"TimeoutError"===e.name?"Coach timed out — try again in a minute.":`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function streamLearnTutor(r,n){const s=t.replace(/\/$/,"")+"/api/icoa/learn/ai",i=new AbortController;let c=setTimeout(()=>i.abort(),6e4);const d=()=>{clearTimeout(c),c=setTimeout(()=>i.abort(),6e4)};try{const t=await a(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_number:r.cardNumber,question:r.question,mode:r.mode,history:r.history?.slice(-6),device_fingerprint:e()}),signal:i.signal});if(!t.ok||!t.body){const e=await t.json().catch(()=>({}));return{ok:!1,answer:"",message:e.message||o(t.status),code:e.code,spent:e.spent,cap:e.cap}}const c=t.body.getReader(),m=new TextDecoder;let p="",u="",l=null,g=null;for(;;){const e=await c.read();if(e.done)break;d(),p+=m.decode(e.value,{stream:!0});const t=p.split("\n");p=t.pop()??"";for(const e of t){const t=e.trim();if(!t.startsWith("data:"))continue;let a;try{a=JSON.parse(t.slice(5).trim())}catch{continue}a.r&&n({kind:"reasoning",text:a.r}),a.c&&(u+=a.c,n({kind:"content",text:a.c})),a.error&&(g=a.error),a.done&&(l=a)}}return g&&!u?{ok:!1,answer:"",message:g}:u?{ok:!0,answer:u,offtopic:!0===l?.offtopic,spent:l?.spent,cap:l?.cap}:{ok:!1,answer:"",message:"No answer received — try again."}}catch(e){return{ok:!1,answer:"",message:e instanceof Error&&"AbortError"===e.name?"AI Panda Tutor timed out (no data for 60s) — try again.":`Network error: ${e instanceof Error?e.message:String(e)}`}}finally{clearTimeout(c)}}
1
+ import{getDeviceFingerprint as e}from"./access.js";import{LEARN_SERVER as t}from"./learn-curricula.js";import{httpFetch as a}from"./http.js";import{getConfig as r}from"./config.js";function o(e,t){const a=(r().language||"en").toLowerCase().startsWith("zh");return 502===e||503===e||504===e?a?"服务这次没能在窗口内答复 — 你的内容已安全存在服务器上。稍等一会再试即可。":"The server did not answer in time — your work is safe on the server. Wait a moment and try again.":t||`HTTP ${e}`}export async function submitPqs(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/submit";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(18e4)}),n=await t.json().catch(()=>({}));if(!t.ok||!n.success||!n.data)return{ok:!1,message:o(t.status,n.message),code:n.code};const s=n.data;return{ok:!0,score:s.score,tier:s.tier,manipulation:s.manipulation,feedback:s.feedback,improve:s.improve,submissionIndex:s.submission_index,submissionsLeft:s.submissions_left,rank:s.rank,entrants:s.entrants,inTop3:s.in_top3,turns:s.turns,spent:s.spent,cap:s.cap}}catch(e){return{ok:!1,message:e instanceof Error&&"TimeoutError"===e.name?"PQS grading timed out — your conversation is safe on the server; try /submit again in a minute.":`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewPqsTop(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/view";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,top3:n.data.top3,myRank:n.data.my_rank,entrants:n.data.entrants}:{ok:!1,message:o(t.status,n.message),code:n.code,myRank:n.rank,entrants:n.entrants}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function fetchMyScores(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/myscores";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_lo:a.cardLo,card_hi:a.cardHi,device_fingerprint:e()}),signal:AbortSignal.timeout(8e3)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,scores:n.data.scores??[]}:{ok:!1,message:o(t.status,n.message),code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewHonors(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/honors";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),curriculum_id:a.curriculumId,card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,board:n.data.board,mine:n.data.mine??null,nickname:n.data.nickname??null,totals:n.data.totals,chapters:n.data.chapters,floor:n.data.floor,phaseBoard:n.data.phaseBoard??null,practiceBoard:n.data.practiceBoard??null}:{ok:!1,message:o(t.status,n.message),code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function setNickname(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/nickname";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),nickname:a.nickname,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,nickname:n.data.nickname}:{ok:!1,message:o(t.status,n.message),code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function recallCoach(r){const n=t.replace(/\/$/,"")+"/api/icoa/learn/recall";try{const t=await a(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),device_fingerprint:e(),phase:r.phase,slot:r.slot,problem_title:r.problemTitle,statement:r.statement,code:r.code,level:r.level,lang:r.lang}),signal:AbortSignal.timeout(55e3)}),s=await t.json().catch(()=>({}));return t.ok&&s.success&&s.data?.hint?{ok:!0,hint:s.data.hint,level:s.data.level,spent:s.data.spent,cap:s.data.cap}:{ok:!1,message:o(t.status,s.message),code:s.code}}catch(e){return{ok:!1,message:e instanceof Error&&"TimeoutError"===e.name?"Coach timed out — try again in a minute.":`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function streamLearnTutor(r,n){const s=t.replace(/\/$/,"")+"/api/icoa/learn/ai",i=new AbortController;let c=setTimeout(()=>i.abort(),6e4);const d=()=>{clearTimeout(c),c=setTimeout(()=>i.abort(),6e4)};try{const t=await a(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_number:r.cardNumber,question:r.question,mode:r.mode,history:r.history?.slice(-6),device_fingerprint:e()}),signal:i.signal});if(!t.ok||!t.body){const e=await t.json().catch(()=>({}));return{ok:!1,answer:"",message:e.message||o(t.status),code:e.code,spent:e.spent,cap:e.cap,tier:e.tier,cardLeft:e.card_left,cardCap:e.card_cap}}const c=t.body.getReader(),m=new TextDecoder;let p="",u="",l=null,g=null;for(;;){const e=await c.read();if(e.done)break;d(),p+=m.decode(e.value,{stream:!0});const t=p.split("\n");p=t.pop()??"";for(const e of t){const t=e.trim();if(!t.startsWith("data:"))continue;let a;try{a=JSON.parse(t.slice(5).trim())}catch{continue}a.r&&n({kind:"reasoning",text:a.r}),a.c&&(u+=a.c,n({kind:"content",text:a.c})),a.error&&(g=a.error),a.done&&(l=a)}}return g&&!u?{ok:!1,answer:"",message:g}:u?{ok:!0,answer:u,offtopic:!0===l?.offtopic,spent:l?.spent,cap:l?.cap,tier:l?.tier,cardLeft:l?.card_left,cardCap:l?.card_cap}:{ok:!1,answer:"",message:"No answer received — try again."}}catch(e){return{ok:!1,answer:"",message:e instanceof Error&&"AbortError"===e.name?"AI Panda Tutor timed out (no data for 60s) — try again.":`Network error: ${e instanceof Error?e.message:String(e)}`}}finally{clearTimeout(c)}}
@@ -1 +1 @@
1
- export function localized(t,e){if(!e.startsWith("zh")||!t._zh)return t;const r=t._zh,n={...t};for(const t of Object.keys(r))void 0!==r[t]&&(n[t]=r[t]);return n}export function isValidCurriculum(t){if(!t||"object"!=typeof t)return!1;const e=t;return"string"==typeof e.id&&"number"==typeof e.totalCards&&Array.isArray(e.cards)&&Array.isArray(e.modules)}import{readFileSync as t,writeFileSync as e,mkdirSync as r,existsSync as n,statSync as a,utimesSync as i}from"node:fs";import{join as o}from"node:path";import{homedir as c}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{transportAdvice as d,transportCauseCode as p}from"./tls-trust.js";import{parseHometask as f}from"./learn-hometask.js";import{parseFaqs as m,parseReplies as g}from"./learn-replies.js";const _=o(c(),".icoa","learn-cache");export const LEARN_SERVER="https://practice.icoa2026.au";function h(t){const e=t.replace(/[^A-Za-z0-9_-]/g,"_");return o(_,`${e}.json`)}function y(t){const e=t.replace(/[^A-Za-z0-9_-]/g,"_");return o(_,`${e}.etag`)}let E=null;export function getLastCurriculumFailure(){return E}export async function loadCurriculumById(o,c,s=!1){"ctf4eai-12"===o&&(o="LEARNDEMO01");const l=function(e){const r=h(e);if(!n(r))return null;try{const e=a(r),n=Date.now()-e.mtimeMs;return{curriculum:JSON.parse(t(r,"utf-8")),ageMs:n}}catch{return null}}(o);if(!s&&l&&l.ageMs<6048e5&&isValidCurriculum(l.curriculum))return l.curriculum;E=null;const d=await async function(e,r){const a=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(e),i={},o=function(e){const r=y(e);if(!n(r))return null;try{return t(r,"utf-8").trim()||null}catch{return null}}(e);o&&(i["If-None-Match"]=o),r&&(i["X-Learn-Token"]=r.toUpperCase()),i["X-ICOA-Client"]=u();try{const t=await fetch(a,{headers:i,signal:AbortSignal.timeout(6e4)});if(304===t.status)return{kind:"unchanged"};if(404===t.status)return{kind:"notfound"};if(!t.ok)return{kind:"error"};const e=await t.json();if(!e.success||!e.data)return{kind:"error"};if(!isValidCurriculum(e.data))return{kind:"error"};const r=t.headers.get("etag")||t.headers.get("ETag")||void 0;return{kind:"fresh",curriculum:e.data,etag:r||void 0}}catch{return{kind:"error"}}}(o,c);return"fresh"===d.kind?(function(t,n,a){try{r(_,{recursive:!0}),e(h(t),JSON.stringify(n)),a&&e(y(t),a)}catch{}}(o,d.curriculum,d.etag),d.curriculum):"unchanged"===d.kind&&l&&isValidCurriculum(l.curriculum)?(function(t){try{const e=h(t);if(n(e)){const t=Date.now()/1e3;i(e,t,t)}}catch{}}(o),l.curriculum):l&&isValidCurriculum(l.curriculum)?l.curriculum:(E="notfound"===d.kind?"notfound":"offline",null)}export function validateResultShape(t){return{curriculumId:String(t.curriculum_id??""),status:String(t.status??""),validUntil:String(t.valid_until??""),aiEnabled:1===t.ai_enabled||!0===t.ai_enabled,gated:1===t.gated||!0===t.gated,replies:g(t.replies),hometask:f(t.hometask)}}export async function validateEAToken(t){const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const r=await l(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:t.toUpperCase(),device_fingerprint:s()}),signal:AbortSignal.timeout(8e3)});if(!r.ok){const t=await r.json().catch(()=>({}));return{ok:!1,message:t.message||`HTTP ${r.status}`,code:t.code}}const n=await r.json();return n.success&&n.data?{ok:!0,...validateResultShape(n.data)}:{ok:!1,message:n.message||"Validation failed"}}catch(t){const e=t instanceof Error?t.message:String(t),r=p(t);return{ok:!1,message:`Network error: ${e}${r?` (${r})`:""}`,code:"network",advice:d(t)}}}export async function postFlowEvent(t,e,r,n){if("LEARNDEMO01"===t.toUpperCase())return;const a=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/flow/event";try{await l(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:t.toUpperCase(),curriculum_id:e,card:r,fork_id:n.fork_id,first:n.first,final:n.final,walls:n.walls,ms:n.ms,choices:n.choices,started_at:n.started_at,device_fingerprint:s()}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function syncProgress(t,e,r){if("LEARNDEMO01"===t.toUpperCase())return;const n=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+t.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:r&&r.length>0?r:void 0}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function postFocusSession(t,e){if("LEARNDEMO01"===t.toUpperCase())return!0;const r=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/focus/"+t.toUpperCase();try{return(await l(r,{method:"POST",headers:{"Content-Type":"application/json","X-ICOA-Client":u()},body:JSON.stringify({...e,device_fingerprint:s()}),signal:AbortSignal.timeout(5e3)})).ok}catch{return!1}}export async function fetchServerProgress(t){if("LEARNDEMO01"===t.toUpperCase())return null;const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+t.toUpperCase();try{const t=await l(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)return null;const r=await t.json();return r?.success&&Array.isArray(r.data?.events)?r.data.events:null}catch{return null}}export async function fetchCardFaqs(t){const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/faq/"+encodeURIComponent(t.toUpperCase());try{const t=await l(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)return[];const r=await t.json();return r.success&&r.data?m(r.data.faqs):[]}catch{return[]}}export async function syncCardFeedback(t,e){const r=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/feedback/"+t.toUpperCase();try{return(await fetch(r,{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
+ export function localized(e,t){if(!t.startsWith("zh")||!e._zh)return e;const r=e._zh,n={...e};for(const e of Object.keys(r))void 0!==r[e]&&(n[e]=r[e]);return n}export function isValidCurriculum(e){if(!e||"object"!=typeof e)return!1;const t=e;return"string"==typeof t.id&&"number"==typeof t.totalCards&&Array.isArray(t.cards)&&Array.isArray(t.modules)}import{readFileSync as e,writeFileSync as t,mkdirSync as r,existsSync as n,statSync as a,utimesSync as i}from"node:fs";import{join as o}from"node:path";import{homedir as c}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{transportAdvice as d,transportCauseCode as p}from"./tls-trust.js";import{parseHometask as f}from"./learn-hometask.js";import{parseFaqs as m,parseReplies as _}from"./learn-replies.js";const g=o(c(),".icoa","learn-cache");export const LEARN_SERVER="https://practice.icoa2026.au";function h(e){const t=e.replace(/[^A-Za-z0-9_-]/g,"_");return o(g,`${t}.json`)}function y(e){const t=e.replace(/[^A-Za-z0-9_-]/g,"_");return o(g,`${t}.etag`)}let E=null;export function getLastCurriculumFailure(){return E}export async function loadCurriculumById(o,c,s=!1){"ctf4eai-12"===o&&(o="LEARNDEMO01");const l=function(t){const r=h(t);if(!n(r))return null;try{const t=a(r),n=Date.now()-t.mtimeMs;return{curriculum:JSON.parse(e(r,"utf-8")),ageMs:n}}catch{return null}}(o);if(!s&&l&&l.ageMs<6048e5&&isValidCurriculum(l.curriculum))return l.curriculum;E=null;const d=await async function(t,r){const a=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(t),i={},o=function(t){const r=y(t);if(!n(r))return null;try{return e(r,"utf-8").trim()||null}catch{return null}}(t);o&&(i["If-None-Match"]=o),r&&(i["X-Learn-Token"]=r.toUpperCase()),i["X-ICOA-Client"]=u();try{const e=await fetch(a,{headers:i,signal:AbortSignal.timeout(6e4)});if(304===e.status)return{kind:"unchanged"};if(404===e.status)return{kind:"notfound"};if(!e.ok)return{kind:"error"};const t=await e.json();if(!t.success||!t.data)return{kind:"error"};if(!isValidCurriculum(t.data))return{kind:"error"};const r=e.headers.get("etag")||e.headers.get("ETag")||void 0;return{kind:"fresh",curriculum:t.data,etag:r||void 0}}catch{return{kind:"error"}}}(o,c);return"fresh"===d.kind?(function(e,n,a){try{r(g,{recursive:!0}),t(h(e),JSON.stringify(n)),a&&t(y(e),a)}catch{}}(o,d.curriculum,d.etag),d.curriculum):"unchanged"===d.kind&&l&&isValidCurriculum(l.curriculum)?(function(e){try{const t=h(e);if(n(t)){const e=Date.now()/1e3;i(t,e,e)}}catch{}}(o),l.curriculum):l&&isValidCurriculum(l.curriculum)?l.curriculum:(E="notfound"===d.kind?"notfound":"offline",null)}export function validateResultShape(e){return{curriculumId:String(e.curriculum_id??""),status:String(e.status??""),validUntil:String(e.valid_until??""),aiEnabled:1===e.ai_enabled||!0===e.ai_enabled,aiBasicPerCard:"number"==typeof e.ai_basic_per_card&&e.ai_basic_per_card>0?e.ai_basic_per_card:3,gated:1===e.gated||!0===e.gated,replies:_(e.replies),hometask:f(e.hometask)}}export async function validateEAToken(e){const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const r=await l(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e.toUpperCase(),device_fingerprint:s()}),signal:AbortSignal.timeout(8e3)});if(!r.ok){const e=await r.json().catch(()=>({}));return{ok:!1,message:e.message||`HTTP ${r.status}`,code:e.code}}const n=await r.json();return n.success&&n.data?{ok:!0,...validateResultShape(n.data)}:{ok:!1,message:n.message||"Validation failed"}}catch(e){const t=e instanceof Error?e.message:String(e),r=p(e);return{ok:!1,message:`Network error: ${t}${r?` (${r})`:""}`,code:"network",advice:d(e)}}}export async function postFlowEvent(e,t,r,n){if("LEARNDEMO01"===e.toUpperCase())return;const a=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/flow/event";try{await l(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e.toUpperCase(),curriculum_id:t,card:r,fork_id:n.fork_id,first:n.first,final:n.final,walls:n.walls,ms:n.ms,choices:n.choices,started_at:n.started_at,device_fingerprint:s()}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function syncProgress(e,t,r){if("LEARNDEMO01"===e.toUpperCase())return;const n=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+e.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:s(),reply_ack:r&&r.length>0?r:void 0}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function postFocusSession(e,t){if("LEARNDEMO01"===e.toUpperCase())return!0;const r=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/focus/"+e.toUpperCase();try{return(await l(r,{method:"POST",headers:{"Content-Type":"application/json","X-ICOA-Client":u()},body:JSON.stringify({...t,device_fingerprint:s()}),signal:AbortSignal.timeout(5e3)})).ok}catch{return!1}}export async function fetchServerProgress(e){if("LEARNDEMO01"===e.toUpperCase())return null;const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+e.toUpperCase();try{const e=await l(t,{signal:AbortSignal.timeout(5e3)});if(!e.ok)return null;const r=await e.json();return r?.success&&Array.isArray(r.data?.events)?r.data.events:null}catch{return null}}export async function fetchCardFaqs(e){const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/faq/"+encodeURIComponent(e.toUpperCase());try{const e=await l(t,{signal:AbortSignal.timeout(5e3)});if(!e.ok)return[];const r=await e.json();return r.success&&r.data?m(r.data.faqs):[]}catch{return[]}}export async function syncCardFeedback(e,t){const r=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/feedback/"+e.toUpperCase();try{return(await fetch(r,{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}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.602",
3
+ "version": "2.19.603",
4
4
  "description": "ICOA CLI — AI-native competition runtime of the International Cyber Olympiad in AI: learn, practise, and compete in AI & cybersecurity from the terminal (AI4CTF · CTF4AI · CTF4EAI)",
5
5
  "author": "Charlie Zhu",
6
6
  "type": "module",