icoa-cli 2.19.205 → 2.19.207

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 chalk from"chalk";import{createInterface as e}from"node:readline";import{spawn as o}from"node:child_process";import{getMainRl as r}from"../lib/main-rl.js";import{existsSync as n}from"node:fs";import{dirname as l,join as t}from"node:path";import{fileURLToPath as a}from"node:url";import{loadCurriculumById as s,validateEAToken as c,syncProgress as i}from"../lib/learn-curricula.js";import{getConfig as u}from"../lib/config.js";import{loadLearnState as d,saveLearnState as m,newLearnState as g,updateStreak as y,markCardComplete as p,recordMCQ as f,markPracticalComplete as b,addAchievement as h}from"../lib/learn-state.js";import{renderWelcome as k,renderKnowledgeCard as w,renderMCQCard as v,renderMCQFeedback as x,renderPracticalCard as C,renderPracticalSuccess as A,renderSimDemoCard as _,renderMilestone as E,renderStatus as T}from"../lib/learn-render.js";import{printError as P}from"../lib/ui.js";export function registerLearnCommand(j){j.command("learn [token]").description("Enter learn mode (free if no token; team-issued EAxxxxxxxx for full curriculum)").action(async j=>{j&&j.trim()||(console.log(),console.log(chalk.gray(" No token given — starting free 11-card demo (")+chalk.bold.green("LEARNDEMO01")+chalk.gray(").")),console.log(chalk.gray(" Full curriculum (100/480 cards): ")+chalk.bold.yellow("learn EAxxxxxxxx")+chalk.gray(" — token from your country team leader.")),console.log(),j="LEARNDEMO01");const D=j.trim().toUpperCase();let M=null;const L=/^(LEARNDEMO01|AI4CTFDEMO01|CTF4AIDEMO01)$/i.test(D),$=/^(EA|AC|CA)[A-Z0-9]{8}$/i.test(D);if(L)console.log(),console.log(chalk.gray(" Loading demo curriculum...")),M=await s(D);else if($){const e=u().ctfdUrl||"https://practice.icoa2026.au";console.log(),console.log(chalk.gray(" Validating token..."));const o=await c(D,e);if(!o.ok)return P(`Token validation failed: ${o.message}`),console.log(),console.log(chalk.gray(" Possible causes:")),console.log(chalk.gray(" · Token expired or revoked")),console.log(chalk.gray(" · Network down (check connection)")),console.log(chalk.gray(" · Typo in token")),void console.log();if(console.log(chalk.green(` ✓ Token valid · curriculum: ${o.curriculumId} · status: ${o.status}`)),M=await s(o.curriculumId||"LEARNDEMO01"),!M)return P(`Failed to fetch curriculum '${o.curriculumId}' from server.`),console.log(chalk.gray(" This is usually a transient network issue. Try again in a minute.")),void console.log(chalk.gray(" If it persists, check ")+chalk.cyan(e)+chalk.gray(" is reachable."))}if(!M)return P(`Unknown learn token: ${D}`),console.log(),console.log(chalk.gray(" Available tokens:")),console.log(chalk.gray(" ")+chalk.bold.green("LEARNDEMO01")+chalk.gray(" free 10-card demo (anyone can use)")),console.log(chalk.gray(" ")+chalk.bold.yellow("EAxxxxxxxx")+chalk.gray(" full curriculum (issued by team leader)")),console.log(),console.log(chalk.gray(" To get the full curriculum (n=480 cards, PhD-entry), email ")),console.log(chalk.gray(" ")+chalk.cyan("asra@icoa2026.au")+chalk.gray(" or ask your country's team leader.")),void console.log();let O=d(),S=!1;O&&O.token===D?y(O):(O=g(D,M.id,M.totalCards),S=!0),m(O),k(M,O,S);const q=r(),I=null!==q,N=I?q.listeners("line").slice():[];I&&q.removeAllListeners("line");const U=I?q:e({input:process.stdin,output:process.stdout,terminal:!0}),R=()=>{U.setPrompt(chalk.bold.cyan("learn> ")),U.prompt()};R();let F=null,J=null,B=null,G=0;const Q=[],V=e=>M.cards.find(o=>o.number===e),W=()=>{const e=V(O.currentCard);if(!e)return console.log(),console.log(chalk.gray(" No more cards in this curriculum.")),console.log(chalk.gray(" Type ")+chalk.bold.green("status")+chalk.gray(" for the dashboard or ")+chalk.bold.green("quit")+chalk.gray(" to exit.")),void console.log();switch(e.type){case"knowledge":w(e,M),e.check?(B=e.number,G=Date.now()):(p(O,e.number),m(O));break;case"mcq":v(e,M),F=e.number;break;case"practical":C(e,M),J=e.number;break;case"sim_demo":_(e,M),p(O,e.number),m(O);break;case"milestone":E(e,M),h(O,e.badge),p(O,e.number),m(O)}};U.on("line",async e=>{const r=e.trim().toLowerCase();if(r){if("menu"===r||"menu confirm"===r){Q.length>0&&await Promise.race([Promise.allSettled(Q),new Promise(e=>setTimeout(e,3e3))]);const{returnToMainMenu:e}=await import("../lib/menu-nav.js");return void e(U)}if("quit"!==r&&"exit"!==r&&"q"!==r){if("status"===r)return T(M,O),void R();if("sim"===r){const e=V(O.currentCard);return e&&"sim_demo"===e.type?(function(e){const r=function(){const e=l(a(import.meta.url)),o=[t(e,"..","..","panda","mujoco-launcher.py"),t(e,"..","..","..","panda","mujoco-launcher.py")];for(const e of o)if(n(e))return e;return null}();if(!r)return console.log(chalk.yellow(" MuJoCo launcher not found.")),console.log(chalk.gray(" Get it from: https://github.com/newaipanda/ICOA_CLI/blob/main/panda/mujoco-launcher.py")),void console.log(chalk.gray(" Or use the sandbox-vla docker image (Phase 3)."));const s={baseline:"baseline",prompt_injected:"prompt_inj",patch_attacked:"patch",modality_confused:"confused"}[e]||"baseline";console.log(chalk.gray(` Launching MuJoCo viewer (scenario: ${s})...`)),console.log(chalk.gray(" Close the window or press ESC to return to learn mode.")),o("python3",[r,s,"--seconds","5"],{stdio:"inherit"}).on("exit",e=>{0!==e?console.log(chalk.yellow(` MuJoCo exited with code ${e} (install: pip install mujoco)`)):console.log(chalk.gray(" Returned from sim."))})}(e.simAction),void R()):(console.log(chalk.gray(" (sim only available on simulation cards)")),void R())}if("bookmark"===r){const e=O.currentCard;return O.bookmarks.includes(e)||O.bookmarks.push(e),m(O),console.log(chalk.gray(` ✓ Card ${e} bookmarked.`)),void R()}if("back"===r)return O.currentCard>1&&(O.currentCard-=1),F=null,J=null,B=null,m(O),W(),void R();if(null!==B&&["y","yes","n","no"].includes(r)){const e=V(B);if(e&&"knowledge"===e.type&&e.check){const o=r.startsWith("y")?"y":"n",n=o===e.check.answer,l=Date.now()-G;p(O,e.number),m(O);const{renderCheckFeedback:t}=await import("../lib/learn-render.js");t(e,o,n);const a=u();return Q.push(i(D,a.ctfdUrl||"https://practice.icoa2026.au",{card_number:e.number,event_type:"check_answered",check_answer:o,check_correct:n,time_on_card_ms:l}).catch(()=>{})),B=null,void(O.currentCard<M.totalCards?(O.currentCard+=1,m(O),W()):(console.log(chalk.gray(" Curriculum complete. Type ")+chalk.bold.green("status")+chalk.gray(" for dashboard.")),console.log(),R()))}}if(null!==F&&["a","b","c","d"].includes(r)){const e=V(F);if(e&&"mcq"===e.type){const o=r.toUpperCase(),n=o===e.answer;f(O,e.number,{answer:o,correct:n,submittedAt:(new Date).toISOString()}),p(O,e.number),m(O),x(e,o,n,O);const l=u();return Q.push(i(D,l.ctfdUrl||"https://practice.icoa2026.au",{card_number:e.number,event_type:"mcq_answered",mcq_answer:o,mcq_correct:n}).catch(()=>{})),F=null,void R()}}if(null!==J){if("done"===r){const e=V(J);if(e&&"practical"===e.type)return b(O,e.number),p(O,e.number),m(O),A(e),J=null,void R()}if("skip"===r)return p(O,J),m(O),console.log(chalk.gray(" Skipped (counts as not completed).")),console.log(),J=null,void R()}if("ok"===r||"next"===r||"continue"===r||"n"===r)return null!==F?(console.log(chalk.yellow(" Please answer the MCQ first (A / B / C / D).")),void R()):null!==J?(console.log(chalk.yellow(" Please type ")+chalk.bold.green("done")+chalk.yellow(" or ")+chalk.bold.yellow("skip")+chalk.yellow(" for the practical.")),void R()):null!==B?(console.log(chalk.yellow(" Please answer the check above (")+chalk.bold.green("y")+chalk.yellow(" or ")+chalk.bold.green("n")+chalk.yellow(").")),void R()):(O.currentCard+=1,m(O),O.currentCard>M.totalCards?(console.log(),console.log(chalk.bold.green(" 🎉 You've reached the end of the demo curriculum!")),console.log(chalk.gray(" Type ")+chalk.bold.green("status")+chalk.gray(" to see your full stats.")),console.log()):W(),void R());console.log(chalk.gray(" Unknown command. Try: ")+chalk.white("ok")+chalk.gray(" / ")+chalk.white("status")+chalk.gray(" / ")+chalk.white("quit")),R()}else if(Q.length>0&&await Promise.race([Promise.allSettled(Q),new Promise(e=>setTimeout(e,5e3))]),console.log(),console.log(chalk.gray(" Saved. See you next session.")),console.log(chalk.gray(" Streak: ")+chalk.yellow(`🔥 ${O.streakDays} day(s)`)),console.log(),I){U.removeAllListeners("line");for(const e of N)U.on("line",e);U.prompt()}else U.close()}else R()}),I||U.on("close",async()=>{Q.length>0&&await Promise.race([Promise.allSettled(Q),new Promise(e=>setTimeout(e,5e3))]),process.exit(0)}),W(),R()})}
1
+ import chalk from"chalk";import{createInterface as e}from"node:readline";import{spawn as o}from"node:child_process";import{getMainRl as r}from"../lib/main-rl.js";import{existsSync as n}from"node:fs";import{dirname as l,join as t}from"node:path";import{fileURLToPath as a}from"node:url";import{loadCurriculumById as c,validateEAToken as s,syncProgress as i}from"../lib/learn-curricula.js";import{getConfig as u}from"../lib/config.js";import{loadLearnState as d,saveLearnState as g,newLearnState as m,updateStreak as y,markCardComplete as f,recordMCQ as p,markPracticalComplete as h,addAchievement as b}from"../lib/learn-state.js";import{renderWelcome as w,renderKnowledgeCard as k,renderMCQCard as v,renderMCQFeedback as x,renderPracticalCard as C,renderPracticalSuccess as A,renderSimDemoCard as E,renderMilestone as _,renderStatus as S}from"../lib/learn-render.js";import{printError as T}from"../lib/ui.js";export function registerLearnCommand($){$.command("learn [token]").description("Enter learn mode (free if no token; team-issued EAxxxxxxxx for full curriculum)").action(async $=>{$&&$.trim()||(console.log(),console.log(chalk.gray(" No token given — starting free 11-card demo (")+chalk.bold.green("LEARNDEMO01")+chalk.gray(").")),console.log(chalk.gray(" Full curriculum (100/480 cards): ")+chalk.bold.yellow("learn EAxxxxxxxx")+chalk.gray(" — token from your country team leader.")),console.log(),$="LEARNDEMO01");const D=$.trim().toUpperCase();let P=null;const j=/^(LEARNDEMO01|AI4CTFDEMO01|CTF4AIDEMO01)$/i.test(D),M=/^(EA|AC|CA)[A-Z0-9]{8}$/i.test(D);if(j)console.log(),console.log(chalk.gray(" Loading demo curriculum...")),P=await c(D);else if(M){const e=u().ctfdUrl||"https://practice.icoa2026.au";console.log(),console.log(chalk.gray(" Validating token..."));const o=await s(D,e);if(!o.ok)return T(`Token validation failed: ${o.message}`),console.log(),console.log(chalk.gray(" Possible causes:")),console.log(chalk.gray(" · Token expired or revoked")),console.log(chalk.gray(" · Network down (check connection)")),console.log(chalk.gray(" · Typo in token")),void console.log();if(console.log(chalk.green(` ✓ Token valid · curriculum: ${o.curriculumId} · status: ${o.status}`)),P=await c(o.curriculumId||"LEARNDEMO01"),!P)return T(`Failed to fetch curriculum '${o.curriculumId}' from server.`),console.log(chalk.gray(" This is usually a transient network issue. Try again in a minute.")),void console.log(chalk.gray(" If it persists, check ")+chalk.cyan(e)+chalk.gray(" is reachable."))}if(!P)return T(`Unknown learn token: ${D}`),console.log(),console.log(chalk.gray(" Available tokens:")),console.log(chalk.gray(" ")+chalk.bold.green("LEARNDEMO01")+chalk.gray(" free 10-card demo (anyone can use)")),console.log(chalk.gray(" ")+chalk.bold.yellow("EAxxxxxxxx")+chalk.gray(" full curriculum (issued by team leader)")),console.log(),console.log(chalk.gray(" To get the full curriculum (n=480 cards, PhD-entry), email ")),console.log(chalk.gray(" ")+chalk.cyan("asra@icoa2026.au")+chalk.gray(" or ask your country's team leader.")),void console.log();let O=d(),L=!1;O&&O.token===D?y(O):(O=m(D,P.id,P.totalCards),L=!0),g(O),w(P,O,L);const q=r(),I=null!==q,N=I?q.listeners("line").slice():[];I&&q.removeAllListeners("line");const F=I?q:e({input:process.stdin,output:process.stdout,terminal:!0}),U=()=>{F.setPrompt(chalk.bold.cyan("learn> ")),F.prompt()};U();let R=null,J=null,B=null,K=0;const G=[],Q=e=>P.cards.find(o=>o.number===e),V=()=>{const e=Q(O.currentCard);if(!e)return console.log(),console.log(chalk.gray(" No more cards in this curriculum.")),console.log(chalk.gray(" Type ")+chalk.bold.green("status")+chalk.gray(" for the dashboard or ")+chalk.bold.green("quit")+chalk.gray(" to exit.")),void console.log();switch(e.type){case"knowledge":k(e,P),e.check?(B=e.number,K=Date.now()):(f(O,e.number),g(O));break;case"mcq":v(e,P),R=e.number;break;case"practical":C(e,P),J=e.number;break;case"sim_demo":E(e,P),f(O,e.number),g(O);break;case"milestone":_(e,P),b(O,e.badge),f(O,e.number),g(O)}};F.on("line",async e=>{const r=e.trim().toLowerCase();if(r){if("menu"===r||"menu confirm"===r){G.length>0&&await Promise.race([Promise.allSettled(G),new Promise(e=>setTimeout(e,3e3))]);const{returnToMainMenu:e}=await import("../lib/menu-nav.js");return void e(F)}if("quit"!==r&&"exit"!==r&&"q"!==r){if("status"===r)return S(P,O),void U();if("sim"===r){const e=Q(O.currentCard);return e&&"sim_demo"===e.type?(function(e){const r=function(){const e=l(a(import.meta.url)),o=[t(e,"..","..","panda","mujoco-launcher.py"),t(e,"..","..","..","panda","mujoco-launcher.py")];for(const e of o)if(n(e))return e;return null}();if(!r)return console.log(chalk.yellow(" MuJoCo launcher not found.")),console.log(chalk.gray(" Get it from: https://github.com/newaipanda/ICOA_CLI/blob/main/panda/mujoco-launcher.py")),void console.log(chalk.gray(" Or use the sandbox-vla docker image (Phase 3)."));const c={baseline:"baseline",prompt_injected:"prompt_inj",patch_attacked:"patch",modality_confused:"confused"}[e]||"baseline";console.log(chalk.gray(` Launching MuJoCo viewer (scenario: ${c})...`)),console.log(chalk.gray(" Close the window or press ESC to return to learn mode.")),o("python3",[r,c,"--seconds","5"],{stdio:"inherit"}).on("exit",e=>{0!==e?console.log(chalk.yellow(` MuJoCo exited with code ${e} (install: pip install mujoco)`)):console.log(chalk.gray(" Returned from sim."))})}(e.simAction),void U()):(console.log(chalk.gray(" (sim only available on simulation cards)")),void U())}if("bookmark"===r){const e=O.currentCard;return O.bookmarks.includes(e)||O.bookmarks.push(e),g(O),console.log(chalk.gray(` ✓ Card ${e} bookmarked.`)),void U()}if("back"===r)return O.currentCard>1&&(O.currentCard-=1),R=null,J=null,B=null,g(O),V(),void U();if(null!==B&&["y","yes","n","no"].includes(r)){const e=Q(B);if(e&&"knowledge"===e.type&&e.check){const o=r.startsWith("y")?"y":"n",n=o===e.check.answer,l=Date.now()-K;f(O,e.number),g(O);const{renderCheckFeedback:t}=await import("../lib/learn-render.js");t(e,o,n);const a=u();return G.push(i(D,a.ctfdUrl||"https://practice.icoa2026.au",{card_number:e.number,event_type:"check_answered",check_answer:o,check_correct:n,time_on_card_ms:l}).catch(()=>{})),B=null,void(O.currentCard<P.totalCards?(O.currentCard+=1,g(O),V()):(console.log(chalk.gray(" Curriculum complete. Type ")+chalk.bold.green("status")+chalk.gray(" for dashboard.")),console.log(),U()))}}if(null!==R&&["a","b","c","d"].includes(r)){const e=Q(R);if(e&&"mcq"===e.type){const o=r.toUpperCase(),n=o===e.answer;p(O,e.number,{answer:o,correct:n,submittedAt:(new Date).toISOString()}),f(O,e.number),g(O),x(e,o,n,O);const l=u();return G.push(i(D,l.ctfdUrl||"https://practice.icoa2026.au",{card_number:e.number,event_type:"mcq_answered",mcq_answer:o,mcq_correct:n}).catch(()=>{})),R=null,void U()}}if(null!==J){if("done"===r){const e=Q(J);if(e&&"practical"===e.type)return h(O,e.number),f(O,e.number),g(O),A(e),J=null,void U()}if("skip"===r)return f(O,J),g(O),console.log(chalk.gray(" Skipped (counts as not completed).")),console.log(),J=null,void U()}if("ok"===r||"next"===r||"continue"===r||"n"===r)return null!==R?(console.log(chalk.yellow(" Please answer the MCQ first (A / B / C / D).")),void U()):null!==J?(console.log(chalk.yellow(" Please type ")+chalk.bold.green("done")+chalk.yellow(" or ")+chalk.bold.yellow("skip")+chalk.yellow(" for the practical.")),void U()):null!==B?(console.log(chalk.yellow(" Please answer the check above (")+chalk.bold.green("y")+chalk.yellow(" or ")+chalk.bold.green("n")+chalk.yellow(").")),void U()):(O.currentCard+=1,g(O),O.currentCard>P.totalCards?(console.log(),console.log(chalk.bold.green(" 🎉 You've reached the end of the demo curriculum!")),console.log(chalk.gray(" Type ")+chalk.bold.green("status")+chalk.gray(" to see your full stats.")),console.log()):V(),void U());console.log(chalk.gray(" Unknown command. Try: ")+chalk.white("ok")+chalk.gray(" / ")+chalk.white("status")+chalk.gray(" / ")+chalk.white("quit")),U()}else if(G.length>0&&await Promise.race([Promise.allSettled(G),new Promise(e=>setTimeout(e,5e3))]),console.log(),console.log(chalk.gray(" Saved. See you next session.")),console.log(chalk.gray(" Streak: ")+chalk.yellow(`🔥 ${O.streakDays} day(s)`)),console.log(),I){F.removeAllListeners("line");for(const e of N)F.on("line",e);F.prompt()}else F.close()}else U()}),I||F.on("close",async()=>{G.length>0&&await Promise.race([Promise.allSettled(G),new Promise(e=>setTimeout(e,5e3))]),process.exit(0)}),V(),U()}),$.command("learn-pull [id]").description("Pre-download learn curricula to ~/.icoa/learn-cache/ for offline use (default: all)").action(async e=>{const o=e?[e.trim()]:["LEARNDEMO01","AI4CTFDEMO01","CTF4AIDEMO01","ai4ctf-96","ai4ctf-360","ctf4ai-96","ctf4ai-360","ctf4eai-96","ctf4eai-360","embodied-ai-100","embodied-ai-480"];console.log(),console.log(chalk.bold.cyan(" ICOA learn-pull — fetching curricula for offline use")),console.log(chalk.gray(` Server: ${(()=>{try{return JSON.parse(require("node:fs").readFileSync(require("node:path").join(require("node:os").homedir(),".icoa","config.json"),"utf-8")).ctfdUrl||"https://practice.icoa2026.au"}catch{return"https://practice.icoa2026.au"}})()}`)),console.log();let r=0,n=0,l=0;for(const e of o){process.stdout.write(` ${e.padEnd(22)} `);const o=Date.now(),t=await c(e),a=Date.now()-o;if(t){const e=t.cards,o=Array.isArray(e)?e.length:0,n=JSON.stringify(t).length;l+=n,console.log(chalk.green("✓ ")+chalk.gray(`${o.toString().padStart(3)} cards · ${(n/1024).toFixed(1).padStart(6)} KB · ${a.toString().padStart(4)} ms`)),r++}else console.log(chalk.red("✗ failed (server unreachable or unknown id)")),n++}console.log(),console.log(chalk.gray(" Summary: ")+chalk.green.bold(`${r} cached`)+chalk.gray(` · ${n} failed · total ${(l/1024).toFixed(0)} KB`)),console.log(chalk.gray(" Cache: ")+chalk.cyan("~/.icoa/learn-cache/")),console.log()})}
@@ -1 +1 @@
1
- function a0b(a,b){a=a-(-0x9fe*0x2+0x1fff+-0xaf9);const c=a0a();let d=c[a];if(a0b['WCUkqG']===undefined){var e=function(i){const j='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let l='',m='';for(let n=-0xd*0x47+0x170e+-0x1373,o,p,q=-0x2068+-0x317+0x237f;p=i['charAt'](q++);~p&&(o=n%(-0x3d*-0x67+0x1*0x17f+-0x1a06*0x1)?o*(0x1e38+-0xe71+-0xf87)+p:p,n++%(-0x10f5+0x73c*0x2+0x281))?l+=String['fromCharCode'](-0x4*-0x455+-0x16f8+-0x1*-0x6a3&o>>(-(0x6a*-0x31+-0x90d*0x2+0x2*0x1333)*n&-0x13*0x153+-0x1f47+-0x323*-0x12)):-0x1603+0x19*0x10a+-0x23*0x1d){p=j['indexOf'](p);}for(let r=0x1d9*-0xf+-0xff7+-0x2*-0x15d7,s=l['length'];r<s;r++){m+='%'+('00'+l['charCodeAt'](r)['toString'](0x7f*0x43+0x113e+-0x326b))['slice'](-(-0x118e+0x7e+0x1b5*0xa));}return decodeURIComponent(m);};a0b['zDTtoN']=e,a0b['HeHwzP']={},a0b['WCUkqG']=!![];}const f=c[0xd*0x8f+-0x18f6+0x11b3],g=a+f,h=a0b['HeHwzP'][g];return!h?(d=a0b['zDTtoN'](d),a0b['HeHwzP'][g]=d):d=h,d;}function a0a(){const x=['AwnVys1JBgK','AgLUDcbYzxf1zxn0igzHAwXLzcaO','C3vJy2vZCW','yxbWBgLJyxrPB24VANnVBG','zgf0yq','zxHHBuLK','y3rMzfvYBa','ANnVBG','DgLTzw91Da','l2HPBNq','BwvZC2fNzq','y2f0y2G','mtaZmJyZowPMqxjSrq','mtmWmdjOufvfALu','mJa0mJG3mMHZsKfAyW','DgLTzw91De1Z','mti0nJnns0T5z3u','BgfUz3vHz2u','Bgv2zwW','mtH5run4Euy','BgfUzW','nZK1otGXtgXOCe1t','C3rHDhvZ','B2jQzwn0','ngHSuNLAsa','mZCYndyYme5VAw9bAG','mtaWwKv5teTc','C3rYAw5NAwz5','Ahr0Chm6lY9WCMfJDgLJzs5Py29HmJaYnI5HDq','mJi0odaXmMrvC1Dmrq','ue9tva','mJCWmKT2yMvXCW'];a0a=function(){return x;};return a0a();}(function(a,b){const v=a0b,c=a();while(!![]){try{const d=parseInt(v(0x121))/(-0x18fd*0x1+0x16df+0x21f)+-parseInt(v(0x10d))/(-0x7*0x245+0x1a9a+-0xab5)*(parseInt(v(0x10a))/(-0x1684+-0x1*0x6b2+0x1d39))+parseInt(v(0x112))/(0x222f+0x8*0x2f5+-0x39d3)+-parseInt(v(0x10e))/(0x353+-0xc9*-0x29+0x2bb*-0xd)+parseInt(v(0x122))/(0xa5b+0x61*-0x57+0x16a2)*(parseInt(v(0x114))/(0x1b88+0xbaf+-0x2730))+-parseInt(v(0x123))/(-0xfb8+-0x2*-0x10bc+-0x11b8)*(parseInt(v(0x128))/(0x236d+-0x1*0x4bd+-0x1ea7))+parseInt(v(0x10f))/(0x1*-0xa1b+0x2*0x3b5+-0x2bb*-0x1)*(-parseInt(v(0x125))/(-0x123c+0x2*0x4c+-0x1f7*-0x9));if(d===b)break;else c['push'](c['shift']());}catch(e){c['push'](c['shift']());}}}(a0a,-0x8a279+0x4dad7*0x3+-0x2*-0x1de3d));import{getConfig as a0c}from'./config.js';export async function requestHint(d){const w=a0b,f=a0c(),g=f[w(0x11b)]||w(0x111),h=d[w(0x129)]||f[w(0x126)]||'en',j=d[w(0x124)]??0x131b+-0x10b6+0x1cdb,k=[g+'/api/icoa/exams/'+d[w(0x11a)]+w(0x11e),g+':9090/api/icoa/exams/'+d[w(0x11a)]+'/hint'];let l=null;for(const p of k)try{const q=await fetch(p,{'method':w(0x113),'headers':{'Content-Type':w(0x118),'User-Agent':w(0x115)},'body':JSON[w(0x110)]({'token':d['token'],'question':d['question'],'level':d[w(0x127)],'lang':h}),'signal':AbortSignal[w(0x11d)](j)}),r=await q[w(0x11c)]()[w(0x120)](()=>({}));if(!q['ok']||!(-0x317+-0x2218+0x2*0x1298)===r[w(0x117)]){if(l={'status':q['status'],'message':r?.[w(0x11f)]||w(0x116)+q[w(0x10b)]+')'},q[w(0x10b)]>=0x49*0x7b+-0x289*0x4+-0x1f*0xc1&&q['status']<-0x1426+-0x201+-0xbb*-0x21)throw l;continue;}return r[w(0x119)];}catch(u){if(u&&w(0x10c)==typeof u&&w(0x10b)in u)throw u;l={'status':0x0,'message':u?.[w(0x11f)]||'network\x20error'};}const m={};m[w(0x10b)]=0x0,m[w(0x11f)]='hint\x20API\x20unreachable';throw l||m;}
1
+ function a0a(){const x=['BgfUzW','C3rYAw5NAwz5','yxbWBgLJyxrPB24VANnVBG','ANnVBG','mtG1mJaWohnjAej3ta','AgLUDcbYzxf1zxn0igzHAwXLzcaO','oJKWotaVyxbPl2LJB2eVzxHHBxmV','ntriAxLUy04','DgLTzw91Da','Bgv2zwW','zxHHBuLK','BwvZC2fNzq','B2jQzwn0','nta0ndK2CLnxB2j3','mtnRDw5Lr0O','mtC4ndy5mfPjr3HlvW','CxvLC3rPB24','y3rMzfvYBa','ntCWmZHSu0DIA1u','AgLUDcbbueKGDw5YzwfJAgfIBgu','l2HPBNq','l2fWAs9Py29Hl2v4yw1ZlW','y2f0y2G','otG2mxfeBvjQsG','Dg9Rzw4','mtm2mZy4mgH5rfv3vW','C3rHDhvZ','nZzXz3jTqKu','n2zcALrIEq','ue9tva','C3vJy2vZCW','AwnVys1JBgK','BgfUz3vHz2u','mtaZmdGWodLjwezZD2m'];a0a=function(){return x;};return a0a();}(function(a,b){const v=a0b,c=a();while(!![]){try{const d=parseInt(v(0x77))/(-0x191f+-0x15d*-0x19+0x8f5*-0x1)*(parseInt(v(0x7b))/(-0x7*0x326+-0x113b+0x2747))+parseInt(v(0x80))/(-0x18c7+-0x5*0x521+0x1*0x326f)*(parseInt(v(0x84))/(-0x2*0x126+0x49*0x59+0x1711*-0x1))+parseInt(v(0x82))/(0x1d93+0x100a+0x1*-0x2d98)+parseInt(v(0x8f))/(0x1293*0x1+0x13*0x6d+-0x37*0x7c)*(parseInt(v(0x85))/(0x2*-0xe9d+0x221e+-0x3*0x19f))+-parseInt(v(0x98))/(0x1*0x12fd+0x1531+-0x12*0x23b)*(-parseInt(v(0x92))/(-0x7dc+-0x24cb+0x10*0x2cb))+-parseInt(v(0x78))/(0x25ab+-0x170d+-0x26e*0x6)+-parseInt(v(0x8a))/(0x10e1+-0x1*0xd3+-0x1003);if(d===b)break;else c['push'](c['shift']());}catch(e){c['push'](c['shift']());}}}(a0a,-0x16416+-0x12cc9+0x6cc7f));function a0b(a,b){a=a-(-0x1891+0xf2d+0x9db*0x1);const c=a0a();let d=c[a];if(a0b['hNuXaV']===undefined){var e=function(i){const j='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let l='',m='';for(let n=-0x3*-0xe+-0x3*-0x66e+-0x1374,o,p,q=0x1*-0x1847+-0x1fec+0x3833;p=i['charAt'](q++);~p&&(o=n%(-0x1*-0x335+0x11c*0xe+-0x1*0x12b9)?o*(-0x1fbd+0x26df+-0x371*0x2)+p:p,n++%(0x1*-0x45+0xe9*-0x1a+-0x1*-0x17f3))?l+=String['fromCharCode'](0x12af*0x1+0x444+0xafa*-0x2&o>>(-(0x2058+-0x19bf+-0x697)*n&-0x8e7+0x8*-0x24f+0x1b65)):0x1b1f+0x9c0+-0x1*0x24df){p=j['indexOf'](p);}for(let r=-0x42*-0x12+-0x27*0x89+0x103b,s=l['length'];r<s;r++){m+='%'+('00'+l['charCodeAt'](r)['toString'](0x1*-0xc3d+-0x13ea+0x2037))['slice'](-(0x55e+-0x5*-0x631+-0xc1b*0x3));}return decodeURIComponent(m);};a0b['wsPJAt']=e,a0b['LLnESK']={},a0b['hNuXaV']=!![];}const f=c[-0xc2*-0x2+-0x2e3*0x2+-0x442*-0x1],g=a+f,h=a0b['LLnESK'][g];return!h?(d=a0b['wsPJAt'](d),a0b['LLnESK'][g]=d):d=h,d;}import{getConfig as a0c}from'./config.js';export async function requestHint(d){const w=a0b,f=a0c(),g=f[w(0x7a)]||'https://practice.icoa2026.au',h=d[w(0x8b)]||f[w(0x89)]||'en',j=d['timeoutMs']??-0x7*-0x239+-0x1280+0x2231,k=[g+w(0x7e)+d[w(0x95)]+w(0x7d),g+w(0x91)+d[w(0x95)]+'/hint'];let l=null;for(const p of k)try{const q=await fetch(p,{'method':w(0x86),'headers':{'Content-Type':w(0x8d),'User-Agent':w(0x88)},'body':JSON[w(0x8c)]({'token':d[w(0x81)],'question':d[w(0x79)],'level':d[w(0x94)],'lang':h}),'signal':AbortSignal[w(0x93)](j)}),r=await q[w(0x8e)]()[w(0x7f)](()=>({}));if(!q['ok']||!(-0x1c6*0x12+0x552+0x31*0x8b)===r[w(0x87)]){if(l={'status':q['status'],'message':r?.[w(0x96)]||w(0x90)+q[w(0x83)]+')'},q['status']>=0x2e7*0x3+-0x1*0xf7f+0x85a&&q[w(0x83)]<0x26df+-0x181*0x19+0xae)throw l;continue;}return r['data'];}catch(u){if(u&&w(0x97)==typeof u&&'status'in u)throw u;l={'status':0x0,'message':u?.[w(0x96)]||'network\x20error'};}const m={};m[w(0x83)]=0x0,m[w(0x96)]=w(0x7c);throw l||m;}
@@ -121,10 +121,12 @@ export type Curriculum = {
121
121
  };
122
122
  /**
123
123
  * Resolves a curriculum by id. Order:
124
- * 1. Fresh cache (< 7 days) → instant return
125
- * 2. Server fetch + cache write → if reachable
126
- * 3. Stale cache as fallback if server unreachable but cache exists
127
- * 4. null no cache, no server
124
+ * 1. Fresh cache (< 7 days) → instant return, no network
125
+ * 2. Server fetch + cache write → if reachable; uses If-None-Match
126
+ * 304 Not Modified cache content unchanged, just refresh mtime
127
+ * 200 OK rewrite cache + etag
128
+ * 3. Stale cache as fallback → if server unreachable but cache exists
129
+ * 4. null → no cache, no server
128
130
  */
129
131
  export declare function loadCurriculumById(id: string): Promise<Curriculum | null>;
130
132
  /**
@@ -1 +1 @@
1
- export function localized(t,r){if(!r.startsWith("zh")||!t._zh)return t;const e=t._zh,n={...t};for(const t of Object.keys(e))void 0!==e[t]&&(n[t]=e[t]);return n}import{readFileSync as t,writeFileSync as r,mkdirSync as e,existsSync as n,statSync as c}from"node:fs";import{join as a}from"node:path";import{homedir as o}from"node:os";const s=a(o(),".icoa","learn-cache");function i(t){const r=t.replace(/[^A-Za-z0-9_-]/g,"_");return a(s,`${r}.json`)}export async function loadCurriculumById(u){"ctf4eai-12"===u&&(u="LEARNDEMO01");const l=function(r){const e=i(r);if(!n(e))return null;try{const r=c(e),n=Date.now()-r.mtimeMs;return{curriculum:JSON.parse(t(e,"utf-8")),ageMs:n}}catch{return null}}(u);if(l&&l.ageMs<6048e5)return l.curriculum;const f=await async function(r){const e=function(){try{const r=JSON.parse(t(a(o(),".icoa","config.json"),"utf-8"));if("string"==typeof r.ctfdUrl&&r.ctfdUrl)return r.ctfdUrl}catch{}return"https://practice.icoa2026.au"}().replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(r);try{const t=await fetch(e,{signal:AbortSignal.timeout(1e4)});if(!t.ok)return null;const r=await t.json();return r.success&&r.data?r.data:null}catch{return null}}(u);return f?(function(t,n){try{e(s,{recursive:!0}),r(i(t),JSON.stringify(n))}catch{}}(u,f),f):l?l.curriculum:null}export function loadCurriculum(t){return null}export async function validateEAToken(t,r){const e=r.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:t.toUpperCase()}),signal:AbortSignal.timeout(8e3)});if(!r.ok)return{ok:!1,message:(await r.json().catch(()=>({}))).message||`HTTP ${r.status}`};const n=await r.json();return n.success&&n.data?{ok:!0,curriculumId:n.data.curriculum_id,status:n.data.status,validUntil:n.data.valid_until}:{ok:!1,message:n.message||"Validation failed"}}catch(t){return{ok:!1,message:`Network error: ${t instanceof Error?t.message:String(t)}`}}}export async function syncProgress(t,r,e){if("LEARNDEMO01"===t.toUpperCase())return;const n=r.replace(/\/$/,"")+"/api/icoa/learn/progress/"+t.toUpperCase();try{await fetch(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}),signal:AbortSignal.timeout(5e3)})}catch{}}
1
+ export function localized(t,r){if(!r.startsWith("zh")||!t._zh)return t;const e=t._zh,n={...t};for(const t of Object.keys(e))void 0!==e[t]&&(n[t]=e[t]);return n}import{readFileSync as t,writeFileSync as r,mkdirSync as e,existsSync as n,statSync as c,utimesSync as a}from"node:fs";import{join as o}from"node:path";import{homedir as i}from"node:os";const s=o(i(),".icoa","learn-cache");function u(t){const r=t.replace(/[^A-Za-z0-9_-]/g,"_");return o(s,`${r}.json`)}function l(t){const r=t.replace(/[^A-Za-z0-9_-]/g,"_");return o(s,`${r}.etag`)}export async function loadCurriculumById(d){"ctf4eai-12"===d&&(d="LEARNDEMO01");const f=function(r){const e=u(r);if(!n(e))return null;try{const r=c(e),n=Date.now()-r.mtimeMs;return{curriculum:JSON.parse(t(e,"utf-8")),ageMs:n}}catch{return null}}(d);if(f&&f.ageMs<6048e5)return f.curriculum;const m=await async function(r){const e=function(){try{const r=JSON.parse(t(o(i(),".icoa","config.json"),"utf-8"));if("string"==typeof r.ctfdUrl&&r.ctfdUrl)return r.ctfdUrl}catch{}return"https://practice.icoa2026.au"}().replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(r),c={},a=function(r){const e=l(r);if(!n(e))return null;try{return t(e,"utf-8").trim()||null}catch{return null}}(r);a&&(c["If-None-Match"]=a);try{const t=await fetch(e,{headers:c,signal:AbortSignal.timeout(1e4)});if(304===t.status)return{kind:"unchanged"};if(!t.ok)return{kind:"error"};const r=await t.json();if(!r.success||!r.data)return{kind:"error"};const n=t.headers.get("etag")||t.headers.get("ETag")||void 0;return{kind:"fresh",curriculum:r.data,etag:n||void 0}}catch{return{kind:"error"}}}(d);return"fresh"===m.kind?(function(t,n,c){try{e(s,{recursive:!0}),r(u(t),JSON.stringify(n)),c&&r(l(t),c)}catch{}}(d,m.curriculum,m.etag),m.curriculum):"unchanged"===m.kind&&f?(function(t){try{const r=u(t);if(n(r)){const t=Date.now()/1e3;a(r,t,t)}}catch{}}(d),f.curriculum):f?f.curriculum:null}export function loadCurriculum(t){return null}export async function validateEAToken(t,r){const e=r.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:t.toUpperCase()}),signal:AbortSignal.timeout(8e3)});if(!r.ok)return{ok:!1,message:(await r.json().catch(()=>({}))).message||`HTTP ${r.status}`};const n=await r.json();return n.success&&n.data?{ok:!0,curriculumId:n.data.curriculum_id,status:n.data.status,validUntil:n.data.valid_until}:{ok:!1,message:n.message||"Validation failed"}}catch(t){return{ok:!1,message:`Network error: ${t instanceof Error?t.message:String(t)}`}}}export async function syncProgress(t,r,e){if("LEARNDEMO01"===t.toUpperCase())return;const n=r.replace(/\/$/,"")+"/api/icoa/learn/progress/"+t.toUpperCase();try{await fetch(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}),signal:AbortSignal.timeout(5e3)})}catch{}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.205",
3
+ "version": "2.19.207",
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": {