icoa-cli 2.19.231 → 2.19.233
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/ai4ctf.js +1 -1
- package/dist/commands/ctf4ai-demo.js +1 -1
- package/dist/commands/ctf4vla.js +1 -1
- package/dist/commands/exam.js +1 -1
- package/dist/commands/learn.js +1 -1
- package/dist/lib/hint-client.js +1 -1
- package/dist/lib/learn-curricula.d.ts +27 -1
- package/dist/lib/learn-curricula.js +1 -1
- package/dist/lib/learn-render.d.ts +10 -1
- package/dist/lib/learn-render.js +1 -1
- package/dist/lib/render-card.js +1 -1
- package/dist/repl.js +1 -1
- package/package.json +3 -2
package/dist/commands/learn.js
CHANGED
|
@@ -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 a}from"node:path";import{fileURLToPath as t}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 p,recordMCQ as f,recordCheck as b,markPracticalComplete as h,addAchievement as w}from"../lib/learn-state.js";import{renderWelcome as k,renderKnowledgeCard as C,renderMCQCard as v,renderMCQFeedback as A,renderPracticalCard as $,renderPracticalSuccess as S,renderSimDemoCard as I,renderMilestone as E,renderStatus as O}from"../lib/learn-render.js";import{printError as T}from"../lib/ui.js";export function registerLearnCommand(_){_.command("learn [token]").description("Enter learn mode (free demo, or team-issued EA/AC/CA/IO/IA token 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 (96 / 360 cards): ")+chalk.bold.yellow("learn EA|AC|CA|IO|IA + 8 chars")+chalk.gray(" — token from your country team leader.")),console.log(),_="LEARNDEMO01");const P=_.trim().toUpperCase();let D=null;const M=/^(LEARNDEMO01|AI4CTFDEMO01|CTF4AIDEMO01)$/i.test(P),j=/^(EA|AC|CA|IO|IA)[A-Z0-9]{8}$/i.test(P);if(M)console.log(),console.log(chalk.gray(" Loading demo curriculum...")),D=await c(P);else if(j){const e=u().ctfdUrl||"https://practice.icoa2026.au";console.log(),console.log(chalk.gray(" Validating token..."));const o=await s(P,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}`)),D=await c(o.curriculumId||"LEARNDEMO01"),!D)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(!D)return T(`Unknown learn token: ${P}`),console.log(),console.log(chalk.gray(" Available tokens:")),console.log(chalk.gray(" ")+chalk.bold.green("LEARNDEMO01")+chalk.gray(" free 11-card demo (anyone can use)")),console.log(chalk.gray(" ")+chalk.bold.yellow("EA|AC|CA|IO|IA")+chalk.gray(" + 8 chars full 96 / 360-card curriculum (issued by team leader)")),console.log(),console.log(chalk.gray(" Tracks: ")+chalk.cyan("EA")+chalk.gray("=ctf4eai · ")+chalk.cyan("AC")+chalk.gray("=ai4ctf · ")+chalk.cyan("CA")+chalk.gray("=ctf4ai · ")+chalk.cyan("IO")+chalk.gray("=ioai · ")+chalk.cyan("IA")+chalk.gray("=iaio")),console.log(),console.log(chalk.gray(" To get a 360-card olympiad-prep curriculum, email ")),console.log(chalk.gray(" ")+chalk.cyan("asra@icoa2026.au")+chalk.gray(" or ask your country's team leader.")),void console.log();let q=d(),L=!1;q&&q.token===P?y(q):(q=m(P,D.id,D.totalCards),L=!0),g(q),k(D,q,L);const N=r(),x=null!==N,R=x?N.listeners("line").slice():[];x&&N.removeAllListeners("line");const F=x?N:e({input:process.stdin,output:process.stdout,terminal:!0}),U=()=>{F.setPrompt(chalk.bold.cyan("icoa learn> ")),F.prompt()};U();let J=null,B=null,Q=null,K=0;const W=[],G=e=>D.cards.find(o=>o.number===e),V=()=>{const e=G(q.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":C(e,D),e.check?(Q=e.number,K=Date.now()):(p(q,e.number),g(q));break;case"mcq":v(e,D),J=e.number;break;case"practical":$(e,D),B=e.number;break;case"sim_demo":I(e,D),p(q,e.number),g(q);break;case"milestone":E(e,D),w(q,e.badge),p(q,e.number),g(q)}};F.on("line",async e=>{const r=e.trim().toLowerCase();if(r){if("menu"===r||"menu confirm"===r){W.length>0&&await Promise.race([Promise.allSettled(W),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 O(D,q),void U();if("sim"===r){const e=G(q.currentCard);return e&&"sim_demo"===e.type?(function(e){const r=function(){const e=l(t(import.meta.url)),o=[a(e,"..","..","panda","mujoco-launcher.py"),a(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=q.currentCard;return q.bookmarks.includes(e)||q.bookmarks.push(e),g(q),console.log(chalk.gray(` ✓ Card ${e} bookmarked.`)),void U()}if("back"===r)return q.currentCard>1&&(q.currentCard-=1),J=null,B=null,Q=null,g(q),V(),void U();if(r.startsWith("card ")){const e=r.slice(5).trim();if("review"===e||"r"===e){const e=G(q.currentCard)||D.cards[0],o=D.modules.find(o=>e.number>=o.cardRange[0]&&e.number<=o.cardRange[1]);if(!o)return console.log(chalk.yellow(" Cannot locate current phase.")),void U();const[r,n]=o.cardRange,l=q.checkResults||{};console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(`Card review — Phase ${o.number}: ${o.name} (cards ${r}–${n})`)),console.log(chalk.cyan(" │"));let a=0,t=0;for(let e=r;e<=n;e++){const o=D.cards.find(o=>o.number===e);if(!o)continue;const r="knowledge"===o.type&&o.check,n=("title"in o?o.title:o.badge).slice(0,44);if(!r){const r="milestone"===o.type?"★":"mcq"===o.type?"Q":"practical"===o.type?"!":"sim_demo"===o.type?"▷":"·";console.log(chalk.cyan(" │ ")+chalk.gray(`${String(e).padStart(3)} [${r}] `)+chalk.gray(n));continue}const c=o,s=l[String(e)];if(s){a++,s.correct&&t++;const o=s.correct?chalk.green("✓"):chalk.red("✗"),r=chalk.white(s.answer),l=chalk.gray(c.check.answer);console.log(chalk.cyan(" │ ")+`${String(e).padStart(3)} [${o}] `+chalk.gray("you=")+r+chalk.gray(" truth=")+l+chalk.gray(" ")+chalk.gray(n))}else console.log(chalk.cyan(" │ ")+chalk.gray(`${String(e).padStart(3)} [—] `)+chalk.gray(`(unanswered) ${n}`))}return console.log(chalk.cyan(" │")),console.log(chalk.cyan(" │ ")+chalk.bold.white(`y/n score: ${t}/${a}`)+chalk.gray(0===a?" (no y/n cards answered in this phase yet)":"")),console.log(chalk.cyan(" ╰──────────────────────────────────────────────")),console.log(),void U()}const o=parseInt(e,10);return!Number.isInteger(o)||o<1||o>D.totalCards?(console.log(chalk.yellow(` Card number must be 1..${D.totalCards}. Try: `)+chalk.bold.green("card 1")+chalk.yellow(" / ")+chalk.bold.green("card review")),void U()):(q.currentCard=o,J=null,B=null,Q=null,g(q),V(),void U())}if(null!==Q&&["y","yes","n","no"].includes(r)){const e=G(Q);if(e&&"knowledge"===e.type&&e.check){const o=r.startsWith("y")?"y":"n",n=o===e.check.answer,l=Date.now()-K;p(q,e.number),b(q,e.number,{answer:o,correct:n,submittedAt:(new Date).toISOString()}),g(q);const{renderCheckFeedback:a}=await import("../lib/learn-render.js");a(e,o,n);const t=u();return W.push(i(P,t.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(()=>{})),Q=null,void(n?q.currentCard<D.totalCards?(q.currentCard+=1,g(q),V()):(console.log(chalk.gray(" Curriculum complete. Type ")+chalk.bold.green("status")+chalk.gray(" for dashboard.")),console.log(),U()):U())}}if(null!==J&&["a","b","c","d"].includes(r)){const e=G(J);if(e&&"mcq"===e.type){const o=r.toUpperCase(),n=o===e.answer;f(q,e.number,{answer:o,correct:n,submittedAt:(new Date).toISOString()}),p(q,e.number),g(q),A(e,o,n,q);const l=u();return W.push(i(P,l.ctfdUrl||"https://practice.icoa2026.au",{card_number:e.number,event_type:"mcq_answered",mcq_answer:o,mcq_correct:n}).catch(()=>{})),J=null,void(n?q.currentCard<D.totalCards?(q.currentCard+=1,g(q),V()):(console.log(chalk.gray(" Curriculum complete. Type ")+chalk.bold.green("status")+chalk.gray(" for dashboard.")),console.log(),U()):U())}}if(null!==B){if("done"===r){const e=G(B);if(e&&"practical"===e.type)return h(q,e.number),p(q,e.number),g(q),S(e),B=null,void U()}if("skip"===r)return p(q,B),g(q),console.log(chalk.gray(" Skipped (counts as not completed).")),console.log(),B=null,void U()}if("ok"===r||"next"===r||"continue"===r||"n"===r)return null!==J?(console.log(chalk.yellow(" Please answer the MCQ first (A / B / C / D).")),void U()):null!==B?(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!==Q?(console.log(chalk.yellow(" Please answer the check above (")+chalk.bold.green("y")+chalk.yellow(" or ")+chalk.bold.green("n")+chalk.yellow(").")),void U()):(q.currentCard+=1,g(q),q.currentCard>D.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());if(null!==Q)return console.log(chalk.yellow(" Please answer the check above (")+chalk.bold.green("y")+chalk.yellow(" or ")+chalk.bold.green("n")+chalk.yellow(").")),void U();if(null!==J)return console.log(chalk.yellow(" Please answer the MCQ first (A / B / C / D).")),void U();if(null!==B)return console.log(chalk.yellow(" Please type ")+chalk.bold.green("done")+chalk.yellow(" or ")+chalk.bold.yellow("skip")+chalk.yellow(" for the practical.")),void U();console.log(chalk.gray(" Unknown command. Try: ")+chalk.white("ok")+chalk.gray(" / ")+chalk.white("status")+chalk.gray(" / ")+chalk.white("card N")+chalk.gray(" / ")+chalk.white("card review")+chalk.gray(" / ")+chalk.white("quit")),U()}else if(W.length>0&&await Promise.race([Promise.allSettled(W),new Promise(e=>setTimeout(e,5e3))]),console.log(),console.log(chalk.gray(" Saved. See you next session.")),console.log(chalk.gray(" Streak: ")+chalk.yellow(`🔥 ${q.streakDays} day(s)`)),console.log(),x){F.removeAllListeners("line");for(const e of R)F.on("line",e);F.prompt()}else F.close()}else U()}),x||F.on("close",async()=>{W.length>0&&await Promise.race([Promise.allSettled(W),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(),a=await c(e),t=Date.now()-o;if(a){const e=a.cards,o=Array.isArray(e)?e.length:0,n=JSON.stringify(a).length;l+=n,console.log(chalk.green("✓ ")+chalk.gray(`${o.toString().padStart(3)} cards · ${(n/1024).toFixed(1).padStart(6)} KB · ${t.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
|
+
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,syncCardFeedback as u}from"../lib/learn-curricula.js";import{getConfig as d}from"../lib/config.js";import{loadLearnState as g,saveLearnState as m,newLearnState as y,updateStreak as f,markCardComplete as p,recordMCQ as b,recordCheck as h,markPracticalComplete as w,addAchievement as k}from"../lib/learn-state.js";import{renderWelcome as C,renderKnowledgeCard as v,renderMCQCard as A,renderMCQFeedback as E,renderPracticalCard as _,renderPracticalSuccess as I,renderSimDemoCard as D,renderMilestone as O,renderStatus as T,renderPhaseComplete as S}from"../lib/learn-render.js";import{printError as P}from"../lib/ui.js";export function registerLearnCommand($){$.command("learn [token]").description("Enter learn mode (free demo, or team-issued EA/AC/CA/IO/IA token 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 (96 / 360 cards): ")+chalk.bold.yellow("learn EA|AC|CA|IO|IA + 8 chars")+chalk.gray(" — token from your country team leader.")),console.log(),$="LEARNDEMO01");const M=$.trim().toUpperCase();let L=null;const j=/^(LEARNDEMO01|AI4CTFDEMO01|CTF4AIDEMO01)$/i.test(M),x=/^(EA|AC|CA|IO|IA)[A-Z0-9]{8}$/i.test(M);if(j)console.log(),console.log(chalk.gray(" Loading demo curriculum...")),L=await c(M);else if(x){const e=d().ctfdUrl||"https://practice.icoa2026.au";console.log(),console.log(chalk.gray(" Validating token..."));const o=await s(M,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}`)),L=await c(o.curriculumId||"LEARNDEMO01"),!L)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(!L)return P(`Unknown learn token: ${M}`),console.log(),console.log(chalk.gray(" Available tokens:")),console.log(chalk.gray(" ")+chalk.bold.green("LEARNDEMO01")+chalk.gray(" free 11-card demo (anyone can use)")),console.log(chalk.gray(" ")+chalk.bold.yellow("EA|AC|CA|IO|IA")+chalk.gray(" + 8 chars full 96 / 360-card curriculum (issued by team leader)")),console.log(),console.log(chalk.gray(" Tracks: ")+chalk.cyan("EA")+chalk.gray("=ctf4eai · ")+chalk.cyan("AC")+chalk.gray("=ai4ctf · ")+chalk.cyan("CA")+chalk.gray("=ctf4ai · ")+chalk.cyan("IO")+chalk.gray("=ioai · ")+chalk.cyan("IA")+chalk.gray("=iaio")),console.log(),console.log(chalk.gray(" To get a 360-card olympiad-prep curriculum, email ")),console.log(chalk.gray(" ")+chalk.cyan("asra@icoa2026.au")+chalk.gray(" or ask your country's team leader.")),void console.log();let N=g(),q=!1;N&&N.token===M?f(N):(N=y(M,L.id,L.totalCards),q=!0),m(N),C(L,N,q);const F=r(),R=null!==F,U=R?F.listeners("line").slice():[];R&&F.removeAllListeners("line");const B=R?F:e({input:process.stdin,output:process.stdout,terminal:!0}),J=()=>{B.setPrompt(chalk.bold.cyan("icoa learn> ")),B.prompt()};J();let W=null,K=null,Q=null,z=null,G=!1,V=0;const Y=(d().language||"en").toLowerCase().startsWith("zh"),Z=[];let H=null;const X=new Promise(e=>{H=e}),ee=()=>{const e=H;H=null,e?.()},oe="1"===process.env.ICOA_LEARN_EMBEDDED,re=e=>L.cards.find(o=>o.number===e),ne=e=>{const o=L.modules.find(o=>o.cardRange[1]===e);if(!o)return!1;const r=L.modules.find(e=>e.number===o.number+1);return S(L,N,o,r),!0},le=()=>{G=!0,console.log(chalk.gray(Y?" 按 Enter 进入下一阶段…":" Press Enter to continue to the next phase…"))},te=()=>{const e=re(N.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":v(e,L),e.check?(Q=e.number,V=Date.now()):(p(N,e.number),m(N));break;case"mcq":A(e,L),W=e.number;break;case"practical":_(e,L),K=e.number;break;case"sim_demo":D(e,L),p(N,e.number),m(N);break;case"milestone":O(e,L),k(N,e.badge),p(N,e.number),m(N)}};B.on("line",async e=>{const r=e.trim().toLowerCase();if(null!==z){const o=e.trim(),r=z;if(z=null,""===o)console.log(chalk.gray(Y?" 已取消(未提交)。":" Cancelled (nothing submitted)."));else{const e=d(),n=V>0?Date.now()-V:void 0;Z.push(u(M,e.ctfdUrl||"https://practice.icoa2026.au",{curriculum_id:L.id,card_number:r,feedback_text:o,time_on_card_ms:n}).catch(()=>{})),console.log(chalk.green(Y?" ✓ 已收到,谢谢帮助完善这张卡。":" ✓ Thanks — your report was recorded."))}return console.log(),void J()}if(G)return G=!1,te(),void J();if(r)if("menu"!==r&&"menu confirm"!==r)if("quit"!==r&&"exit"!==r&&"q"!==r){if("status"===r)return T(L,N),void J();if("sim"===r){const e=re(N.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 J()):(console.log(chalk.gray(" (sim only available on simulation cards)")),void J())}if("bookmark"===r){const e=N.currentCard;return N.bookmarks.includes(e)||N.bookmarks.push(e),m(N),console.log(chalk.gray(` ✓ Card ${e} bookmarked.`)),void J()}if("e"===r)return z=N.currentCard,console.log(chalk.gray(Y?" 描述问题(回车提交 / 空行取消):":" Describe the issue (Enter to submit / blank to cancel):")),void J();if("back"===r||"b"===r)return N.currentCard>1&&(N.currentCard-=1),W=null,K=null,Q=null,m(N),te(),void J();if(r.startsWith("card ")){const e=r.slice(5).trim(),o=parseInt(e,10);return!Number.isInteger(o)||o<1||o>L.totalCards?(console.log(chalk.yellow(` Card number must be 1..${L.totalCards}. Try: `)+chalk.bold.green("card 1")),void J()):(N.currentCard=o,W=null,K=null,Q=null,m(N),te(),void J())}if(null!==Q&&["y","yes","n","no"].includes(r)){const e=re(Q);if(e&&"knowledge"===e.type&&e.check){const o=r.startsWith("y")?"y":"n",n=o===e.check.answer,l=Date.now()-V;p(N,e.number),h(N,e.number,{answer:o,correct:n,submittedAt:(new Date).toISOString()}),m(N);const{renderCheckFeedback:t}=await import("../lib/learn-render.js");t(e,o,n,!1);const a=d();if(Z.push(i(M,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(()=>{})),Q=null,N.currentCard<L.totalCards){const e=N.currentCard;N.currentCard+=1,m(N),ne(e)?le():te(),J()}else console.log(chalk.gray(" Curriculum complete. Type ")+chalk.bold.green("status")+chalk.gray(" for dashboard.")),console.log(),J();return}}if(null!==W&&["a","b","c","d"].includes(r)){const e=re(W);if(e&&"mcq"===e.type){const o=r.toUpperCase(),n=o===e.answer;b(N,e.number,{answer:o,correct:n,submittedAt:(new Date).toISOString()}),p(N,e.number),m(N),E(e,o,n,N);const l=d();return Z.push(i(M,l.ctfdUrl||"https://practice.icoa2026.au",{card_number:e.number,event_type:"mcq_answered",mcq_answer:o,mcq_correct:n}).catch(()=>{})),W=null,void(n?N.currentCard<L.totalCards?(N.currentCard+=1,m(N),te()):(console.log(chalk.gray(" Curriculum complete. Type ")+chalk.bold.green("status")+chalk.gray(" for dashboard.")),console.log(),J()):J())}}if(null!==K){if("done"===r){const e=re(K);if(e&&"practical"===e.type)return w(N,e.number),p(N,e.number),m(N),I(e),K=null,void J()}if("skip"===r)return p(N,K),m(N),console.log(chalk.gray(" Skipped (counts as not completed).")),console.log(),K=null,void J()}if("ok"===r||"next"===r||"continue"===r||"n"===r){if(null!==W)return console.log(chalk.yellow(" Please answer the MCQ first (A / B / C / D).")),void J();if(null!==K)return console.log(chalk.yellow(" Please type ")+chalk.bold.green("done")+chalk.yellow(" or ")+chalk.bold.yellow("skip")+chalk.yellow(" for the practical.")),void J();if(null!==Q)return console.log(chalk.yellow(" Please answer the check above (")+chalk.bold.green("y")+chalk.yellow(" or ")+chalk.bold.green("n")+chalk.yellow(").")),void J();const e=N.currentCard;return N.currentCard+=1,m(N),N.currentCard>L.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()):ne(e)?le():te(),void J()}if(null!==Q)return console.log(chalk.yellow(" Please answer the check above (")+chalk.bold.green("y")+chalk.yellow(" or ")+chalk.bold.green("n")+chalk.yellow(").")),void J();if(null!==W)return console.log(chalk.yellow(" Please answer the MCQ first (A / B / C / D).")),void J();if(null!==K)return console.log(chalk.yellow(" Please type ")+chalk.bold.green("done")+chalk.yellow(" or ")+chalk.bold.yellow("skip")+chalk.yellow(" for the practical.")),void J();console.log(chalk.gray(" Unknown command. Try: ")+chalk.white("ok")+chalk.gray(" / ")+chalk.white("status")+chalk.gray(" / ")+chalk.white("card N")+chalk.gray(" / ")+chalk.white("quit")),J()}else if(Z.length>0&&await Promise.race([Promise.allSettled(Z),new Promise(e=>setTimeout(e,5e3))]),console.log(),console.log(chalk.gray(" Saved. See you next session.")),console.log(chalk.gray(" Streak: ")+chalk.yellow(`🔥 ${N.streakDays} day(s)`)),console.log(),R){B.removeAllListeners("line");for(const e of U)B.on("line",e);B.prompt(),ee()}else B.removeAllListeners("line"),B.close();else if(Z.length>0&&await Promise.race([Promise.allSettled(Z),new Promise(e=>setTimeout(e,3e3))]),B.removeAllListeners("line"),R||!oe){const{returnToMainMenu:e}=await import("../lib/menu-nav.js");e(R?B:void 0),ee()}else B.close();else J()}),R||B.on("close",async()=>{Z.length>0&&await Promise.race([Promise.allSettled(Z),new Promise(e=>setTimeout(e,5e3))]),oe?ee():process.exit(0)}),te(),J(),await X}),$.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()})}
|
package/dist/lib/hint-client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(a,b){const v=a0b,c=a();while(!![]){try{const d=parseInt(v(
|
|
1
|
+
(function(a,b){const v=a0b,c=a();while(!![]){try{const d=parseInt(v(0x14a))/(-0x198+-0x564+0x6fd)*(-parseInt(v(0x140))/(0x13*0x9+-0x1*0x1feb+0x1f42))+parseInt(v(0x13d))/(0x13c*0x1+0x5f*0x1+-0x198)+parseInt(v(0x12f))/(0x49c+0x1*-0xb15+0x67d)*(parseInt(v(0x142))/(0x1ca2+0x1*0x1037+0x97*-0x4c))+-parseInt(v(0x147))/(0xfdb+0x71*-0x17+-0x1*0x5ae)*(parseInt(v(0x145))/(-0x9f5+0x1*-0xeb0+0x18ac))+parseInt(v(0x134))/(0x5*-0x35b+-0x1f69*0x1+0x3038)*(-parseInt(v(0x136))/(-0xb8*0x7+-0x1bd2+0x20e3))+-parseInt(v(0x130))/(-0x52a+0xa28+-0x1*0x4f4)+parseInt(v(0x14c))/(-0x2f+0xbad+-0xb73);if(d===b)break;else c['push'](c['shift']());}catch(e){c['push'](c['shift']());}}}(a0a,-0x152137+0x10cb10+0x2a07c*0x7));import{getConfig as a0c}from'./config.js';function a0b(a,b){a=a-(-0x201f*-0x1+0x1*0x1bdb+0x3acb*-0x1);const c=a0a();let d=c[a];if(a0b['iJVZDc']===undefined){var e=function(i){const j='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let l='',m='';for(let n=-0x1d4+-0x1299+0x3*0x6cf,o,p,q=0x1a7b*-0x1+0xc19+0xe62;p=i['charAt'](q++);~p&&(o=n%(-0x4a*0x7+0xbe9+-0x9df)?o*(0x3f9*-0x2+-0xb0d+0x133f)+p:p,n++%(0x2*0x1019+-0xefd+-0x1131))?l+=String['fromCharCode'](-0x11*0x6+0x559*0x4+-0x1*0x13ff&o>>(-(0x74b+0x191d+-0x3a*0x8f)*n&0x1f41+-0x730*0x4+0x1*-0x27b)):-0x136b+0x26b6+0xb*-0x1c1){p=j['indexOf'](p);}for(let r=-0x1d05+-0x25b0+0x42b5,s=l['length'];r<s;r++){m+='%'+('00'+l['charCodeAt'](r)['toString'](0x2*0x3d9+0x1ba9*-0x1+-0x1407*-0x1))['slice'](-(0x7a2+0x2*0x48b+-0x45*0x3e));}return decodeURIComponent(m);};a0b['ZWQeUq']=e,a0b['EiHzNF']={},a0b['iJVZDc']=!![];}const f=c[0x377*-0x4+0x18*-0xb5+0x1ed4],g=a+f,h=a0b['EiHzNF'][g];return!h?(d=a0b['ZWQeUq'](d),a0b['EiHzNF'][g]=d):d=h,d;}export async function requestHint(d){const w=a0b,f=a0c(),g=f[w(0x131)]||w(0x133),h=d[w(0x13b)]||f[w(0x148)]||'en',j=d['timeoutMs']??-0x2ed+-0x1dc2+0x1*0x3fef,k=[g+'/api/icoa/exams/'+d[w(0x13c)]+w(0x146),g+w(0x132)+d[w(0x13c)]+w(0x146)];let l=null;for(const p of k)try{const q=await fetch(p,{'method':'POST','headers':{'Content-Type':w(0x13f),'User-Agent':w(0x143)},'body':JSON[w(0x13a)]({'token':d['token'],'question':d[w(0x13e)],'level':d[w(0x135)],'lang':h}),'signal':AbortSignal[w(0x137)](j)}),r=await q[w(0x144)]()['catch'](()=>({}));if(!q['ok']||!(0x1a7b*-0x1+0xc19+0xe63)===r[w(0x141)]){if(l={'status':q[w(0x149)],'message':r?.['message']||w(0x138)+q[w(0x149)]+')'},q[w(0x149)]>=-0x4a*0x7+0xbe9+-0x853&&q['status']<0x3f9*-0x2+-0xb0d+0x14f3)throw l;continue;}return r['data'];}catch(u){if(u&&'object'==typeof u&&w(0x149)in u)throw u;l={'status':0x0,'message':u?.[w(0x139)]||'network\x20error'};}const m={};m['status']=0x0,m[w(0x139)]=w(0x14b);throw l||m;}function a0a(){const x=['mta5mZqXm3vyr2DHEa','CxvLC3rPB24','yxbWBgLJyxrPB24VANnVBG','ndi4y0TcBLP2','C3vJy2vZCW','mJeWnujzrwjgrW','AwnVys1JBgK','ANnVBG','nZqYotGZnw9fuujnCG','l2HPBNq','nMzAs0LTCq','BgfUz3vHz2u','C3rHDhvZ','mZmWmKXUvwDTsG','AgLUDcbbueKGDw5YzwfJAgfIBgu','mZq5nZKZmJLcDg90uge','mZaYognOrMHnrq','mtG4mJmWuLf3u0nt','y3rMzfvYBa','oJKWotaVyxbPl2LJB2eVzxHHBxmV','Ahr0Chm6lY9WCMfJDgLJzs5Py29HmJaYnI5HDq','mtK3otjnDxjQA00','Bgv2zwW','ndiWm29Ms0nXtG','DgLTzw91Da','AgLUDcbYzxf1zxn0igzHAwXLzcaO','BwvZC2fNzq','C3rYAw5NAwz5','BgfUzW','zxHHBuLK'];a0a=function(){return x;};return a0a();}
|
|
@@ -22,12 +22,14 @@ export type CardKnowledge = {
|
|
|
22
22
|
statement: string;
|
|
23
23
|
answer: 'y' | 'n';
|
|
24
24
|
};
|
|
25
|
+
key_point?: string;
|
|
25
26
|
chart_ids?: string[];
|
|
26
27
|
_zh?: {
|
|
27
28
|
title?: string;
|
|
28
29
|
body?: string[];
|
|
29
30
|
icoaConnection?: string;
|
|
30
31
|
checkStatement?: string;
|
|
32
|
+
key_point?: string;
|
|
31
33
|
};
|
|
32
34
|
};
|
|
33
35
|
export type CardMCQ = {
|
|
@@ -98,9 +100,13 @@ export type CardMilestone = {
|
|
|
98
100
|
};
|
|
99
101
|
export type Card = CardKnowledge | CardMCQ | CardPractical | CardSimDemo | CardMilestone;
|
|
100
102
|
export type ChartArt = {
|
|
101
|
-
|
|
103
|
+
type?: 'chart' | 'code';
|
|
104
|
+
art?: string;
|
|
102
105
|
art_t0?: string;
|
|
103
106
|
width_cols?: number;
|
|
107
|
+
color_map?: Record<string, string>;
|
|
108
|
+
lang?: string;
|
|
109
|
+
code?: string;
|
|
104
110
|
tier?: string;
|
|
105
111
|
tags?: string[];
|
|
106
112
|
embargo_ok?: boolean;
|
|
@@ -171,3 +177,23 @@ export declare function syncProgress(token: string, serverUrl: string, event: {
|
|
|
171
177
|
check_correct?: boolean;
|
|
172
178
|
time_on_card_ms?: number;
|
|
173
179
|
}): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Best-effort card-feedback submission (the `e` "report a problem" channel).
|
|
182
|
+
* Crowdsourced card QA — a learner flags an error/confusion on a card; we
|
|
183
|
+
* record it server-side bound to (curriculum_id, card_number) so the author
|
|
184
|
+
* team can triage. Fire-and-forget, same contract as syncProgress.
|
|
185
|
+
*
|
|
186
|
+
* Unlike syncProgress, demo (LEARNDEMO01) feedback IS sent — the demo is the
|
|
187
|
+
* highest-traffic curriculum, so its cards benefit most from crowdsourced QA.
|
|
188
|
+
* The server records feedback for any token-shaped string (no learn_tokens
|
|
189
|
+
* membership required; feedback is low-risk user text).
|
|
190
|
+
*
|
|
191
|
+
* Card identity is the (curriculum_id, card_number) composite — card numbers
|
|
192
|
+
* are curriculum-LOCAL, so curriculum_id is required to disambiguate.
|
|
193
|
+
*/
|
|
194
|
+
export declare function syncCardFeedback(token: string, serverUrl: string, payload: {
|
|
195
|
+
curriculum_id: string;
|
|
196
|
+
card_number: number;
|
|
197
|
+
feedback_text: string;
|
|
198
|
+
time_on_card_ms?: number;
|
|
199
|
+
}): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function localized(t,
|
|
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}import{readFileSync as t,writeFileSync as e,mkdirSync as r,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 e=t.replace(/[^A-Za-z0-9_-]/g,"_");return o(s,`${e}.json`)}function l(t){const e=t.replace(/[^A-Za-z0-9_-]/g,"_");return o(s,`${e}.etag`)}export async function loadCurriculumById(d){"ctf4eai-12"===d&&(d="LEARNDEMO01");const f=function(e){const r=u(e);if(!n(r))return null;try{const e=c(r),n=Date.now()-e.mtimeMs;return{curriculum:JSON.parse(t(r,"utf-8")),ageMs:n}}catch{return null}}(d);if(f&&f.ageMs<6048e5)return f.curriculum;const m=await async function(e){const r=function(){try{const e=JSON.parse(t(o(i(),".icoa","config.json"),"utf-8"));if("string"==typeof e.ctfdUrl&&e.ctfdUrl)return e.ctfdUrl}catch{}return"https://practice.icoa2026.au"}().replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(e),c={},a=function(e){const r=l(e);if(!n(r))return null;try{return t(r,"utf-8").trim()||null}catch{return null}}(e);a&&(c["If-None-Match"]=a);try{const t=await fetch(r,{headers:c,signal:AbortSignal.timeout(1e4)});if(304===t.status)return{kind:"unchanged"};if(!t.ok)return{kind:"error"};const e=await t.json();if(!e.success||!e.data)return{kind:"error"};const n=t.headers.get("etag")||t.headers.get("ETag")||void 0;return{kind:"fresh",curriculum:e.data,etag:n||void 0}}catch{return{kind:"error"}}}(d);return"fresh"===m.kind?(function(t,n,c){try{r(s,{recursive:!0}),e(u(t),JSON.stringify(n)),c&&e(l(t),c)}catch{}}(d,m.curriculum,m.etag),m.curriculum):"unchanged"===m.kind&&f?(function(t){try{const e=u(t);if(n(e)){const t=Date.now()/1e3;a(e,t,t)}}catch{}}(d),f.curriculum):f?f.curriculum:null}export function loadCurriculum(t){return null}export async function validateEAToken(t,e){const r=e.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const e=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:t.toUpperCase()}),signal:AbortSignal.timeout(8e3)});if(!e.ok)return{ok:!1,message:(await e.json().catch(()=>({}))).message||`HTTP ${e.status}`};const n=await e.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,e,r){if("LEARNDEMO01"===t.toUpperCase())return;const n=e.replace(/\/$/,"")+"/api/icoa/learn/progress/"+t.toUpperCase();try{await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({card_number:r.card_number,event_type:r.event_type,mcq_answer:r.mcq_answer,mcq_correct:r.mcq_correct?1:0,check_answer:r.check_answer,check_correct:r.check_correct?1:0,time_on_card_ms:r.time_on_card_ms}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function syncCardFeedback(t,e,r){const n=e.replace(/\/$/,"")+"/api/icoa/learn/feedback/"+t.toUpperCase();try{await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({curriculum_id:r.curriculum_id,card_number:r.card_number,feedback_text:r.feedback_text,time_on_card_ms:r.time_on_card_ms}),signal:AbortSignal.timeout(5e3)})}catch{}}
|
|
@@ -6,7 +6,16 @@ export declare function renderKnowledgeCard(card: Extract<Card, {
|
|
|
6
6
|
}>, curriculum: Curriculum): void;
|
|
7
7
|
export declare function renderCheckFeedback(card: Extract<Card, {
|
|
8
8
|
type: 'knowledge';
|
|
9
|
-
}>, userAnswer: 'y' | 'n', correct: boolean): void;
|
|
9
|
+
}>, userAnswer: 'y' | 'n', correct: boolean, premiumExplain?: boolean): void;
|
|
10
|
+
export declare function renderPhaseComplete(curriculum: Curriculum, state: LearnState, done: {
|
|
11
|
+
number: number;
|
|
12
|
+
name: string;
|
|
13
|
+
cardRange: [number, number];
|
|
14
|
+
}, next?: {
|
|
15
|
+
number: number;
|
|
16
|
+
name: string;
|
|
17
|
+
cardRange: [number, number];
|
|
18
|
+
}): void;
|
|
10
19
|
export declare function renderMCQCard(card: Extract<Card, {
|
|
11
20
|
type: 'mcq';
|
|
12
21
|
}>, curriculum: Curriculum): void;
|
package/dist/lib/learn-render.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import chalk from"chalk";import{localized as o}from"./learn-curricula.js";import{t as e}from"./learn-i18n.js";import{getConfig as n}from"./config.js";import{renderCharts as l,detectCaps as r}from"./render-card.js";function t(){return(n().language||"en").toLowerCase()}function c(o="─"){return o.repeat(66)}function a(o,e,n=20){const l=e>0?o/e:0,r=Math.floor(l*n),t=n-r;return`${chalk.green("█".repeat(r))+chalk.gray("░".repeat(t))} ${(100*l).toFixed(1)}%`}function s(o){return o>=30?chalk.red("🔥🔥🔥 "+o+" days"):o>=7?chalk.yellow("🔥 "+o+" days"):o>=1?chalk.gray("· "+o+" day"+(o>1?"s":"")):chalk.gray("—")}export function renderWelcome(o,n,l){const r=t(),g=n.cardsCompleted.length,y=o.totalCards,d=y-g,i=o.modules.find(o=>n.currentCard>=o.cardRange[0]&&n.currentCard<=o.cardRange[1]);if(console.log(),console.log(chalk.cyan(" ╭"+c("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+e("academy_title",r))),console.log(chalk.cyan(" ║")),l)console.log(chalk.cyan(" ║ ")+chalk.white(" "+e("welcome_new_demo",r)));else{const o=new Date(n.lastSeenAt),l=Math.floor((Date.now()-o.getTime())/36e5),t=l<1?e("just_now",r):l<24?`${l}${e("ago_hours",r)}`:`${Math.floor(l/24)}${e("ago_days",r)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${e("welcome_back",r)} ${t}`))}console.log(chalk.cyan(" ║"));const u=i?`${i.number}. ${i.name}`:"—";console.log(chalk.cyan(" ║ ")+" "+chalk.gray(e("module",r).padEnd(11))+chalk.white(u)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(e("progress",r).padEnd(11))+a(g,y)+chalk.gray(` (${g}/${y})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(e("streak",r).padEnd(11))+s(n.streakDays)),d>0&&n.currentCard<=y&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(e("next_card",r).padEnd(11))+chalk.white(`#${n.currentCard} ${e("of",r)} ${y}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+c("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const h=0===g?e("continue_start",r):`${e("continue_resume",r)} ${n.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+h)),console.log(chalk.yellow(" status")+chalk.gray(" "+e("status_full",r))),n.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${n.bookmarks.length} ${e("bookmarks_desc",r)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+e("quit_desc",r))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function g(o,n){const l=t(),r=o.module,c=n.modules.find(o=>o.number===r),a=c?c.name:e("unknown_module",l),s=e("module",l).replace(":","").trim(),g=`${e("card",l)} ${o.number} / ${n.totalCards} · ${s} ${r} · ${a}`;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(g)+" "+chalk.cyan("─".repeat(Math.max(0,64-g.length-4))))}function y(){console.log(chalk.cyan(" ╰"+c())),console.log()}function d(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function i(){console.log(chalk.cyan(" │"))}const u=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function h(o){return/^(?: {4}|\t)/.test(o)||u.test(o)||/ {3,}/.test(o)}function m(o,e){const n=[];for(const l of o.split("\n")){if(""===l){n.push("");continue}if(h(l)){n.push(l);continue}let o="";for(const r of l.split(" "))(o+" "+r).trim().length>e?(n.push(o.trim()),o=r):o=(o+" "+r).trim();o&&n.push(o)}return n}export function renderKnowledgeCard(n,c){const a=t(),s=o(n,a);g(s,c),i(),d(chalk.bold.yellow(s.title)),d(chalk.gray("─".repeat(Math.min(s.title.length,64)))),n.chart_ids&&n.chart_ids.length>0?l(n.chart_ids,c.charts,r()):i();for(const o of s.body){for(const e of m(o,60))""===e?i():d(" "+e);i()}if(s.icoaConnection){d(chalk.magenta(" "+e("icoa_connection",a)+" 📌")),d(chalk.gray(" "+"─".repeat(20)));for(const o of m(s.icoaConnection,60))d(" "+chalk.magenta(o));i()}if(n.check){const o=a.startsWith("zh"),e=o?n._zh?.checkStatement??n.check.statement:n.check.statement;d(chalk.cyan(" 🤔 "+(o?"快速理解检查 (输 y 或 n):":"Quick comprehension check (type y or n):"))),d(chalk.gray(" "+"─".repeat(20)));for(const o of m(e,60))d(" "+chalk.white(o));i()}y(),n.check?console.log(chalk.gray(" ")+chalk.bold.green("y")+chalk.gray(" / ")+chalk.bold.green("n")+chalk.gray(" "+(a.startsWith("zh")?"回答上面问题":"answer the check above"))):console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+e("continue_to_next",a))),console.log(chalk.gray(" bookmark "+e("bookmark_desc",a))),console.log(chalk.gray(" back "+e("back_desc",a))),console.log(chalk.gray(" card N / card review jump to card N / phase y/n recap")),console.log(chalk.gray(" quit "+e("quit_desc",a))),console.log()}export function renderCheckFeedback(o,n,l){const r=t(),c=r.startsWith("zh");if(console.log(),l)console.log(chalk.green(" ✓ ")+chalk.white(c?"答对了。":"Correct."));else{const e="y"===o.check?.answer?c?"是":"Yes":c?"否":"No";console.log(chalk.yellow(" ✗ ")+chalk.white((c?"正确答案是 ":"Actually ")+e+". "+(c?"回去再看一遍上面那段。":"Re-read the paragraph above.")))}console.log(),l||(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+e("continue_to_next",r))),console.log())}export function renderMCQCard(n,l){const r=t(),c=o(n,r);g(c,l),i(),d(chalk.bold.yellow("🎯 "+c.title)),d(chalk.gray("─".repeat(Math.min(c.title.length+4,64)))),i();for(const o of m(c.question,60))d(" "+o);i();for(const o of["A","B","C","D"])d(chalk.cyan(` ${o}.`)+" "+chalk.white(c.options[o]));i(),y(),console.log(chalk.gray(" "+(r.startsWith("zh")?"输入 ":"Type "))+chalk.bold.green("A")+chalk.gray(" / ")+chalk.bold.green("B")+chalk.gray(" / ")+chalk.bold.green("C")+chalk.gray(" / ")+chalk.bold.green("D")+chalk.gray(" "+e("type_to_answer",r))),console.log()}export function renderMCQFeedback(n,l,r,c){const a=t(),s=o(n,a);console.log(),r?console.log(" "+chalk.bold.green(e("correct",a)+" ")+chalk.gray(`${e("one_point",a)} · ${l} = ${s.options[l]}`)):console.log(" "+chalk.bold.red(e("not_quite",a)+" ")+chalk.gray(`${e("you_chose",a)} ${l}; ${e("answer_is",a)} ${s.answer}.`)),console.log(),console.log(chalk.gray(" "+e("explanation",a)));for(const o of m(s.explanation,60))console.log(chalk.gray(" "+o));const g=Object.values(c.mcqResults),y=g.filter(o=>o.correct).length;console.log(),console.log(chalk.gray(" "+e("mcq_accuracy_so_far",a)+" ")+chalk.white(`${y}/${g.length}`)),console.log(),r||(console.log(chalk.gray(" "+e("press_ok_continue",a)+" ")+chalk.bold.green("ok")+chalk.gray(" "+e("to_continue",a))),console.log())}export function renderPracticalCard(n,l){const r=t(),c=o(n,r);g(c,l),i(),d(chalk.bold.yellow("🛠 "+c.title)),d(chalk.gray("─".repeat(Math.min(c.title.length+4,64)))),i();for(const o of m(c.task,60))d(" "+o);if(c.starterCode){i(),d(chalk.gray(" "+e("starter_code",r))),i();for(const o of c.starterCode.split("\n"))d(" "+chalk.cyan(o))}i(),y(),console.log(chalk.gray(" "+e("try_in_sandbox",r)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+e("drops_into_python",r))),console.log(chalk.gray(" "+e("when_done",r))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+e("done_desc",r))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+e("skip_desc",r))),console.log(chalk.gray(" card N / card review jump to card N / phase y/n recap")),console.log()}export function renderPracticalSuccess(n){const l=t(),r=o(n,l);console.log(),console.log(" "+chalk.bold.green(e("practical_recorded",l))),console.log(),console.log(chalk.gray(" "+e("reference_answer",l)));for(const o of m(r.successHint,60))console.log(chalk.gray(" "+o));console.log(),console.log(chalk.gray(" "+e("press_ok_continue",l)+" ")+chalk.bold.green("ok")+chalk.gray(" "+e("to_continue",l))),console.log()}export function renderSimDemoCard(n,l){const r=t(),c=o(n,r);g(c,l),i(),d(chalk.bold.yellow("🎬 "+c.title)),i();for(const o of m(c.description,60))d(" "+o);i(),d(chalk.gray(" "+e("sim_requires",r))),i(),y(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+e("sim_launch_desc",r))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+e("continue_to_next",r))),console.log(chalk.gray(" bookmark "+e("bookmark_desc",r))),console.log(chalk.gray(" back "+e("back_desc",r))),console.log(chalk.gray(" card N / card review jump to card N / phase y/n recap")),console.log(chalk.gray(" quit "+e("quit_desc",r))),console.log()}export function renderMilestone(n,l){const r=t(),a=o(n,r),s=n.number===l.totalCards;console.log(),console.log(chalk.bold.yellow(" ╭"+c("═"))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+chalk.white(" "+e("milestone_header",r))),console.log(chalk.bold.yellow(" ║"));const g=` ${a.badge} ${a.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(g)),console.log(chalk.bold.yellow(" ║ ")+chalk.gray(" ─".repeat(Math.max(1,Math.floor(a.badge.length/2+2))))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(e("in_wild_corresponds",r)));for(const o of m(a.realWorldLevel,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(e("whats_next",r)));for(const o of m(a.unlockedNext,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+c("═"))),console.log(),s?(console.log(chalk.gray(" "+e("demo_complete",r))),console.log(),console.log(chalk.gray(" "+e("unlock_full",r)+" ")+chalk.white(e("curriculum_name",r))+chalk.gray(",")),console.log(chalk.gray(" "+e("contact_team_leader",r)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+e("learn_token",r))),console.log(chalk.gray(" "+e("or_email",r)+" ")+chalk.cyan("asra@icoa2026.au")+chalk.gray(" "+e("for_partnership",r))),console.log(),console.log(chalk.gray(" "+e("type_quit",r)+" ")+chalk.bold.green("quit")+chalk.gray(" "+e("to_exit",r)+" ")+chalk.bold.green("status")+chalk.gray(" "+e("for_dashboard",r)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+e("continue_to_next",r))),console.log(chalk.gray(" status "+e("status_full",r))),console.log(chalk.gray(" card N / card review jump to card N / phase y/n recap")),console.log(chalk.gray(" quit "+e("quit_desc",r)))),console.log()}export function renderStatus(o,n){const l=t(),r=n.cardsCompleted.length,c=o.totalCards,g=Object.values(n.mcqResults),u=g.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(e("status_title",l))+" "+chalk.cyan("─".repeat(25))),i(),d(" "+chalk.gray(e("total_progress",l)+" ")+a(r,c)+chalk.gray(` (${r}/${c})`)),d(" "+chalk.gray(e("streak",l)+" ")+s(n.streakDays)+chalk.gray(` (${e("longest",l)} ${n.longestStreak})`)),d(" "+chalk.gray(e("mcq_accuracy",l)+" ")+chalk.white(`${u}/${g.length}`)),d(" "+chalk.gray(e("practicals_done",l)+" ")+chalk.white(`${n.practicalsCompleted.length}`)),d(" "+chalk.gray(e("bookmarked",l)+" ")+chalk.white(`${n.bookmarks.length}`)),i();const h=e("module",l).replace(":","").trim();for(const e of o.modules){const[o,l]=e.cardRange,r=n.cardsCompleted.filter(e=>e>=o&&e<=l).length,t=l-o+1;d(" "+(r===t?chalk.green("✓"):r>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${h} ${e.number}: `)+chalk.white(`${r}/${t}`)+chalk.gray(" "+e.name))}if(i(),n.achievements.length>0){d(" "+chalk.gray(e("achievements",l)));for(const o of n.achievements)d(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else d(" "+chalk.gray(e("achievements_none",l)));i(),y()}
|
|
1
|
+
import chalk from"chalk";import o from"string-width";import{localized as e}from"./learn-curricula.js";import{t as n}from"./learn-i18n.js";import{getConfig as l}from"./config.js";import{renderCharts as t,detectCaps as r}from"./render-card.js";function c(){return(l().language||"en").toLowerCase()}const a=64;function s(o="─"){return o.repeat(66)}function g(o,e,n=20){const l=e>0?o/e:0,t=Math.floor(l*n),r=n-t;return`${chalk.green("█".repeat(t))+chalk.gray("░".repeat(r))} ${(100*l).toFixed(1)}%`}function i(o){return o>=30?chalk.red("🔥🔥🔥 "+o+" days"):o>=7?chalk.yellow("🔥 "+o+" days"):o>=1?chalk.gray("· "+o+" day"+(o>1?"s":"")):chalk.gray("—")}export function renderWelcome(o,e,l){const t=c(),r=e.cardsCompleted.length,a=o.totalCards,y=a-r,d=o.modules.find(o=>e.currentCard>=o.cardRange[0]&&e.currentCard<=o.cardRange[1]);if(console.log(),console.log(chalk.cyan(" ╭"+s("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+n("academy_title",t))),console.log(chalk.cyan(" ║")),l)console.log(chalk.cyan(" ║ ")+chalk.white(" "+n("welcome_new_demo",t)));else{const o=new Date(e.lastSeenAt),l=Math.floor((Date.now()-o.getTime())/36e5),r=l<1?n("just_now",t):l<24?`${l}${n("ago_hours",t)}`:`${Math.floor(l/24)}${n("ago_days",t)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${n("welcome_back",t)} ${r}`))}console.log(chalk.cyan(" ║"));const u=d?`${d.number}. ${d.name}`:"—";console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",t).padEnd(11))+chalk.white(u)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",t).padEnd(11))+g(r,a)+chalk.gray(` (${r}/${a})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",t).padEnd(11))+i(e.streakDays)),y>0&&e.currentCard<=a&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",t).padEnd(11))+chalk.white(`#${e.currentCard} ${n("of",t)} ${a}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+s("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const h=0===r?n("continue_start",t):`${n("continue_resume",t)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+h)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",t))),e.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${e.bookmarks.length} ${n("bookmarks_desc",t)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+n("quit_desc",t))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function y(o,e){const l=c(),t=o.module,r=e.modules.find(o=>o.number===t),s=r?r.name:n("unknown_module",l),g=n("module",l).replace(":","").trim(),i=`${n("card",l)} ${o.number} / ${e.totalCards} · ${g} ${t} · ${s}`;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(i)+" "+chalk.cyan("─".repeat(Math.max(0,a-i.length-4))))}function d(){console.log(chalk.cyan(" ╰"+s())),console.log()}function u(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function h(){console.log(chalk.cyan(" │"))}const m=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function f(o){return/^(?: {4}|\t)/.test(o)||m.test(o)||/ {3,}/.test(o)}function b(e,n){const l=[];let t="",r=0,c=!1;for(const a of function(e){const n=[],l=e=>{let l="";const t=()=>{l&&(n.push({t:"atom",s:l}),l="")};for(const r of e)" "===r?(t(),n.push({t:"space"})):2===o(r)?(t(),n.push({t:"wide",s:r})):l+=r;t()},t=/`[^`]*`/g;let r,c=0;for(;null!==(r=t.exec(e));)l(e.slice(c,r.index)),n.push({t:"code",s:r[0]}),c=r.index+r[0].length;return l(e.slice(c)),n}(e)){if("space"===a.t){""!==t&&(c=!0);continue}const e=a.s,s=o("code"===a.t?e.replace(/`/g,""):e),g=c?1:0,i="atom"===a.t&&e.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(e);""!==t&&!i&&r+g+s>n?(l.push(t),t=e,r=s,c=!1):(c&&(t+=" ",r+=1,c=!1),t+=e,r+=s)}return t&&l.push(t),l}const p=/\b(?:Common bug|Common mistake|Common error|Pitfall|Gotcha|Footgun)\b[::]?|\b(?:Warning|Caution)\b[::]|(?:常见错误|常见陷阱|常见误区|陷阱|注意|警告|误区)[::]/gi;function w(o,e){const n=e?chalk.bold.white:chalk.white;let l,t="",r=0;for(p.lastIndex=0;null!==(l=p.exec(o));)l.index>r&&(t+=n(o.slice(r,l.index))),t+=chalk.bold.yellow(l[0]),r=l.index+l[0].length;return r<o.length&&(t+=n(o.slice(r))),t}function _(o,e=!1){let n="",l=0;const t=/`([^`]*)`/g;let r;for(;null!==(r=t.exec(o));)r.index>l&&(n+=w(o.slice(l,r.index),e)),n+=chalk.cyan(r[1]),l=r.index+r[0].length;return l<o.length&&(n+=w(o.slice(l),e)),n}function $(o,e=!1){let n=!1;for(const l of o.split("\n"))if(/^\s*```/.test(l))n=!n;else if(n)console.log(chalk.cyan(" │ ")+chalk.cyan(` ${l}`));else if(""!==l)if(f(l))console.log(chalk.cyan(" │ ")+chalk.white(l));else for(const o of b(l,60))console.log(chalk.cyan(" │ ")+` ${_(o,e)}`);else h()}function k(o,e){const n=[];for(const l of o.split("\n")){if(""===l){n.push("");continue}if(f(l)){n.push(l);continue}let o="";for(const t of l.split(" "))(o+" "+t).trim().length>e?(n.push(o.trim()),o=t):o=(o+" "+t).trim();o&&n.push(o)}return n}export function renderKnowledgeCard(l,s){const g=c(),i=e(l,g);y(i,s),h();const m=b(i.title,60);for(const o of m)u(chalk.bold.yellow(o));u(chalk.gray("─".repeat(Math.min(Math.max(...m.map(e=>o(e))),a))));const f=!!(l.chart_ids&&l.chart_ids.length>0);if(f&&t(l.chart_ids.slice(0,1),s.charts,r()),i.key_point&&""!==i.key_point.trim()?(f||h(),function(o,e,n){const l=c().startsWith("zh"),t=r().cols,s="-".repeat(Math.min(a,Math.max(24,t-6))),g=l?"要点":"KEY POINT",i=l?`第 ${e} / ${n} 张`:`Card ${e} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(s)),console.log(chalk.cyan(" │ ")+chalk.green(`${g} · ${i}`));for(const e of b(o,60))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+e));console.log(chalk.cyan(" │ ")+chalk.green(s)),h()}(i.key_point,i.number,s.totalCards)):f||h(),i.body.forEach((o,e)=>{const n=0===e?o.match(/^([\s\S]*?(?:[.!?](?=\s|$)|[。!?]))(\s[\s\S]*)?$/):null;if(n&&n[1].trim().length<=180){$(n[1],!0);const o=n[2]?.trimStart();o&&$(o,!1)}else $(o,!1);h()}),i.icoaConnection){u(chalk.magenta(" "+n("icoa_connection",g))),u(chalk.gray(" "+"─".repeat(20)));for(const o of k(i.icoaConnection,60))u(" "+chalk.magenta(o));h()}if(l.check){const o=g.startsWith("zh"),e=o?l._zh?.checkStatement??l.check.statement:l.check.statement;u(chalk.cyan(" 🤔 "+(o?"快速理解检查 (输 y 或 n):":"Quick comprehension check (type y or n):"))),u(chalk.gray(" "+"─".repeat(20)));for(const o of k(e,60))u(" "+chalk.white(o));h()}d(),function(o,e){const n=c().startsWith("zh"),l=r().cols,t="-".repeat(Math.min(a,Math.max(24,l-6))),s=chalk.gray(" "),g=[];o.check?(g.push(chalk.bold.green("[y]")+chalk.gray(n?" 是":" yes")),g.push(chalk.bold.green("[n]")+chalk.gray(n?" 否":" no"))):g.push(chalk.bold.green("[ok]")+chalk.gray(n?" 继续":" next")),g.push(chalk.bold.yellow("[e]")+chalk.gray(n?" 报告问题":" report")),g.push(chalk.bold.cyan("[b]")+chalk.gray(n?" 上一张":" back")),g.push(chalk.bold.cyan("[card N]")+chalk.gray(n?" 跳转":" jump")),g.push(chalk.gray("[quit]")+chalk.gray(n?" 退出":" quit")),console.log(chalk.gray(" "+t)),console.log(chalk.gray(" "+(n?`第 ${o.number} / ${e.totalCards} 张`:`Card ${o.number} / ${e.totalCards}`))),console.log(" "+g.join(s)),console.log(chalk.gray(" "+t)),console.log(chalk.gray(" "+(n?"e = 报告本卡错误,不算作答;提交后回到本题继续":"e = report a problem with this card (not an answer); you return here after"))),console.log(chalk.gray(" "+(n?"更多: bookmark · status":"more: bookmark · status"))),console.log()}(l,s)}export function renderCheckFeedback(o,e,n,l=!1){const t=c().startsWith("zh");if(console.log(),n)console.log(chalk.green(" ✓ ")+chalk.white(t?"答对了。":"Correct."));else if(console.log(chalk.yellow(" ✗ ")+chalk.white(t?"记下了,继续。":"Noted — moving on.")),l){const e="y"===o.check?.answer?t?"是":"Yes":t?"否":"No";console.log(chalk.gray(" "+(t?"正确答案: ":"Answer: ")+e+". "+(t?"回看上面那段。":"Re-read the paragraph above.")))}console.log()}export function renderPhaseComplete(o,e,n,l){const t=c().startsWith("zh"),[r,a]=n.cardRange,s=e.checkResults||{};let g=0,i=0;for(let o=r;o<=a;o++){const e=s[String(o)];e&&(g++,e.correct&&i++)}const y="-".repeat(48);console.log(),console.log(chalk.bold.yellow(" "+y)),console.log(chalk.bold.yellow(" "+(t?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const d=g>0&&i===g?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(t?"理解检查得分: ":"comprehension score: "))+d(`${i}/${g}`)+chalk.gray(0===g?t?" (本阶段未作答 y/n)":" (no y/n answered)":"")),l&&console.log(chalk.gray(" "+(t?"下一阶段: ":"next: "))+chalk.bold.cyan(t?`阶段 ${l.number}`:`Phase ${l.number}`)+chalk.gray(" · ")+chalk.white(l.name)),console.log(chalk.bold.yellow(" "+y))}export function renderMCQCard(o,l){const t=c(),r=e(o,t);y(r,l),h(),u(chalk.bold.yellow("🎯 "+r.title)),u(chalk.gray("─".repeat(Math.min(r.title.length+4,a)))),h();for(const o of k(r.question,60))u(" "+o);h();for(const o of["A","B","C","D"])u(chalk.cyan(` ${o}.`)+" "+chalk.white(r.options[o]));h(),d(),console.log(chalk.gray(" "+(t.startsWith("zh")?"输入 ":"Type "))+chalk.bold.green("A")+chalk.gray(" / ")+chalk.bold.green("B")+chalk.gray(" / ")+chalk.bold.green("C")+chalk.gray(" / ")+chalk.bold.green("D")+chalk.gray(" "+n("type_to_answer",t))),console.log()}export function renderMCQFeedback(o,l,t,r){const a=c(),s=e(o,a);console.log(),t?console.log(" "+chalk.bold.green(n("correct",a)+" ")+chalk.gray(`${n("one_point",a)} · ${l} = ${s.options[l]}`)):console.log(" "+chalk.bold.red(n("not_quite",a)+" ")+chalk.gray(`${n("you_chose",a)} ${l}; ${n("answer_is",a)} ${s.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",a)));for(const o of k(s.explanation,60))console.log(chalk.gray(" "+o));const g=Object.values(r.mcqResults),i=g.filter(o=>o.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",a)+" ")+chalk.white(`${i}/${g.length}`)),console.log(),t||(console.log(chalk.gray(" "+n("press_ok_continue",a)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",a))),console.log())}export function renderPracticalCard(o,l){const t=c(),r=e(o,t);y(r,l),h(),u(chalk.bold.yellow("🛠 "+r.title)),u(chalk.gray("─".repeat(Math.min(r.title.length+4,a)))),h();for(const o of k(r.task,60))u(" "+o);if(r.starterCode){h(),u(chalk.gray(" "+n("starter_code",t))),h();for(const o of r.starterCode.split("\n"))u(" "+chalk.cyan(o))}h(),d(),console.log(chalk.gray(" "+n("try_in_sandbox",t)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+n("drops_into_python",t))),console.log(chalk.gray(" "+n("when_done",t))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+n("done_desc",t))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+n("skip_desc",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log()}export function renderPracticalSuccess(o){const l=c(),t=e(o,l);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",l))),console.log(),console.log(chalk.gray(" "+n("reference_answer",l)));for(const o of k(t.successHint,60))console.log(chalk.gray(" "+o));console.log(),console.log(chalk.gray(" "+n("press_ok_continue",l)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",l))),console.log()}export function renderSimDemoCard(o,l){const t=c(),r=e(o,t);y(r,l),h(),u(chalk.bold.yellow("🎬 "+r.title)),h();for(const o of k(r.description,60))u(" "+o);h(),u(chalk.gray(" "+n("sim_requires",t))),h(),d(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+n("sim_launch_desc",t))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",t))),console.log(chalk.gray(" bookmark "+n("bookmark_desc",t))),console.log(chalk.gray(" back "+n("back_desc",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",t))),console.log()}export function renderMilestone(o,l){const t=c(),r=e(o,t),a=o.number===l.totalCards;console.log(),console.log(chalk.bold.yellow(" ╭"+s("═"))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+chalk.white(" "+n("milestone_header",t))),console.log(chalk.bold.yellow(" ║"));const g=` ${r.badge} ${r.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(g)),console.log(chalk.bold.yellow(" ║ ")+chalk.gray(" ─".repeat(Math.max(1,Math.floor(r.badge.length/2+2))))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("in_wild_corresponds",t)));for(const o of k(r.realWorldLevel,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",t)));for(const o of k(r.unlockedNext,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+s("═"))),console.log(),a?(console.log(chalk.gray(" "+n("demo_complete",t))),console.log(),console.log(chalk.gray(" "+n("unlock_full",t)+" ")+chalk.white(n("curriculum_name",t))+chalk.gray(",")),console.log(chalk.gray(" "+n("contact_team_leader",t)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+n("learn_token",t))),console.log(chalk.gray(" "+n("or_email",t)+" ")+chalk.cyan("asra@icoa2026.au")+chalk.gray(" "+n("for_partnership",t))),console.log(),console.log(chalk.gray(" "+n("type_quit",t)+" ")+chalk.bold.green("quit")+chalk.gray(" "+n("to_exit",t)+" ")+chalk.bold.green("status")+chalk.gray(" "+n("for_dashboard",t)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",t))),console.log(chalk.gray(" status "+n("status_full",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",t)))),console.log()}export function renderStatus(o,e){const l=c(),t=e.cardsCompleted.length,r=o.totalCards,a=Object.values(e.mcqResults),s=a.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(n("status_title",l))+" "+chalk.cyan("─".repeat(25))),h(),u(" "+chalk.gray(n("total_progress",l)+" ")+g(t,r)+chalk.gray(` (${t}/${r})`)),u(" "+chalk.gray(n("streak",l)+" ")+i(e.streakDays)+chalk.gray(` (${n("longest",l)} ${e.longestStreak})`)),u(" "+chalk.gray(n("mcq_accuracy",l)+" ")+chalk.white(`${s}/${a.length}`)),u(" "+chalk.gray(n("practicals_done",l)+" ")+chalk.white(`${e.practicalsCompleted.length}`)),u(" "+chalk.gray(n("bookmarked",l)+" ")+chalk.white(`${e.bookmarks.length}`)),h();const y=n("module",l).replace(":","").trim();for(const n of o.modules){const[o,l]=n.cardRange,t=e.cardsCompleted.filter(e=>e>=o&&e<=l).length,r=l-o+1;u(" "+(t===r?chalk.green("✓"):t>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${y} ${n.number}: `)+chalk.white(`${t}/${r}`)+chalk.gray(" "+n.name))}if(h(),e.achievements.length>0){u(" "+chalk.gray(n("achievements",l)));for(const o of e.achievements)u(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else u(" "+chalk.gray(n("achievements_none",l)));h(),d()}
|
package/dist/lib/render-card.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import chalk from"chalk";export function detectCaps(){const t=process.stdout.columns||80,o=process.env.LANG||process.env.LC_ALL||"",
|
|
1
|
+
import chalk from"chalk";export function detectCaps(){const t=process.stdout.columns||80,o=process.env.LANG||process.env.LC_ALL||"",n=/utf-?8/i.test(o)||""===o||"C.UTF-8"===o;return"ascii"!==(process.env.ICOA_RENDER||"").toLowerCase()&&"C"!==o&&"POSIX"!==o&&n?{tier:"T1",cols:t,utf8:n}:{tier:"T0",cols:t,utf8:!1}}function t(t,o){return"T0"===o.tier&&t.art_t0||t.width_cols&&t.width_cols>o.cols-6&&t.art_t0?t.art_t0:t.art??t.art_t0??""}function o(t){try{const o=t.split(".").reduce((t,o)=>t[o],chalk);return"function"==typeof o?o:chalk.gray}catch{return chalk.gray}}function n(t,n){let c="";for(const e of t){const t=" "===e?void 0:n[e];c+=t?o(t)(e):chalk.gray(e)}return c}export function renderCharts(o,c,e=detectCaps(),r={}){if(!o||0===o.length)return;if(!c)return;const s=!1!==r.frameLeft,i=s?chalk.cyan(" │ "):" ",l=s?chalk.cyan(" │"):"";let f=0;for(const r of o){const o=c[r];if(!o)continue;if("code"===o.type){const t=o.code??"";if(""===t.trim())continue;console.log(l);for(const o of t.split("\n"))console.log(i+chalk.cyan(o));f++;continue}const s=t(o,e);if(""===s)continue;const a=chalk.level>0&&o.color_map&&Object.keys(o.color_map).length>0;console.log(l);for(const t of s.split("\n"))console.log(i+(a?n(t,o.color_map):chalk.gray(t)));f++}f>0&&console.log(l)}
|
package/dist/repl.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as o}from"node:readline";import{spawn as e,execSync as l}from"node:child_process";import chalk from"chalk";import{isConnected as n,getConfig as t,saveConfig as a}from"./lib/config.js";import{isActivated as s,activateToken as r,isFreeCommand as i,isDeviceMatch as c,recordExit as g,recordResume as y,isFirstRunOrUpgrade as m,markVersionSeen as d}from"./lib/access.js";import{setReplMode as p}from"./lib/ui.js";import{returnToMainMenu as u,printMenuHint as h,menuHintInline as w}from"./lib/menu-nav.js";import{setMainRl as f}from"./lib/main-rl.js";import{isChatActive as b,handleChatMessage as x}from"./commands/ai4ctf.js";import{isCtf4aiActive as v,handleCtf4aiMessage as C}from"./commands/ctf4ai-demo.js";import{isCtf4VlaActive as A,handleCtf4VlaMessage as I}from"./commands/ctf4vla.js";import{isDemo2Active as k}from"./commands/demo2.js";import{loadDemo2Progress as T}from"./lib/demo2-progress.js";import{hasAnySimHistory as S,lastArmAt as $,lastBundledAt as O,lastSimAt as E}from"./lib/sim-cooldown.js";import{getExamState as L,getRealExamState as D,getDemoState as P}from"./lib/exam-state.js";import{resetTerminalTheme as R}from"./lib/theme.js";import{ensureSandbox as j,runInSandbox as q,isDockerAvailable as F}from"./lib/sandbox.js";import{checkShellRisk as N,logShellAudit as M,getActiveCwd as B}from"./lib/exam-sandbox.js";import{logCommand as U}from"./lib/logger.js";import{startLogSync as _,stopLogSync as Q}from"./lib/log-sync.js";import{existsSync as W,mkdirSync as X,writeFileSync as Y}from"node:fs";import{join as z}from"node:path";import{homedir as V}from"node:os";function G(){return D()?chalk.cyan("icoa exam> "):P()?chalk.yellow("icoa demo> "):chalk.green("icoa> ")}const J=z(V(),"icoa-workspace"),H=new Set(["sudo","su","doas","pkexec","brew","apt","apt-get","yum","choco","npm","npx","pip","pip3","shutdown","reboot","halt","mkfs","fdisk","dd","iptables","ufw"]),K="__REPL_NO_EXIT__",Z="2.5.1";function oo(){console.log(),console.log(` ${chalk.cyan.bold("🎯 ICOA 2026 · National Selection")}`),console.log(),console.log(" "+chalk.bold.cyan("learn ")+chalk.white("ACxxxxxxxx")+chalk.gray(" → ai4ctf-12 (AI as your CTF teammate)")),console.log(" "+chalk.bold.cyan("learn ")+chalk.white("CAxxxxxxxx")+chalk.gray(" → ctf4ai-12 (red-team software AI)")),console.log(" "+chalk.bold.cyan("learn ")+chalk.white("EAxxxxxxxx")+chalk.gray(" → ctf4eai-12 (red-team embodied AI)")),console.log(" "+chalk.bold.cyan("exam ")+chalk.white("<CC>xxxxxxxx")+chalk.gray(" → Paper A/B/C/D/E (country exam token)")),console.log(" "+chalk.bold.cyan("demo")+chalk.gray(" → free practice")),console.log(" "+chalk.bold.cyan("lang")+chalk.white(" es")+chalk.gray(" → switch language")),console.log(" "+chalk.gray("help · menu · quit")),console.log()}export async function startRepl(e,P){process.env.ICOA_INSIDE_REPL="1";const no=t(),to=n(),ao=process.exit.bind(process),so=s();if(no.demoCleanedForVersion!==Z){try{const{existsSync:o,unlinkSync:e}=await import("node:fs"),{join:l}=await import("node:path"),{getIcoaDir:n}=await import("./lib/config.js"),t=l(n(),"demo-state.json");o(t)&&e(t)}catch{}a({demoCleanedForVersion:Z})}const{select:ro,confirm:io}=await import("@inquirer/prompts"),co=no.mode||"",go=""===co?"instant":["instant","learn","selection","olympiad"].includes(co)?co:"selection",yo=[{name:` ${chalk.bold("⚡ Try ICOA in 30 seconds")} ${chalk.gray("—")} ${chalk.gray("watch · no token needed")}`,value:"instant"},{name:` ${chalk.bold("🧠 Learn")} ${chalk.gray("—")} ${chalk.gray("AC / CA / EA token · skill up")}`,value:"learn"},{name:` ${chalk.bold("🎯 National Selection")} ${chalk.gray("—")} ${chalk.gray("country exam token · aim for finals")}`,value:"selection"},{name:` ${chalk.bold("🏆 International Olympiad")} ${chalk.gray("—")} ${chalk.gray("finals stage · Sydney 2026 · full sandbox")}`,value:"olympiad"},{name:` ${chalk.gray("ℹ️ About ICOA")} ${chalk.gray("·")} ${chalk.gray("what this is · how to join")}`,value:"about"}];console.log(chalk.gray(" Type ")+chalk.bold.cyan("menu")+chalk.gray(" from anywhere to return here.")),console.log();let mo="";for(;!mo;){let o;try{o=await ro({message:"Mode",choices:yo,default:go||"instant"})}catch(o){throw o instanceof Error&&"ExitPromptError"===o.name&&(console.log(),console.log(chalk.gray(" Goodbye.")),process.exit(0)),o}"about"!==o?mo=o:(console.clear(),console.log(),console.log(chalk.cyan(" ═══════════════════════════════════════════════════")),console.log(chalk.bold.yellow(" ICOA")+chalk.white(" — AI-Native CLI OS for Cyber & AI Security")),console.log(chalk.gray(" Olympiad & Competition · K-12 to University")),console.log(chalk.cyan(" ───────────────────────────────────────────────────")),console.log(),console.log(chalk.bold.white(" What Makes ICOA Different")),console.log(chalk.gray(" · AI-native AI teammate, AI adversary, AI translation")),console.log(chalk.gray(" · CLI OS Complete competition environment in terminal")),console.log(chalk.gray(" · 110 tools pwntools, z3, gdb, nmap, sleuthkit... pre-configured")),console.log(chalk.gray(" · Global scale 15,000+ concurrent exams · 15 languages")),console.log(),console.log(chalk.bold.white(" Competition Format (2-day standard + 1-day Pioneer)")),console.log(` ${chalk.green.bold("AI4CTF")}${chalk.gray(" [Day 1] AI as teammate — speed track, dynamic decay + top-3 bonus")}`),console.log(` ${chalk.red.bold("CTF4AI")}${chalk.gray(" [Day 2] AI as target — knowledge track, static value per challenge")}`),console.log(` ${chalk.cyan.bold("CTF4EAI")}${chalk.gray(" [Pioneer / Practical Round] Embodied AI — VLA + world models + sim-to-real")}`),console.log(),console.log(chalk.bold.white(" Scoring (in brief)")),console.log(chalk.gray(" · Standard medals: AI4CTF + CTF4AI combined · 1/12 Gold · 2/12 Silver · 3/12 Bronze")),console.log(chalk.gray(" · HMA: top 50% on either day, total below bronze cutoff")),console.log(chalk.gray(" · CTF4EAI: separate 15-finalist pool, G/S/B + 12 Finalist Awards")),console.log(),console.log(chalk.white(" Sydney, Australia")+chalk.gray(" · Jun 27 - Jul 2, 2026")),console.log(),console.log(chalk.bold.white(" Organized by")+chalk.gray(" ASRA (Australia) · ICO Foundation Inc")),console.log(chalk.bold.white(" Contact ")+chalk.cyan(" australia@icoa2026.au · accreditation@icoa2026.au")),console.log(chalk.bold.white(" Website ")+chalk.cyan.underline(" https://icoa2026.au")),console.log(chalk.cyan(" ═══════════════════════════════════════════════════")),console.log(),console.log(chalk.gray(" Press ")+chalk.yellow("Enter")+chalk.gray(" to return...")),await new Promise(o=>{const e=l=>{process.stdin.removeListener("data",e),process.stdin.isTTY&&process.stdin.setRawMode&&process.stdin.setRawMode(!1),process.stdin.pause(),o()};process.stdin.isTTY&&process.stdin.setRawMode&&process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.once("data",e)}),console.clear())}if("olympiad"===mo&&"olympiad"!==go){console.log(),console.log(chalk.yellow(" This mode will download ~500MB of CTF tools and AI models."));let o=!0;try{o=await io({message:"Continue?",default:!0})}catch(e){if(!(e instanceof Error&&"ExitPromptError"===e.name))throw e;o=!1}o||(mo="selection",console.log(chalk.gray(" Switched to National Selection mode.")))}if(mo!==go&&a({mode:mo}),console.log(),"olympiad"===mo&&m(Z)){d(Z),console.log(chalk.gray(" Checking competition environment..."));const{execSync:o}=await import("node:child_process"),e=[{name:"pwntools",cmd:'python3 -c "import pwn"'},{name:"z3-solver",cmd:'python3 -c "import z3"'},{name:"numpy",cmd:'python3 -c "import numpy"'},{name:"requests",cmd:'python3 -c "import requests"'}];let l=0;for(const n of e)try{o(n.cmd,{stdio:"ignore"})}catch{l++}if(l>0){console.log(chalk.yellow(` ${l} core libraries missing.`));try{const{confirm:o}=await import("@inquirer/prompts");if(await o({message:" Install competition Python libraries now?",default:!0,theme:{prefix:"",style:{message:o=>chalk.green(o),defaultAnswer:o=>chalk.green(o)}}})){console.log();const{execSync:o}=await import("node:child_process");o("icoa env setup",{stdio:"inherit"})}}catch{console.log(chalk.gray(" Run ")+chalk.white("env setup")+chalk.gray(" later to install."))}console.log()}else console.log(chalk.green(" All core libraries ready.")),console.log()}if(P){const o=y();if(o){const e=Math.floor(o.awaySeconds/60),l=o.awaySeconds%60;console.log(chalk.yellow(` Session resumed. Away: ${e}m ${l}s | Total exits: ${o.exitCount}`)),console.log()}}if("selection"===mo)console.log(chalk.gray(" ─── exam hall · 90 min per paper once started · no pause ───")),console.log(),oo();else if("embodied"===mo)!function(){console.log(chalk.cyan.bold(" [🤖 ctf4eai — Embodied AI Security]")),console.log(),console.log(chalk.bold.cyan(" CTF4VLA")+chalk.gray(" — ICOA 2026's ")+chalk.bold.white("Pioneer Round")),console.log(chalk.gray(" an independent parallel track alongside the scored main events: ")+chalk.green("AI4CTF")+chalk.gray(" (Day 1) and ")+chalk.red("CTF4AI")+chalk.gray(" (Day 2)")),console.log(),console.log(chalk.white(" Embodied AI is at its ImageNet moment.")),console.log(chalk.gray(" VLA models, MuJoCo physics, real robot arms.")),console.log(chalk.gray(" Real models, real physics, real attack surfaces.")),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const o=O()>0||E()>0,e=$()>0,l=S(),n=T(),t=null!==n&&"number"==typeof n.completedAt&&n.nextCardIndex>=n.totalCards,a=null!==n&&!t;if(t)console.log(chalk.bold.yellow(" Continue: ")+chalk.bold.cyan("④ learn LEARNDEMO01")+chalk.gray(" (demo2 ✓ · sim")+(e?chalk.gray(" ✓ · sim arm ✓)"):chalk.gray(")")));else if(a){const o=`${n.nextCardIndex} / ${n.totalCards}`;console.log(chalk.bold.yellow(" Continue: ")+chalk.bold.cyan("③ demo2")+chalk.gray(" (last left at card ")+chalk.bold.yellow(o)+chalk.gray(")"))}else o&&e?console.log(chalk.bold.yellow(" Next up: ")+chalk.bold.cyan("③ demo2")+chalk.gray(" → ")+chalk.bold.cyan("④ learn LEARNDEMO01")+chalk.gray(" (sim ✓ · sim arm ✓)")):l?console.log(chalk.bold.yellow(" Next up: ")+(e?chalk.bold.cyan("① sim")+chalk.gray(" → ")+chalk.bold.cyan("③ demo2"):chalk.bold.cyan("② sim arm")+chalk.gray(" → ")+chalk.bold.cyan("③ demo2"))+chalk.gray(" → ")+chalk.bold.cyan("④ learn LEARNDEMO01")):console.log(chalk.bold.yellow(" First time? Try in order: ")+chalk.bold.cyan("① sim")+chalk.gray(" → ")+chalk.bold.cyan("② sim arm")+chalk.gray(" → ")+chalk.bold.cyan("③ demo2")+chalk.gray(" → ")+chalk.bold.cyan("④ learn LEARNDEMO01"));console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.bold.green(" sim")+chalk.gray(" ")+chalk.bold.cyan("①")+chalk.gray(" ")+chalk.bold.yellow("★ INSTANT")+chalk.gray(" — 8-robot group dance, no wait, no setup")),console.log(chalk.bold.green(" sim arm")+chalk.gray(" ")+chalk.bold.cyan("②")+chalk.gray(" interactive 6-DOF arm sandbox — type joint values, watch")),console.log(chalk.bold.green(" demo2")+chalk.gray(" ")+chalk.bold.cyan("③")+chalk.gray(" ~8 min interactive intro + live MuJoCo sim")),console.log(chalk.bold.green(" learn LEARNDEMO01")+chalk.gray(" ")+chalk.bold.cyan("④")+chalk.gray(" free 11-card intro (paste this command as-is)")),console.log(chalk.bold.green(" learn ")+chalk.bold.yellow("EA")+chalk.gray(" + 8 chars full 96 / 360 curriculum (team-issued)")),console.log(chalk.bold.green(" exam ")+chalk.bold.yellow("PDxxxxxxxx")+chalk.gray(" Paper D (CTF4VLA) — national selection contestants")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(),console.log(chalk.gray(" Three open tiers: ")+chalk.green("free 11")+chalk.gray(" · ")+chalk.yellow("registered 100")+chalk.gray(" · ")+chalk.bold.magenta("national team 480")+chalk.gray(" (PhD-ready)")),console.log(chalk.gray(" Country team contact: ")+chalk.cyan("australia@icoa2026.au")),console.log(),h(),console.log()}();else{if("instant"===mo){console.log(chalk.cyan.bold(" [⚡ Try ICOA in 30 seconds — no token needed]")),console.log(),console.log(chalk.gray(" Pick what you want to see first:")),console.log();const o=[{name:` ${chalk.bold.green("★ demo ")} ${chalk.gray("Paper A/B/C taste — MCQ + ai4ctf + ctf4ai ")}`+chalk.bold.cyan("🎯 selection-style"),value:"demo"},{name:` ${chalk.bold.green("★ demo2 ")} ${chalk.gray("15-sec countdown render · bundled & offline ")}`+chalk.bold.yellow("⚡ visual wow"),value:"demo2"},{name:` ${chalk.gray("↩ back to main menu")}`,value:"menu"}];let l="menu";try{l=await ro({message:"Pick",choices:o,default:"demo"})}catch{}if("menu"!==l){console.log();const o="demo2"===l?["sim"]:["exam","demo"];try{await e.parseAsync(["node","icoa",...o])}catch{}}return a({mode:"selection"}),void u()}if("learn"===mo){console.log(chalk.cyan.bold(" [🧠 Learn]")),console.log(),console.log(chalk.gray(" Three tracks · same difficulty curve.")),console.log(),console.log(chalk.white(" Free 12-card demos — paste the full command:")),console.log(),console.log(chalk.gray(" 🤝 ai4ctf AI as your teammate ")+chalk.bold.yellow("learn AI4CTFDEMO01")),console.log(chalk.gray(" 🔓 ctf4ai AI as your target ")+chalk.bold.yellow("learn CTF4AIDEMO01")),console.log(chalk.gray(" 🤖 ctf4eai Embodied AI Security ")+chalk.bold.yellow("learn LEARNDEMO01")),console.log(),console.log(chalk.gray(" Got a cohort token? Type ")+chalk.bold.yellow("learn <TOKEN>")+chalk.gray(" (e.g. ")+chalk.bold.yellow("learn EAABCD1234")+chalk.gray(").")),console.log();try{const{input:o}=await import("@inquirer/prompts"),{resolveLearnInput:l}=await import("./lib/learn-input.js"),n=l(await o({message:" Paste full command (or blank to go back):"}));"token"===n.kind?(console.log(),console.log(chalk.gray(" Opening ")+chalk.bold.cyan("learn")+chalk.gray(`: ${n.token}`)),console.log(),a({mode:"selection"}),await e.parseAsync(["node","icoa","learn",n.token])):"ambiguous"!==n.kind&&"unknown"!==n.kind||(console.log(),console.log(chalk.yellow(" "+n.hint)),console.log(chalk.gray(" Returning to main menu — pick 🧠 Learn again to retry.")),console.log())}catch{}return a({mode:"selection"}),void u()}"organizer"===mo?(console.log(chalk.yellow.bold(" [National/Regional Partner]")),console.log(),console.log(chalk.bold.white(" ██╗ ██████╗ ██████╗ █████╗")),console.log(chalk.bold.white(" ██║██╔════╝██╔═══██╗██╔══██╗")),console.log(chalk.bold.white(" ██║██║ ██║ ██║███████║")),console.log(chalk.bold.white(" ██║██║ ██║ ██║██╔══██║")),console.log(chalk.bold.white(" ██║╚██████╗╚██████╔╝██║ ██║")),console.log(chalk.bold.white(" ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝")),console.log(),console.log(chalk.yellow(" International Cyber Olympiad in AI 2026")),console.log(chalk.bold.magenta(" The World's First AI-Native CLI Operating System")),console.log(chalk.bold.magenta(" for Cybersecurity & AI Security Competition")),console.log(chalk.bold.magenta(" and Olympiad for K-12")),console.log(chalk.gray(" Sydney, Australia · Jun 27 - Jul 2, 2026")),console.log(),console.log(chalk.white(" Vision")),console.log(chalk.gray(" Building a global pipeline for youth cyber & AI")),console.log(chalk.gray(" security talent through education and competition.")),console.log(),console.log(chalk.white(" Capacity")),console.log(chalk.gray(" 15,000+ concurrent online examinations")),console.log(chalk.gray(" National selection, training, and education support")),console.log(),console.log(chalk.white(" Olympic Spirit")),console.log(chalk.gray(" Excellence · Friendship · Respect")),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.white(" New country accreditation & support:")),console.log(chalk.cyan(" australia@icoa2026.au")),console.log(chalk.cyan(" accreditation@icoa2026.au")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(),to?(console.log(chalk.green(` Logged in as ${no.userName}`)),console.log(chalk.white(" exam list")+chalk.gray(" Manage exams")),console.log(chalk.white(" logout")+chalk.gray(" Disconnect"))):console.log(chalk.white(" join <url>")+chalk.gray(" Connect to manage exams")),console.log(),h(),console.log()):(console.log(` ${chalk.cyan.bold("🏆 ICOA 2026 · International Olympiad")}`),console.log(),so&&!c()?(console.log(chalk.red(" Token was activated on a different device.")),console.log(chalk.gray(" Contact organizer for assistance.")),console.log()):to?(console.log(chalk.green.bold(` Welcome back, ${no.userName}!`)),console.log(chalk.gray(` Connected to ${no.ctfdUrl}`)),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.white(" Ready to compete? Start here:")),console.log(),console.log(chalk.bold.cyan(" challenges")+chalk.gray(" Browse challenges by category")),console.log(chalk.white(" status")+chalk.gray(" Your score & hint budget")),console.log(chalk.white(" scoreboard")+chalk.gray(" Live rankings")),console.log(chalk.white(" help")+chalk.gray(" Full command list")),console.log(),console.log(chalk.gray(" Tool environment:")),console.log(chalk.white(" env")+chalk.gray(" See which of the 110 CTF tools are installed")),console.log(chalk.white(" env setup")+chalk.gray(" Install anything missing (~5 min, one-time)")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.gray(" Tip: ")+chalk.cyan("help")+chalk.gray(" · ")+w()+chalk.gray(" · ")+chalk.cyan("Ctrl+C")+chalk.gray(" pauses · ")+chalk.cyan("quit")+chalk.gray(" closes")),console.log()):so?(W(J)||X(J,{recursive:!0}),console.log(chalk.green.bold(" Welcome, competitor!")),console.log(chalk.gray(` Workspace: ${J}`)),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.white(" Get started:")),console.log(),console.log(chalk.white(" Step 1 ")+chalk.bold.cyan("join <url>")+chalk.gray(" Connect to competition server")),console.log(chalk.white(" Step 2 ")+chalk.bold.cyan("challenges")+chalk.gray(" Browse & solve challenges")),console.log(chalk.white(" Step 3 ")+chalk.bold.cyan("ai4ctf")+chalk.gray(" Ask AI when stuck")),console.log(),console.log(chalk.gray(" Before Step 1 — make sure your tools are ready:")),console.log(chalk.white(" env")+chalk.gray(" See which of the 110 CTF tools are installed")),console.log(chalk.white(" env setup")+chalk.gray(" Install anything missing (~5 min, one-time)")),console.log(),console.log(chalk.gray(" Also: ")+chalk.white("help")+chalk.gray(" all commands")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.gray(" Tip: ")+chalk.cyan("Ctrl+C")+chalk.gray(" pauses · ")+w()+chalk.gray(" · ")+chalk.cyan("quit")+chalk.gray(" closes CLI")),console.log()):(console.log(chalk.bold.yellow(" 🏆 You reached the finals tier.")),console.log(chalk.gray(" Full sandbox · 110 CTF tools · Sydney 2026 · this is where finalists compete.")),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.white(" To begin, activate your competition token:")),console.log(),console.log(chalk.bold.cyan(" activate <token>")),console.log(),console.log(chalk.gray(" While waiting, explore:")),console.log(chalk.white(" ref linux")+chalk.gray(" Quick reference for Linux")),console.log(chalk.white(" ref web")+chalk.gray(" Quick reference for Web")),console.log(chalk.white(" env")+chalk.gray(" See which of the 110 CTF tools are installed")),console.log(chalk.white(" env setup")+chalk.gray(" Install anything missing (~5 min, one-time)")),console.log(chalk.white(" help")+chalk.gray(" All available commands")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.gray(" Tip: ")+chalk.cyan("Ctrl+C")+chalk.gray(" pauses · ")+w()+chalk.gray(" · ")+chalk.cyan("quit")+chalk.gray(" closes CLI")),console.log()))}e.exitOverride(),e.configureOutput({writeErr:()=>{},writeOut:o=>{console.log(o)}});const po=o({input:process.stdin,output:process.stdout,prompt:G(),terminal:!0});f(po);let uo=!1;p(!0),_();const ho=po.prompt.bind(po);po.prompt=o=>{b()||v()||A()||k()||po.setPrompt(G()),ho(o)},po.prompt(),po.on("line",async o=>{if(uo)return;const n=o.trim();if(!n)return po.setPrompt(b()?chalk.magenta("icoa ai4ctf> "):G()),void po.prompt();if("menu"===n||"menu confirm"===n)return D()&&"menu confirm"!==n?(console.log(),console.log(chalk.yellow(" Real exam active. Progress is auto-saved.")),console.log(chalk.gray(" To return to main menu anyway, type ")+chalk.white("menu confirm")),console.log(),void po.prompt()):void u(po);if(b()){uo=!0;const o=await x(n);return uo=!1,"exit"===o&&po.setPrompt(G()),void po.prompt()}if(v()){uo=!0;const o=await C(n);return uo=!1,"exit"!==o&&"solved"!==o||po.setPrompt(G()),void po.prompt()}if(A()){uo=!0;const o=await I(n);return uo=!1,"exit"===o&&po.setPrompt(G()),po.setPrompt(A()?chalk.bold.cyan("icoa ctf4vla> "):G()),void po.prompt()}if(U(n),"exit"===n)return L()?(console.log(),console.log(chalk.yellow(" ⚠ An exam is in progress.")),console.log(chalk.white(" To return to menu without losing progress, type: ")+chalk.bold.cyan("back")),console.log(chalk.white(" To fully close ICOA CLI, type: ")+chalk.bold.cyan("quit")),console.log(chalk.gray(" Your progress is auto-saved either way.")),console.log(),void po.prompt()):(console.log(),console.log(chalk.gray(" ")+chalk.white("exit")+chalk.gray(" returns to the main menu. To fully close ICOA CLI, type ")+chalk.bold.cyan("quit")+chalk.gray(".")),"selection"===mo&&oo(),void po.prompt());if("quit"===n||"q"===n||"quit confirm"===n){const o=L();return o&&"demo-free"!==o.session.examId&&"quit confirm"!==n?(console.log(),console.log(chalk.yellow(" ⚠ A real exam is in progress.")),console.log(chalk.gray(" Your answers are auto-saved on the server, but the exam timer keeps ticking")),console.log(chalk.gray(" on the server side even if you close the CLI.")),console.log(),console.log(chalk.white(" To leave the CLI but keep the exam alive, type: ")+chalk.bold.cyan("back")),console.log(chalk.gray(" (recommended — you can resume with ")+chalk.cyan("exam q 1")+chalk.gray(" after relaunching icoa)")),console.log(),console.log(chalk.white(" To really close ICOA CLI, type: ")+chalk.bold.cyan("quit confirm")),console.log(),void po.prompt()):(o&&"demo-free"===o.session.examId&&(console.log(),console.log(chalk.gray(" Demo paused. Resume with: ")+chalk.white("demo")+chalk.gray(" (fresh) or ")+chalk.white("exam q 1")+chalk.gray(" (continue)."))),Q(),g(),console.log(chalk.gray(" Session saved. Use ")+chalk.white("icoa --resume")+chalk.gray(" to continue.")),R(),void ao(0))}if("back"===n){const o=L(),e=o&&"demo-free"!==o.session.examId,l=o&&"demo-free"===o.session.examId&&(()=>{const e=new Date(o.session.startedAt||0).getTime();return Date.now()-e<18e5})();if(e)console.log(),console.log(chalk.gray(" Exam paused. Your progress is saved.")),console.log(chalk.white(" Resume: exam q 1")+chalk.gray(" · ")+chalk.white("exam review")+chalk.gray(" · ")+chalk.white("exam submit")),console.log();else if(l){const e=Object.keys(o.answers).length,l=o.session.questionCount;console.log(),console.log(chalk.gray(` Demo paused (${e}/${l} answered). Resume with: `)+chalk.white("exam q 1")),console.log(chalk.gray(" Or type ")+chalk.white("demo")+chalk.gray(" to restart.")),console.log()}else{if(o&&"demo-free"===o.session.examId){const{clearExamState:o}=await import("./lib/exam-state.js");o("demo-free")}const e=t();fetch("https://practice.icoa2026.au/api/icoa/demo-stats",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:"post-report-back",lang:e.language||"en",timestamp:(new Date).toISOString()}),signal:AbortSignal.timeout(5e3)}).catch(()=>{}),"selection"===mo?oo():console.log(chalk.gray(" Already at main menu."))}return void po.prompt()}if("help"===n||"?"===n){if(L()){uo=!0;try{await e.parseAsync(["node","icoa","exam","help"])}catch{}return uo=!1,void po.prompt()}return lo(s(),mo),void po.prompt()}if("man"===n||"manual"===n)return lo(s(),mo),void po.prompt();if("more help"===n.toLowerCase()&&L()){uo=!0;try{await e.parseAsync(["node","icoa","exam","more-help"])}catch{}return uo=!1,void po.prompt()}if("continue"===n.toLowerCase())return console.log(),console.log(chalk.green.bold(" ═══ AI4CTF — AI as Your Teammate ═══")),console.log(),console.log(chalk.white(" In AI4CTF, you solve cybersecurity challenges")),console.log(chalk.white(" with AI by your side.")),console.log(),console.log(chalk.white(" In competition, you get AI help at 3 levels:")),console.log(chalk.yellow(" hint a")+chalk.gray(" General guidance (50 uses)")),console.log(chalk.yellow(" hint b")+chalk.gray(" Deep analysis (10 uses)")),console.log(chalk.yellow(" hint c")+chalk.gray(" Critical assist (2 uses)")),console.log(),console.log(chalk.white(" Try it now! Type: ")+chalk.bold.green("ai4ctf")),console.log(chalk.gray(' Chat freely with your AI teammate. Type "exit" when done.')),console.log(),console.log(chalk.gray(" After ai4ctf, try: ")+chalk.bold.red("ctf4ai")+chalk.gray(' — trick the AI into saying "koala"')),console.log(),void po.prompt();if(/^ICOA-[A-Z]{2,3}-\d{1,6}$/i.test(n.trim())){uo=!0;try{await e.parseAsync(["node","icoa","exam","token",n.trim()])}catch{}return uo=!1,void po.prompt()}if(/^[A-Z]{2}[0-9A-HJKMNP-TV-Z]{8}$/i.test(n.trim())){uo=!0;try{await e.parseAsync(["node","icoa","exam","token",n.trim().toUpperCase()])}catch{}return uo=!1,void po.prompt()}const a=n.match(/^exam\s+([A-Z]{2}[0-9A-HJKMNP-TV-Z]{8})$/i);if(a){uo=!0;try{await e.parseAsync(["node","icoa","exam","token",a[1].toUpperCase()])}catch{}return uo=!1,void po.prompt()}const y=n.match(/^exam\s+([A-Z]{2,3})$/i);if(y){uo=!0;try{await e.parseAsync(["node","icoa","exam","list",y[1]])}catch{}return uo=!1,void po.prompt()}if("clear"===n||"cls"===n)return console.clear(),void po.prompt();if(n.startsWith("activate ")){const o=n.slice(9).trim(),e=r(o);return"ok"===e?console.log(chalk.green(" Access granted! Token bound to this device.")):"already_bound"===e?(console.log(),console.log(chalk.red(" Token already activated on a different device.")),console.log(chalk.gray(" Each token binds to the first device that uses it. If you lost the device,")),console.log(chalk.gray(" contact your proctor to have the token re-issued for a new device."))):(console.log(),console.log(chalk.red(" Token not recognized.")),console.log(chalk.gray(" Possible reasons:")),console.log(chalk.white(" • ")+chalk.gray("Typo — access tokens are case-insensitive and look like ")+chalk.cyan("ICOA-XXXX-XXXX-XXXX")+chalk.gray(" (4-char hex groups, e.g. ICOA-385E-27F2-B21B)")),console.log(chalk.white(" • ")+chalk.gray("Expired — ask your proctor or organizer for a fresh token")),console.log(chalk.white(" • ")+chalk.gray("Network — verify connection to ")+chalk.cyan("practice.icoa2026.au")),console.log(chalk.gray(" Still stuck? type ")+chalk.cyan("help")+chalk.gray(" or try ")+chalk.cyan("exam demo")+chalk.gray(" for a free practice round."))),console.log(),void po.prompt()}if("activate"===n)return console.log(chalk.gray(" Usage: ")+chalk.white("activate <token>")),console.log(),void po.prompt();const m=L();if(m){const o=n.toUpperCase().trim(),l=o=>{const e=m.questions.find(e=>e.number===o);return!!e&&("ai4ctf"===e.type||"ctf4ai"===e.type||e.options&&!e.options.A&&!e.options.B)},t=o=>{const e="demo-free"!==m.session.examId,l=e&&o>=39?"ctf4ai":e&&o>=31?"ai4ctf":null;console.log(),console.log(chalk.yellow(` Q${o} is a practical question — letters (A/B/C/D) don't apply here.`)),l?(console.log(chalk.white(" Enter the AI chat for this question: ")+chalk.bold.cyan(l)),console.log(chalk.gray(" Or submit a flag directly: ")+chalk.green(`exam answer ${o} ICOA{your_flag}`))):console.log(chalk.gray(" Submit a flag: ")+chalk.green(`exam answer ${o} ICOA{your_flag}`)),console.log()};if(/^[ABCD]$/.test(o)){const n=m._lastQ||1;if(l(n))return t(n),void po.prompt();uo=!0;try{await e.parseAsync(["node","icoa","exam","answer",String(n),o])}catch{}return uo=!1,void po.prompt()}const a=o.match(/^(\d+)\s+([ABCD])$/);if(a){const o=parseInt(a[1],10);if(l(o))return t(o),void po.prompt();uo=!0;try{await e.parseAsync(["node","icoa","exam","answer",a[1],a[2]])}catch{}return uo=!1,void po.prompt()}}const d=n.split(/\s+/)[0].toLowerCase(),p=/^python3?(\.\d+)?$/.test(d),h="ls"===d||"pwd"===d,w=n.startsWith("!")||d.startsWith("!")||p||h,f=new Set(["nano","vi","vim","emacs","pico","ed","code","cat","less","more","head","tail","ls","pwd","cd","base64","xxd","hexdump","od","openssl","md5sum","sha256sum","grep","find","awk","sed","strings","file","curl","wget","nc","ping","nmap","echo","bash","sh","zsh","pip","pip3","node","npm"]),k=()=>{console.log(chalk.gray(" Shell commands need ")+chalk.bold.cyan("!")+chalk.gray(" prefix. Try: ")+chalk.bold.cyan(`!${n}`)),console.log()};if("embodied"===mo&&!w&&!["demo2","learn","exam","ctf4vla","lang","ref","setup","env","sim"].includes(d))return f.has(d)?(k(),void po.prompt()):(console.log(chalk.gray(" Not available in Embodied AI Security mode.")),console.log(chalk.white(" Try ")+chalk.bold.cyan("demo2")+chalk.gray(" (intro) · ")+chalk.bold.cyan("learn")+chalk.gray(" (curriculum) · ")+chalk.bold.cyan("exam <PD-token>")+chalk.gray(" (Paper D)")),console.log(),void po.prompt());if("selection"===mo&&!w&&!["exam","demo","retry","nations","next","prev","continue","setup","lang","ref","ai4ctf","ctf4ai","mark","unmark","review","submit","env","sim"].includes(d)){if(f.has(d))return k(),void po.prompt();if(console.log(chalk.gray(" Not available in Selection mode.")),m){const o=m._lastQ||1;console.log(chalk.white(` Resume exam: exam q ${o}`)+chalk.gray(" · ")+chalk.white("A/B/C/D")+chalk.gray(" to answer"))}else console.log(chalk.gray(" Try: demo · setup to switch mode"));return console.log(),void po.prompt()}if("organizer"===mo&&!["join","exam","demo","retry","next","prev","logout","setup","lang","ref","ctf","mark","unmark","review","submit"].includes(d))return console.log(chalk.gray(" Not available in Organizer mode. Switch via: setup")),console.log(),void po.prompt();if(!("olympiad"!==mo||s()&&c()||i(d)))return console.log(chalk.yellow(" Restricted mode. ")+chalk.gray("Enter your access token:")),console.log(chalk.white(" activate <token>")),console.log(),console.log(chalk.gray(" Free commands: ")+chalk.white("ref [topic]")+chalk.gray(", ")+chalk.white("help")+chalk.gray(", ")+chalk.white("exit")),console.log(),void po.prompt();if(!["join","activate","challenges","ch","open","submit","flag","scoreboard","sb","status","time","ref","shell","files","connect","note","notes","logbook","log","man","manual","lang","setup","env","ai4ctf","model","ctf","exam","demo","retry","nations","next","prev","continue","logout","ctf4ai","mark","unmark","review","submit","learn","demo2","ctf4vla","theme","sim"].includes(d)){if(H.has(d))return console.log(chalk.yellow(` "${d}" is a shell command — the ICOA prompt isn't a shell.`)),console.log(chalk.gray(" In real-exam mode, prefix with ! to run inside the sandbox: ")+chalk.cyan(`!${n}`)),console.log(),void po.prompt();if(/(?:^|\s)(?:\/(?!home\/|Users\/|tmp\/)|\.\.\/|~\/)/.test(n)&&!n.startsWith("cd ")){const o=/(?:^|\s)\/(?!home\/\w+\/icoa-workspace|Users\/\w+\/icoa-workspace|tmp\/)/.test(n),e=/\.\./.test(n);if(o||e)return console.log(chalk.red(" Blocked: access outside workspace is not allowed.")),console.log(chalk.gray(` Workspace: ${J}`)),console.log(),void po.prompt()}let o=n.startsWith("!")?n.slice(1).trim():n;if("darwin"===process.platform){const e="/opt/homebrew/opt/python@3.12/bin/python3.12";o=o.replace(/^python3?\s/,`${e} `).replace(/^(python3|python)$/,e)}else if("win32"===process.platform){const e=(()=>{try{return l("py -3 --version",{stdio:["ignore","ignore","ignore"],timeout:1500}),"py -3"}catch{}return"python"})();o=o.replace(/^python3?(\.\d+)?\s/,`${e} `).replace(/^python3?(\.\d+)?$/,e)}else{const e=(()=>{try{return l("which python3.12",{stdio:"ignore"}),"python3.12"}catch{return"python3"}})();o=o.replace(/^python\s/,`${e} `).replace(/^python$/,e)}const e=B(),t=N(n);M({cwd:e,input:n,riskFlags:t}),t.length>0&&D()&&(console.log(chalk.yellow(` ⚠ Action flagged: ${t.join(", ")} — logged.`)),console.log()),/^(\S*python3?(\.\d+)?)\s*$/.test(o)&&(o=`PYTHONSTARTUP="${function(){const o=z(V(),".icoa");W(o)||X(o,{recursive:!0});const e=z(o,"python-startup.py");return W(e)||Y(e,"# ICOA exam interactive startup — auto-loaded by PYTHONSTARTUP\nimport base64, struct, hashlib, re, json, os, sys, binascii\ntry: import requests\nexcept ImportError: pass\ntry: from Crypto.Cipher import AES\nexcept ImportError: pass\ntry: from Crypto.Util.Padding import pad, unpad\nexcept ImportError: pass\ntry: from pwn import xor, p32, u32, p64, u64\nexcept ImportError: pass\ntry: import bs4\nexcept ImportError: pass\ntry: import numpy as np\nexcept ImportError: pass\n"),e}()}" ${o}`,console.log(),console.log(chalk.cyan(" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")),console.log(chalk.bold.white(" Python ready — ICOA exam toolkit pre-loaded")),console.log(chalk.cyan(" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")),console.log(),console.log(chalk.white(" Already imported: ")+chalk.gray("base64, struct, hashlib, re, json, binascii")),console.log(chalk.white(" Also available: ")+chalk.gray("requests, bs4, numpy, AES, pad/unpad, xor, p32/u32/p64/u64")),console.log(),console.log(chalk.yellow(" Quick examples:")),console.log(chalk.gray(' base64.b64decode("aGVsbG8=") ')+chalk.gray("# decode base64")),console.log(chalk.gray(' bytes.fromhex("48656c6c6f") ')+chalk.gray("# hex → bytes")),console.log(chalk.gray(' "ICOA{x}".encode() ')+chalk.gray("# str → bytes")),console.log(chalk.gray(" [chr(c) for c in [73,67,79,65]] ")+chalk.gray("# ASCII codes")),console.log(chalk.gray(' xor(bytes.fromhex("0a2b"), b"IC") ')+chalk.gray("# pwntools XOR")),console.log(),console.log(chalk.gray(" Exit: ")+chalk.white("exit()")+chalk.gray(" or Ctrl-D")),console.log(chalk.cyan(" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")),console.log()),uo=!0;try{F()&&await j()?await q(o,po):await eo(o,po,e)}catch{console.log(chalk.yellow(` Command failed: ${d}`))}return uo=!1,console.log(),void po.prompt()}uo=!0;const T=n.trim(),S=T.toLowerCase();let $,O=null,E="";if(m)if("submit"===S)O="final";else if(S.startsWith("submit ")){let o=T.slice(7).trim();/^submit\s+/i.test(o)&&(o=o.replace(/^submit\s+/i,"").trim()),o=o.replace(/^["'`]+|["'`]+$/g,"").trim(),o&&/^ICOA\{[^}]*\}?$/i.test(o)&&(O="flag",E=o)}else/^ICOA\{[^}]+\}$/i.test(T)&&(O="flag",E=T);$="final"===O?["exam","submit"]:"flag"===O?["exam","answer",String(m?._lastQ||1),E]:function(o){const e=o.split(/\s+/),l=e[0].toLowerCase(),n=e.slice(1),t={demo:["exam","demo"],retry:["exam","demo-retry"],nations:["exam","nations"],next:["exam","next"],prev:["exam","prev"],mark:["exam","mark",...n],unmark:["exam","unmark",...n],review:["exam","review"],logout:["ctf","logout"],join:["ctf","join",...n],activate:["ctf","activate",...n],challenges:["ctf","challenges"],ch:["ctf","challenges"],open:["ctf","open",...n],submit:["ctf","submit",...n],flag:["ctf","submit",...n],scoreboard:["ctf","scoreboard",...n],sb:["ctf","scoreboard",...n],status:["ctf","status"],time:["ctf","time"]};return t[l]?t[l]:["ref","shell","files","connect","note","notes","logbook","log","man","manual","lang","setup","env","ai4ctf","model","ctf","exam","ctf4ai"].includes(l)?[l,...n]:e}(n);const P="ctf"===$[0]&&"join"===$[1];P&&po.pause(),process.exit=()=>{throw new Error(K)};try{await e.parseAsync(["node","icoa",...$])}catch(o){const e=o instanceof Error?o.message:String(o);if(e===K);else if(e.includes("commander.unknownCommand")){const{distance:o}=await import("fastest-levenshtein"),e=["ctf","ref","shell","files","connect","note","notes","logbook","log","manual","lang","setup","env","ai4ctf","exam","ctf4ai","theme","clear","cls","quit","exit","back","menu","help","continue","activate","demo","challenges","status","scoreboard","join","logout"],l=d.split(/\s+/)[0]||d;let n={word:"",dist:1/0};for(const t of e){const e=o(l.toLowerCase(),t);e<n.dist&&(n={word:t,dist:e})}console.log(chalk.yellow(` Unknown command: ${d}.`)),n.dist>0&&n.dist<=2&&console.log(chalk.gray(" Did you mean: ")+chalk.bold.cyan(n.word)+chalk.gray("?")),console.log(chalk.gray(" Type ")+chalk.cyan("help")+chalk.gray(" for the full command list."))}else e.includes("commander.")||(e.includes("fetch failed")||e.includes("ECONNREFUSED")||e.includes("ETIMEDOUT"))&&console.log(chalk.yellow(" Network error. Check your connection."))}finally{process.exit=ao,uo=!1,P&&po.resume()}b()?po.setPrompt(chalk.magenta("icoa ai4ctf> ")):v()?po.setPrompt(chalk.red("icoa ctf4ai> ")):A()&&po.setPrompt(chalk.bold.cyan("icoa ctf4vla> ")),console.log(),po.prompt()}),po.on("SIGINT",()=>{if(console.log(),b()||v())console.log(chalk.yellow(" Ctrl+C did not close ICOA CLI — you are still in the AI chat.")),console.log(chalk.white(" Type ")+chalk.bold.cyan("exit")+chalk.white(" to leave the chat and return to the menu."));else if(L()){const o="demo-free"!==L().session.examId;console.log(chalk.yellow(" Ctrl+C did NOT close ICOA CLI.")),console.log(chalk.gray(` Your ${o?"exam":"demo"} is paused and every answer is auto-saved.`)),console.log(),console.log(chalk.white(" Resume: ")+chalk.cyan("exam q 1")+chalk.gray(" · Back to menu: ")+chalk.cyan("back")+chalk.gray(" · Close CLI: ")+chalk.cyan(o?"quit confirm":"quit"))}else console.log(chalk.yellow(" Ctrl+C did not close ICOA CLI — you are still at the ")+chalk.cyan("icoa>")+chalk.yellow(" prompt.")),console.log(chalk.gray(" Keep typing — ")+chalk.cyan("help")+chalk.gray(" lists commands. (Only ")+chalk.cyan("quit")+chalk.gray(" or Ctrl+D actually close the CLI.)"));console.log(),po.prompt()}),po.on("close",()=>{Q(),g(),R(),ao(0)})}function eo(o,l,n){return new Promise(t=>{const a=process.stdin,s=!!a.isTTY&&!!a.isRaw;if(l.pause(),a.isTTY&&"function"==typeof a.setRawMode)try{a.setRawMode(!1)}catch{}const r=e(o,{shell:!0,stdio:"inherit",cwd:n||process.cwd()}),i=()=>{if(a.isTTY&&"function"==typeof a.setRawMode&&s)try{a.setRawMode(!0)}catch{}l.resume(),t()};r.on("close",i),r.on("error",i)})}function lo(o,e="olympiad"){return console.log(),"selection"===e||"organizer"===e?(console.log(chalk.bold.white(" Exam")),console.log(chalk.white(" join <url> ")+chalk.gray("Connect to exam server")),console.log(chalk.white(" exam list ")+chalk.gray("Available exams")),console.log(chalk.white(" exam start <id> ")+chalk.gray("Begin an exam")),console.log(chalk.white(" exam q [n] ")+chalk.gray("View questions")),console.log(chalk.white(" exam answer <n> <X> ")+chalk.gray("Answer question")),console.log(chalk.white(" exam review ")+chalk.gray("Review all answers")),console.log(chalk.white(" exam submit ")+chalk.gray("Submit for grading")),console.log(chalk.white(" exam result ")+chalk.gray("View your score")),console.log(),console.log(chalk.bold.white(" System")),console.log(chalk.white(" ref [topic] ")+chalk.gray("Quick reference")),console.log(chalk.white(" setup ")+chalk.gray("Settings / switch mode")),console.log(chalk.white(" lang [code] ")+chalk.gray("Switch language")),console.log(chalk.white(" clear ")+chalk.gray("Clear screen")),console.log(chalk.white(" exit ")+chalk.gray("Quit")),void console.log()):o?(console.log(chalk.cyan(" ═══════════════════════════════════════════════")),console.log(chalk.bold.white(" How it works")),console.log(),console.log(chalk.gray(" 1. Browse ")+chalk.white("challenges")+chalk.gray(" and pick one")),console.log(chalk.gray(" 2. ")+chalk.white("open <id>")+chalk.gray(" to read the challenge")),console.log(chalk.gray(" 3. Use ")+chalk.white("ai4ctf")+chalk.gray(" to chat with AI when stuck")),console.log(chalk.gray(" 4. ")+chalk.white("submit <id> icoa{flag}")+chalk.gray(" to score points")),console.log(chalk.gray(" 5. Check ")+chalk.white("scoreboard")+chalk.gray(" to track your rank")),console.log(chalk.cyan(" ═══════════════════════════════════════════════")),console.log(),console.log(chalk.bold.white(" Competition")),console.log(chalk.white(" join <url> ")+chalk.gray("Connect to CTFd")),console.log(chalk.white(" challenges (ch) ")+chalk.gray("List challenges by category")),console.log(chalk.white(" open <id> ")+chalk.gray("Read challenge + get next steps")),console.log(chalk.white(" submit <id> <flag> ")+chalk.gray("Submit a flag")),console.log(chalk.white(" scoreboard (sb) ")+chalk.gray("Live rankings")),console.log(chalk.white(" status ")+chalk.gray("Your score, budget & timer")),console.log(chalk.white(" time ")+chalk.gray("Countdown timer")),console.log(),console.log(chalk.bold.white(" AI Teammate")+chalk.gray(" — 3 levels, use wisely")),console.log(chalk.white(' hint "question" ')+chalk.gray("Level A — General guidance (50 uses)")),console.log(chalk.white(' hint-b "question" ')+chalk.gray("Level B — Deep analysis (10 uses)")),console.log(chalk.white(' hint-c "question" ')+chalk.gray("Level C — Critical assist (2 uses)")),console.log(chalk.white(" hint budget ")+chalk.gray("Check remaining uses")),console.log(chalk.white(" ai4ctf ")+chalk.gray("Free-chat with AI (no limit)")),console.log(),console.log(chalk.bold.white(" Tools")),console.log(chalk.white(" ref [topic] ")+chalk.gray("Quick reference (linux, web, crypto...)")),console.log(chalk.white(" shell ")+chalk.gray("Docker sandbox")),console.log(chalk.white(" files <id> ")+chalk.gray("Download challenge files")),console.log(chalk.white(" connect <id> ")+chalk.gray("Connect to remote target")),console.log(chalk.white(" note [text] ")+chalk.gray("Add a logbook entry (in-exam: tags current Q)")),console.log(chalk.white(" logbook [n] ")+chalk.gray("View your logbook (in-exam: filter by Q)")),console.log(chalk.white(" log ")+chalk.gray("Session history")),console.log(chalk.white(" man / manual ")+chalk.gray("Show this command list (works in exam too, does not burn elimination)")),console.log(),console.log(chalk.bold.white(" System")),console.log(chalk.white(" setup ")+chalk.gray("Configure settings")),console.log(chalk.white(" lang [code] ")+chalk.gray("Switch language (15 supported)")),console.log(chalk.white(" logout ")+chalk.gray("Disconnect")),console.log(chalk.white(" clear ")+chalk.gray("Clear screen")),console.log(chalk.white(" exit ")+chalk.gray("Quit (session saved)")),void console.log()):(console.log(chalk.bold.yellow(" Restricted Mode — activate with a token to unlock all commands")),console.log(),console.log(chalk.white(" activate <token> ")+chalk.gray("Unlock full access")),console.log(chalk.white(" ref [topic] ")+chalk.gray("Quick reference")),console.log(chalk.white(" exit ")+chalk.gray("Quit")),void console.log())}
|
|
1
|
+
import{createInterface as o}from"node:readline";import{spawn as e,execSync as l}from"node:child_process";import chalk from"chalk";import{isConnected as n,getConfig as t,saveConfig as a}from"./lib/config.js";import{isActivated as s,activateToken as r,isFreeCommand as i,isDeviceMatch as c,recordExit as g,recordResume as y,isFirstRunOrUpgrade as m,markVersionSeen as d}from"./lib/access.js";import{setReplMode as p}from"./lib/ui.js";import{returnToMainMenu as u,printMenuHint as h,menuHintInline as w}from"./lib/menu-nav.js";import{setMainRl as f}from"./lib/main-rl.js";import{isChatActive as b,handleChatMessage as x}from"./commands/ai4ctf.js";import{isCtf4aiActive as v,handleCtf4aiMessage as C}from"./commands/ctf4ai-demo.js";import{isCtf4VlaActive as A,handleCtf4VlaMessage as I}from"./commands/ctf4vla.js";import{isDemo2Active as k}from"./commands/demo2.js";import{loadDemo2Progress as T}from"./lib/demo2-progress.js";import{hasAnySimHistory as E,lastArmAt as S,lastBundledAt as $,lastSimAt as O}from"./lib/sim-cooldown.js";import{getExamState as D,getRealExamState as L,getDemoState as R}from"./lib/exam-state.js";import{resetTerminalTheme as P}from"./lib/theme.js";import{ensureSandbox as j,runInSandbox as N,isDockerAvailable as q}from"./lib/sandbox.js";import{checkShellRisk as F,logShellAudit as M,getActiveCwd as B}from"./lib/exam-sandbox.js";import{logCommand as _}from"./lib/logger.js";import{startLogSync as U,stopLogSync as Q}from"./lib/log-sync.js";import{existsSync as W,mkdirSync as X,writeFileSync as Y}from"node:fs";import{join as z}from"node:path";import{homedir as V}from"node:os";function G(){return L()?chalk.cyan("icoa exam> "):R()?chalk.yellow("icoa demo> "):chalk.green("icoa> ")}const J=z(V(),"icoa-workspace"),H=new Set(["sudo","su","doas","pkexec","brew","apt","apt-get","yum","choco","npm","npx","pip","pip3","shutdown","reboot","halt","mkfs","fdisk","dd","iptables","ufw"]),K="__REPL_NO_EXIT__",Z="2.5.1";function oo(){console.log(),console.log(` ${chalk.cyan.bold("🎯 ICOA 2026 · National Selection")}`),console.log(),console.log(" "+chalk.bold.cyan("learn ")+chalk.white("ACxxxxxxxx")+chalk.gray(" → ai4ctf-12 (AI as your CTF teammate)")),console.log(" "+chalk.bold.cyan("learn ")+chalk.white("CAxxxxxxxx")+chalk.gray(" → ctf4ai-12 (red-team software AI)")),console.log(" "+chalk.bold.cyan("learn ")+chalk.white("EAxxxxxxxx")+chalk.gray(" → ctf4eai-12 (red-team embodied AI)")),console.log(" "+chalk.bold.cyan("exam ")+chalk.white("<CC>xxxxxxxx")+chalk.gray(" → Paper A/B/C/D/E (country exam token)")),console.log(" "+chalk.bold.cyan("demo")+chalk.gray(" → free practice")),console.log(" "+chalk.bold.cyan("lang")+chalk.white(" es")+chalk.gray(" → switch language")),console.log(" "+chalk.gray("help · menu · quit")),console.log()}export async function startRepl(e,R){process.env.ICOA_INSIDE_REPL="1";const no=t(),to=n(),ao=process.exit.bind(process),so=s();if(no.demoCleanedForVersion!==Z){try{const{existsSync:o,unlinkSync:e}=await import("node:fs"),{join:l}=await import("node:path"),{getIcoaDir:n}=await import("./lib/config.js"),t=l(n(),"demo-state.json");o(t)&&e(t)}catch{}a({demoCleanedForVersion:Z})}const{select:ro,confirm:io}=await import("@inquirer/prompts"),co=no.mode||"",go=""===co?"instant":["instant","learn","selection","olympiad"].includes(co)?co:"selection",yo=[{name:` ${chalk.bold("⚡ Try ICOA in 30 seconds")} ${chalk.gray("—")} ${chalk.gray("watch · no token needed")}`,value:"instant"},{name:` ${chalk.bold("🧠 Learn")} ${chalk.gray("—")} ${chalk.gray("AC / CA / EA token · skill up")}`,value:"learn"},{name:` ${chalk.bold("🎯 National Selection")} ${chalk.gray("—")} ${chalk.gray("country exam token · aim for finals")}`,value:"selection"},{name:` ${chalk.bold("🏆 International Olympiad")} ${chalk.gray("—")} ${chalk.gray("finals stage · Sydney 2026 · full sandbox")}`,value:"olympiad"},{name:` ${chalk.gray("ℹ️ About ICOA")} ${chalk.gray("·")} ${chalk.gray("what this is · how to join")}`,value:"about"}];console.log(chalk.gray(" Type ")+chalk.bold.cyan("menu")+chalk.gray(" from anywhere to return here.")),console.log();let mo="";for(;!mo;){let o;try{o=await ro({message:"Mode",choices:yo,default:go||"instant"})}catch(o){throw o instanceof Error&&"ExitPromptError"===o.name&&(console.log(),console.log(chalk.gray(" Goodbye.")),process.exit(0)),o}"about"!==o?mo=o:(console.clear(),console.log(),console.log(chalk.cyan(" ═══════════════════════════════════════════════════")),console.log(chalk.bold.yellow(" ICOA")+chalk.white(" — AI-Native CLI OS for Cyber & AI Security")),console.log(chalk.gray(" Olympiad & Competition · K-12 to University")),console.log(chalk.cyan(" ───────────────────────────────────────────────────")),console.log(),console.log(chalk.bold.white(" What Makes ICOA Different")),console.log(chalk.gray(" · AI-native AI teammate, AI adversary, AI translation")),console.log(chalk.gray(" · CLI OS Complete competition environment in terminal")),console.log(chalk.gray(" · 110 tools pwntools, z3, gdb, nmap, sleuthkit... pre-configured")),console.log(chalk.gray(" · Global scale 15,000+ concurrent exams · 15 languages")),console.log(),console.log(chalk.bold.white(" Competition Format (2-day standard + 1-day Pioneer)")),console.log(` ${chalk.green.bold("AI4CTF")}${chalk.gray(" [Day 1] AI as teammate — speed track, dynamic decay + top-3 bonus")}`),console.log(` ${chalk.red.bold("CTF4AI")}${chalk.gray(" [Day 2] AI as target — knowledge track, static value per challenge")}`),console.log(` ${chalk.cyan.bold("CTF4EAI")}${chalk.gray(" [Pioneer / Practical Round] Embodied AI — VLA + world models + sim-to-real")}`),console.log(),console.log(chalk.bold.white(" Scoring (in brief)")),console.log(chalk.gray(" · Standard medals: AI4CTF + CTF4AI combined · 1/12 Gold · 2/12 Silver · 3/12 Bronze")),console.log(chalk.gray(" · HMA: top 50% on either day, total below bronze cutoff")),console.log(chalk.gray(" · CTF4EAI: separate 15-finalist pool, G/S/B + 12 Finalist Awards")),console.log(),console.log(chalk.white(" Sydney, Australia")+chalk.gray(" · Jun 27 - Jul 2, 2026")),console.log(),console.log(chalk.bold.white(" Organized by")+chalk.gray(" ASRA (Australia) · ICO Foundation Inc")),console.log(chalk.bold.white(" Contact ")+chalk.cyan(" australia@icoa2026.au · accreditation@icoa2026.au")),console.log(chalk.bold.white(" Website ")+chalk.cyan.underline(" https://icoa2026.au")),console.log(chalk.cyan(" ═══════════════════════════════════════════════════")),console.log(),console.log(chalk.gray(" Press ")+chalk.yellow("Enter")+chalk.gray(" to return...")),await new Promise(o=>{const e=l=>{process.stdin.removeListener("data",e),process.stdin.isTTY&&process.stdin.setRawMode&&process.stdin.setRawMode(!1),process.stdin.pause(),o()};process.stdin.isTTY&&process.stdin.setRawMode&&process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.once("data",e)}),console.clear())}if("olympiad"===mo&&"olympiad"!==go){console.log(),console.log(chalk.yellow(" This mode will download ~500MB of CTF tools and AI models."));let o=!0;try{o=await io({message:"Continue?",default:!0})}catch(e){if(!(e instanceof Error&&"ExitPromptError"===e.name))throw e;o=!1}o||(mo="selection",console.log(chalk.gray(" Switched to National Selection mode.")))}if(mo!==go&&a({mode:mo}),console.log(),"olympiad"===mo&&m(Z)){d(Z),console.log(chalk.gray(" Checking competition environment..."));const{execSync:o}=await import("node:child_process"),e=[{name:"pwntools",cmd:'python3 -c "import pwn"'},{name:"z3-solver",cmd:'python3 -c "import z3"'},{name:"numpy",cmd:'python3 -c "import numpy"'},{name:"requests",cmd:'python3 -c "import requests"'}];let l=0;for(const n of e)try{o(n.cmd,{stdio:"ignore"})}catch{l++}if(l>0){console.log(chalk.yellow(` ${l} core libraries missing.`));try{const{confirm:o}=await import("@inquirer/prompts");if(await o({message:" Install competition Python libraries now?",default:!0,theme:{prefix:"",style:{message:o=>chalk.green(o),defaultAnswer:o=>chalk.green(o)}}})){console.log();const{execSync:o}=await import("node:child_process");o("icoa env setup",{stdio:"inherit"})}}catch{console.log(chalk.gray(" Run ")+chalk.white("env setup")+chalk.gray(" later to install."))}console.log()}else console.log(chalk.green(" All core libraries ready.")),console.log()}if(R){const o=y();if(o){const e=Math.floor(o.awaySeconds/60),l=o.awaySeconds%60;console.log(chalk.yellow(` Session resumed. Away: ${e}m ${l}s | Total exits: ${o.exitCount}`)),console.log()}}if("selection"===mo)console.log(chalk.gray(" ─── exam hall · 90 min per paper once started · no pause ───")),console.log(),oo();else if("embodied"===mo)!function(){console.log(chalk.cyan.bold(" [🤖 ctf4eai — Embodied AI Security]")),console.log(),console.log(chalk.bold.cyan(" CTF4VLA")+chalk.gray(" — ICOA 2026's ")+chalk.bold.white("Pioneer Round")),console.log(chalk.gray(" an independent parallel track alongside the scored main events: ")+chalk.green("AI4CTF")+chalk.gray(" (Day 1) and ")+chalk.red("CTF4AI")+chalk.gray(" (Day 2)")),console.log(),console.log(chalk.white(" Embodied AI is at its ImageNet moment.")),console.log(chalk.gray(" VLA models, MuJoCo physics, real robot arms.")),console.log(chalk.gray(" Real models, real physics, real attack surfaces.")),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const o=$()>0||O()>0,e=S()>0,l=E(),n=T(),t=null!==n&&"number"==typeof n.completedAt&&n.nextCardIndex>=n.totalCards,a=null!==n&&!t;if(t)console.log(chalk.bold.yellow(" Continue: ")+chalk.bold.cyan("④ learn LEARNDEMO01")+chalk.gray(" (demo2 ✓ · sim")+(e?chalk.gray(" ✓ · sim arm ✓)"):chalk.gray(")")));else if(a){const o=`${n.nextCardIndex} / ${n.totalCards}`;console.log(chalk.bold.yellow(" Continue: ")+chalk.bold.cyan("③ demo2")+chalk.gray(" (last left at card ")+chalk.bold.yellow(o)+chalk.gray(")"))}else o&&e?console.log(chalk.bold.yellow(" Next up: ")+chalk.bold.cyan("③ demo2")+chalk.gray(" → ")+chalk.bold.cyan("④ learn LEARNDEMO01")+chalk.gray(" (sim ✓ · sim arm ✓)")):l?console.log(chalk.bold.yellow(" Next up: ")+(e?chalk.bold.cyan("① sim")+chalk.gray(" → ")+chalk.bold.cyan("③ demo2"):chalk.bold.cyan("② sim arm")+chalk.gray(" → ")+chalk.bold.cyan("③ demo2"))+chalk.gray(" → ")+chalk.bold.cyan("④ learn LEARNDEMO01")):console.log(chalk.bold.yellow(" First time? Try in order: ")+chalk.bold.cyan("① sim")+chalk.gray(" → ")+chalk.bold.cyan("② sim arm")+chalk.gray(" → ")+chalk.bold.cyan("③ demo2")+chalk.gray(" → ")+chalk.bold.cyan("④ learn LEARNDEMO01"));console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.bold.green(" sim")+chalk.gray(" ")+chalk.bold.cyan("①")+chalk.gray(" ")+chalk.bold.yellow("★ INSTANT")+chalk.gray(" — 8-robot group dance, no wait, no setup")),console.log(chalk.bold.green(" sim arm")+chalk.gray(" ")+chalk.bold.cyan("②")+chalk.gray(" interactive 6-DOF arm sandbox — type joint values, watch")),console.log(chalk.bold.green(" demo2")+chalk.gray(" ")+chalk.bold.cyan("③")+chalk.gray(" ~8 min interactive intro + live MuJoCo sim")),console.log(chalk.bold.green(" learn LEARNDEMO01")+chalk.gray(" ")+chalk.bold.cyan("④")+chalk.gray(" free 11-card intro (paste this command as-is)")),console.log(chalk.bold.green(" learn ")+chalk.bold.yellow("EA")+chalk.gray(" + 8 chars full 96 / 360 curriculum (team-issued)")),console.log(chalk.bold.green(" exam ")+chalk.bold.yellow("PDxxxxxxxx")+chalk.gray(" Paper D (CTF4VLA) — national selection contestants")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(),console.log(chalk.gray(" Three open tiers: ")+chalk.green("free 11")+chalk.gray(" · ")+chalk.yellow("registered 100")+chalk.gray(" · ")+chalk.bold.magenta("national team 480")+chalk.gray(" (PhD-ready)")),console.log(chalk.gray(" Country team contact: ")+chalk.cyan("australia@icoa2026.au")),console.log(),h(),console.log()}();else{if("instant"===mo){console.log(chalk.cyan.bold(" [⚡ Try ICOA in 30 seconds — no token needed]")),console.log(),console.log(chalk.gray(" Pick what you want to see first:")),console.log();const o=[{name:` ${chalk.bold.green("★ demo ")} ${chalk.gray("Paper A/B/C taste — MCQ + ai4ctf + ctf4ai ")}`+chalk.bold.cyan("🎯 selection-style"),value:"demo"},{name:` ${chalk.bold.green("★ demo2 ")} ${chalk.gray("15-sec countdown render · bundled & offline ")}`+chalk.bold.yellow("⚡ visual wow"),value:"demo2"},{name:` ${chalk.gray("↩ back to main menu")}`,value:"menu"}];let l="menu";try{l=await ro({message:"Pick",choices:o,default:"demo"})}catch{}if("menu"!==l){console.log();const o="demo2"===l?["sim"]:["exam","demo"];try{await e.parseAsync(["node","icoa",...o])}catch{}}return a({mode:"selection"}),void u()}if("learn"===mo){console.log(chalk.cyan.bold(" [🧠 Learn]")),console.log(),console.log(chalk.gray(" Three tracks · same difficulty curve.")),console.log(),console.log(chalk.white(" Free 12-card demos — paste the full command:")),console.log(),console.log(chalk.gray(" 🤝 ai4ctf AI as your teammate ")+chalk.bold.yellow("learn AI4CTFDEMO01")),console.log(chalk.gray(" 🔓 ctf4ai AI as your target ")+chalk.bold.yellow("learn CTF4AIDEMO01")),console.log(chalk.gray(" 🤖 ctf4eai Embodied AI Security ")+chalk.bold.yellow("learn LEARNDEMO01")),console.log(),console.log(chalk.gray(" Got a cohort token? Type ")+chalk.bold.yellow("learn <TOKEN>")+chalk.gray(" (e.g. ")+chalk.bold.yellow("learn EAABCD1234")+chalk.gray(").")),console.log();try{const{input:o}=await import("@inquirer/prompts"),{resolveLearnInput:l}=await import("./lib/learn-input.js");console.log(chalk.gray(" Enter a token (or just press Enter to go back).")),console.log();const n=l(await o({message:chalk.bold.cyan("icoa learn>"),theme:{prefix:"",style:{message:o=>o}}}));if("token"===n.kind){console.log(),console.log(chalk.gray(" Opening ")+chalk.bold.cyan("learn")+chalk.gray(`: ${n.token}`)),console.log(),a({mode:"selection"}),process.env.ICOA_LEARN_EMBEDDED="1";try{await e.parseAsync(["node","icoa","learn",n.token])}finally{delete process.env.ICOA_LEARN_EMBEDDED}}else"ambiguous"!==n.kind&&"unknown"!==n.kind||(console.log(),console.log(chalk.yellow(" "+n.hint)),console.log(chalk.gray(" Returning to main menu — pick 🧠 Learn again to retry.")),console.log())}catch{}return a({mode:"selection"}),void u()}"organizer"===mo?(console.log(chalk.yellow.bold(" [National/Regional Partner]")),console.log(),console.log(chalk.bold.white(" ██╗ ██████╗ ██████╗ █████╗")),console.log(chalk.bold.white(" ██║██╔════╝██╔═══██╗██╔══██╗")),console.log(chalk.bold.white(" ██║██║ ██║ ██║███████║")),console.log(chalk.bold.white(" ██║██║ ██║ ██║██╔══██║")),console.log(chalk.bold.white(" ██║╚██████╗╚██████╔╝██║ ██║")),console.log(chalk.bold.white(" ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝")),console.log(),console.log(chalk.yellow(" International Cyber Olympiad in AI 2026")),console.log(chalk.bold.magenta(" The World's First AI-Native CLI Operating System")),console.log(chalk.bold.magenta(" for Cybersecurity & AI Security Competition")),console.log(chalk.bold.magenta(" and Olympiad for K-12")),console.log(chalk.gray(" Sydney, Australia · Jun 27 - Jul 2, 2026")),console.log(),console.log(chalk.white(" Vision")),console.log(chalk.gray(" Building a global pipeline for youth cyber & AI")),console.log(chalk.gray(" security talent through education and competition.")),console.log(),console.log(chalk.white(" Capacity")),console.log(chalk.gray(" 15,000+ concurrent online examinations")),console.log(chalk.gray(" National selection, training, and education support")),console.log(),console.log(chalk.white(" Olympic Spirit")),console.log(chalk.gray(" Excellence · Friendship · Respect")),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.white(" New country accreditation & support:")),console.log(chalk.cyan(" australia@icoa2026.au")),console.log(chalk.cyan(" accreditation@icoa2026.au")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(),to?(console.log(chalk.green(` Logged in as ${no.userName}`)),console.log(chalk.white(" exam list")+chalk.gray(" Manage exams")),console.log(chalk.white(" logout")+chalk.gray(" Disconnect"))):console.log(chalk.white(" join <url>")+chalk.gray(" Connect to manage exams")),console.log(),h(),console.log()):(console.log(` ${chalk.cyan.bold("🏆 ICOA 2026 · International Olympiad")}`),console.log(),so&&!c()?(console.log(chalk.red(" Token was activated on a different device.")),console.log(chalk.gray(" Contact organizer for assistance.")),console.log()):to?(console.log(chalk.green.bold(` Welcome back, ${no.userName}!`)),console.log(chalk.gray(` Connected to ${no.ctfdUrl}`)),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.white(" Ready to compete? Start here:")),console.log(),console.log(chalk.bold.cyan(" challenges")+chalk.gray(" Browse challenges by category")),console.log(chalk.white(" status")+chalk.gray(" Your score & hint budget")),console.log(chalk.white(" scoreboard")+chalk.gray(" Live rankings")),console.log(chalk.white(" help")+chalk.gray(" Full command list")),console.log(),console.log(chalk.gray(" Tool environment:")),console.log(chalk.white(" env")+chalk.gray(" See which of the 110 CTF tools are installed")),console.log(chalk.white(" env setup")+chalk.gray(" Install anything missing (~5 min, one-time)")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.gray(" Tip: ")+chalk.cyan("help")+chalk.gray(" · ")+w()+chalk.gray(" · ")+chalk.cyan("Ctrl+C")+chalk.gray(" pauses · ")+chalk.cyan("quit")+chalk.gray(" closes")),console.log()):so?(W(J)||X(J,{recursive:!0}),console.log(chalk.green.bold(" Welcome, competitor!")),console.log(chalk.gray(` Workspace: ${J}`)),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.white(" Get started:")),console.log(),console.log(chalk.white(" Step 1 ")+chalk.bold.cyan("join <url>")+chalk.gray(" Connect to competition server")),console.log(chalk.white(" Step 2 ")+chalk.bold.cyan("challenges")+chalk.gray(" Browse & solve challenges")),console.log(chalk.white(" Step 3 ")+chalk.bold.cyan("ai4ctf")+chalk.gray(" Ask AI when stuck")),console.log(),console.log(chalk.gray(" Before Step 1 — make sure your tools are ready:")),console.log(chalk.white(" env")+chalk.gray(" See which of the 110 CTF tools are installed")),console.log(chalk.white(" env setup")+chalk.gray(" Install anything missing (~5 min, one-time)")),console.log(),console.log(chalk.gray(" Also: ")+chalk.white("help")+chalk.gray(" all commands")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.gray(" Tip: ")+chalk.cyan("Ctrl+C")+chalk.gray(" pauses · ")+w()+chalk.gray(" · ")+chalk.cyan("quit")+chalk.gray(" closes CLI")),console.log()):(console.log(chalk.bold.yellow(" 🏆 You reached the finals tier.")),console.log(chalk.gray(" Full sandbox · 110 CTF tools · Sydney 2026 · this is where finalists compete.")),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.white(" To begin, activate your competition token:")),console.log(),console.log(chalk.bold.cyan(" activate <token>")),console.log(),console.log(chalk.gray(" While waiting, explore:")),console.log(chalk.white(" ref linux")+chalk.gray(" Quick reference for Linux")),console.log(chalk.white(" ref web")+chalk.gray(" Quick reference for Web")),console.log(chalk.white(" env")+chalk.gray(" See which of the 110 CTF tools are installed")),console.log(chalk.white(" env setup")+chalk.gray(" Install anything missing (~5 min, one-time)")),console.log(chalk.white(" help")+chalk.gray(" All available commands")),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.gray(" Tip: ")+chalk.cyan("Ctrl+C")+chalk.gray(" pauses · ")+w()+chalk.gray(" · ")+chalk.cyan("quit")+chalk.gray(" closes CLI")),console.log()))}e.exitOverride(),e.configureOutput({writeErr:()=>{},writeOut:o=>{console.log(o)}});const po=o({input:process.stdin,output:process.stdout,prompt:G(),terminal:!0});f(po);let uo=!1;p(!0),U();const ho=po.prompt.bind(po);po.prompt=o=>{b()||v()||A()||k()||po.setPrompt(G()),ho(o)},po.prompt(),po.on("line",async o=>{if(uo)return;const n=o.trim();if(!n)return po.setPrompt(b()?chalk.magenta("icoa ai4ctf> "):G()),void po.prompt();if("menu"===n||"menu confirm"===n)return L()&&"menu confirm"!==n?(console.log(),console.log(chalk.yellow(" Real exam active. Progress is auto-saved.")),console.log(chalk.gray(" To return to main menu anyway, type ")+chalk.white("menu confirm")),console.log(),void po.prompt()):void u(po);if(b()){uo=!0;const o=await x(n);return uo=!1,"exit"===o&&po.setPrompt(G()),void po.prompt()}if(v()){uo=!0;const o=await C(n);return uo=!1,"exit"!==o&&"solved"!==o||po.setPrompt(G()),void po.prompt()}if(A()){uo=!0;const o=await I(n);return uo=!1,"exit"===o&&po.setPrompt(G()),po.setPrompt(A()?chalk.bold.cyan("icoa ctf4vla> "):G()),void po.prompt()}if(_(n),"exit"===n)return D()?(console.log(),console.log(chalk.yellow(" ⚠ An exam is in progress.")),console.log(chalk.white(" To return to menu without losing progress, type: ")+chalk.bold.cyan("back")),console.log(chalk.white(" To fully close ICOA CLI, type: ")+chalk.bold.cyan("quit")),console.log(chalk.gray(" Your progress is auto-saved either way.")),console.log(),void po.prompt()):(console.log(),console.log(chalk.gray(" ")+chalk.white("exit")+chalk.gray(" returns to the main menu. To fully close ICOA CLI, type ")+chalk.bold.cyan("quit")+chalk.gray(".")),"selection"===mo&&oo(),void po.prompt());if("quit"===n||"q"===n||"quit confirm"===n){const o=D();return o&&"demo-free"!==o.session.examId&&"quit confirm"!==n?(console.log(),console.log(chalk.yellow(" ⚠ A real exam is in progress.")),console.log(chalk.gray(" Your answers are auto-saved on the server, but the exam timer keeps ticking")),console.log(chalk.gray(" on the server side even if you close the CLI.")),console.log(),console.log(chalk.white(" To leave the CLI but keep the exam alive, type: ")+chalk.bold.cyan("back")),console.log(chalk.gray(" (recommended — you can resume with ")+chalk.cyan("exam q 1")+chalk.gray(" after relaunching icoa)")),console.log(),console.log(chalk.white(" To really close ICOA CLI, type: ")+chalk.bold.cyan("quit confirm")),console.log(),void po.prompt()):(o&&"demo-free"===o.session.examId&&(console.log(),console.log(chalk.gray(" Demo paused. Resume with: ")+chalk.white("demo")+chalk.gray(" (fresh) or ")+chalk.white("exam q 1")+chalk.gray(" (continue)."))),Q(),g(),console.log(chalk.gray(" Session saved. Use ")+chalk.white("icoa --resume")+chalk.gray(" to continue.")),P(),void ao(0))}if("back"===n){const o=D(),e=o&&"demo-free"!==o.session.examId,l=o&&"demo-free"===o.session.examId&&(()=>{const e=new Date(o.session.startedAt||0).getTime();return Date.now()-e<18e5})();if(e)console.log(),console.log(chalk.gray(" Exam paused. Your progress is saved.")),console.log(chalk.white(" Resume: exam q 1")+chalk.gray(" · ")+chalk.white("exam review")+chalk.gray(" · ")+chalk.white("exam submit")),console.log();else if(l){const e=Object.keys(o.answers).length,l=o.session.questionCount;console.log(),console.log(chalk.gray(` Demo paused (${e}/${l} answered). Resume with: `)+chalk.white("exam q 1")),console.log(chalk.gray(" Or type ")+chalk.white("demo")+chalk.gray(" to restart.")),console.log()}else{if(o&&"demo-free"===o.session.examId){const{clearExamState:o}=await import("./lib/exam-state.js");o("demo-free")}const e=t();fetch("https://practice.icoa2026.au/api/icoa/demo-stats",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:"post-report-back",lang:e.language||"en",timestamp:(new Date).toISOString()}),signal:AbortSignal.timeout(5e3)}).catch(()=>{}),"selection"===mo?oo():console.log(chalk.gray(" Already at main menu."))}return void po.prompt()}if("help"===n||"?"===n){if(D()){uo=!0;try{await e.parseAsync(["node","icoa","exam","help"])}catch{}return uo=!1,void po.prompt()}return lo(s(),mo),void po.prompt()}if("man"===n||"manual"===n)return lo(s(),mo),void po.prompt();if("more help"===n.toLowerCase()&&D()){uo=!0;try{await e.parseAsync(["node","icoa","exam","more-help"])}catch{}return uo=!1,void po.prompt()}if("continue"===n.toLowerCase())return console.log(),console.log(chalk.green.bold(" ═══ AI4CTF — AI as Your Teammate ═══")),console.log(),console.log(chalk.white(" In AI4CTF, you solve cybersecurity challenges")),console.log(chalk.white(" with AI by your side.")),console.log(),console.log(chalk.white(" In competition, you get AI help at 3 levels:")),console.log(chalk.yellow(" hint a")+chalk.gray(" General guidance (50 uses)")),console.log(chalk.yellow(" hint b")+chalk.gray(" Deep analysis (10 uses)")),console.log(chalk.yellow(" hint c")+chalk.gray(" Critical assist (2 uses)")),console.log(),console.log(chalk.white(" Try it now! Type: ")+chalk.bold.green("ai4ctf")),console.log(chalk.gray(' Chat freely with your AI teammate. Type "exit" when done.')),console.log(),console.log(chalk.gray(" After ai4ctf, try: ")+chalk.bold.red("ctf4ai")+chalk.gray(' — trick the AI into saying "koala"')),console.log(),void po.prompt();if(/^ICOA-[A-Z]{2,3}-\d{1,6}$/i.test(n.trim())){uo=!0;try{await e.parseAsync(["node","icoa","exam","token",n.trim()])}catch{}return uo=!1,void po.prompt()}if(/^[A-Z]{2}[0-9A-HJKMNP-TV-Z]{8}$/i.test(n.trim())){uo=!0;try{await e.parseAsync(["node","icoa","exam","token",n.trim().toUpperCase()])}catch{}return uo=!1,void po.prompt()}const a=n.match(/^exam\s+([A-Z]{2}[0-9A-HJKMNP-TV-Z]{8})$/i);if(a){uo=!0;try{await e.parseAsync(["node","icoa","exam","token",a[1].toUpperCase()])}catch{}return uo=!1,void po.prompt()}const y=n.match(/^exam\s+([A-Z]{2,3})$/i);if(y){uo=!0;try{await e.parseAsync(["node","icoa","exam","list",y[1]])}catch{}return uo=!1,void po.prompt()}if("clear"===n||"cls"===n)return console.clear(),void po.prompt();if(n.startsWith("activate ")){const o=n.slice(9).trim(),e=r(o);return"ok"===e?console.log(chalk.green(" Access granted! Token bound to this device.")):"already_bound"===e?(console.log(),console.log(chalk.red(" Token already activated on a different device.")),console.log(chalk.gray(" Each token binds to the first device that uses it. If you lost the device,")),console.log(chalk.gray(" contact your proctor to have the token re-issued for a new device."))):(console.log(),console.log(chalk.red(" Token not recognized.")),console.log(chalk.gray(" Possible reasons:")),console.log(chalk.white(" • ")+chalk.gray("Typo — access tokens are case-insensitive and look like ")+chalk.cyan("ICOA-XXXX-XXXX-XXXX")+chalk.gray(" (4-char hex groups, e.g. ICOA-385E-27F2-B21B)")),console.log(chalk.white(" • ")+chalk.gray("Expired — ask your proctor or organizer for a fresh token")),console.log(chalk.white(" • ")+chalk.gray("Network — verify connection to ")+chalk.cyan("practice.icoa2026.au")),console.log(chalk.gray(" Still stuck? type ")+chalk.cyan("help")+chalk.gray(" or try ")+chalk.cyan("exam demo")+chalk.gray(" for a free practice round."))),console.log(),void po.prompt()}if("activate"===n)return console.log(chalk.gray(" Usage: ")+chalk.white("activate <token>")),console.log(),void po.prompt();const m=D();if(m){const o=n.toUpperCase().trim(),l=o=>{const e=m.questions.find(e=>e.number===o);return!!e&&("ai4ctf"===e.type||"ctf4ai"===e.type||e.options&&!e.options.A&&!e.options.B)},t=o=>{const e="demo-free"!==m.session.examId,l=e&&o>=39?"ctf4ai":e&&o>=31?"ai4ctf":null;console.log(),console.log(chalk.yellow(` Q${o} is a practical question — letters (A/B/C/D) don't apply here.`)),l?(console.log(chalk.white(" Enter the AI chat for this question: ")+chalk.bold.cyan(l)),console.log(chalk.gray(" Or submit a flag directly: ")+chalk.green(`exam answer ${o} ICOA{your_flag}`))):console.log(chalk.gray(" Submit a flag: ")+chalk.green(`exam answer ${o} ICOA{your_flag}`)),console.log()};if(/^[ABCD]$/.test(o)){const n=m._lastQ||1;if(l(n))return t(n),void po.prompt();uo=!0;try{await e.parseAsync(["node","icoa","exam","answer",String(n),o])}catch{}return uo=!1,void po.prompt()}const a=o.match(/^(\d+)\s+([ABCD])$/);if(a){const o=parseInt(a[1],10);if(l(o))return t(o),void po.prompt();uo=!0;try{await e.parseAsync(["node","icoa","exam","answer",a[1],a[2]])}catch{}return uo=!1,void po.prompt()}}const d=n.split(/\s+/)[0].toLowerCase(),p=/^python3?(\.\d+)?$/.test(d),h="ls"===d||"pwd"===d,w=n.startsWith("!")||d.startsWith("!")||p||h,f=new Set(["nano","vi","vim","emacs","pico","ed","code","cat","less","more","head","tail","ls","pwd","cd","base64","xxd","hexdump","od","openssl","md5sum","sha256sum","grep","find","awk","sed","strings","file","curl","wget","nc","ping","nmap","echo","bash","sh","zsh","pip","pip3","node","npm"]),k=()=>{console.log(chalk.gray(" Shell commands need ")+chalk.bold.cyan("!")+chalk.gray(" prefix. Try: ")+chalk.bold.cyan(`!${n}`)),console.log()};if("embodied"===mo&&!w&&!["demo2","learn","exam","ctf4vla","lang","ref","setup","env","sim"].includes(d))return f.has(d)?(k(),void po.prompt()):(console.log(chalk.gray(" Not available in Embodied AI Security mode.")),console.log(chalk.white(" Try ")+chalk.bold.cyan("demo2")+chalk.gray(" (intro) · ")+chalk.bold.cyan("learn")+chalk.gray(" (curriculum) · ")+chalk.bold.cyan("exam <PD-token>")+chalk.gray(" (Paper D)")),console.log(),void po.prompt());if("selection"===mo&&!w&&!["exam","demo","retry","nations","next","prev","continue","setup","lang","ref","ai4ctf","ctf4ai","mark","unmark","review","submit","env","sim"].includes(d)){if(f.has(d))return k(),void po.prompt();if(console.log(chalk.gray(" Not available in Selection mode.")),m){const o=m._lastQ||1;console.log(chalk.white(` Resume exam: exam q ${o}`)+chalk.gray(" · ")+chalk.white("A/B/C/D")+chalk.gray(" to answer"))}else console.log(chalk.gray(" Try: demo · setup to switch mode"));return console.log(),void po.prompt()}if("organizer"===mo&&!["join","exam","demo","retry","next","prev","logout","setup","lang","ref","ctf","mark","unmark","review","submit"].includes(d))return console.log(chalk.gray(" Not available in Organizer mode. Switch via: setup")),console.log(),void po.prompt();if(!("olympiad"!==mo||s()&&c()||i(d)))return console.log(chalk.yellow(" Restricted mode. ")+chalk.gray("Enter your access token:")),console.log(chalk.white(" activate <token>")),console.log(),console.log(chalk.gray(" Free commands: ")+chalk.white("ref [topic]")+chalk.gray(", ")+chalk.white("help")+chalk.gray(", ")+chalk.white("exit")),console.log(),void po.prompt();if(!["join","activate","challenges","ch","open","submit","flag","scoreboard","sb","status","time","ref","shell","files","connect","note","notes","logbook","log","man","manual","lang","setup","env","ai4ctf","model","ctf","exam","demo","retry","nations","next","prev","continue","logout","ctf4ai","mark","unmark","review","submit","learn","demo2","ctf4vla","theme","sim"].includes(d)){if(H.has(d))return console.log(chalk.yellow(` "${d}" is a shell command — the ICOA prompt isn't a shell.`)),console.log(chalk.gray(" In real-exam mode, prefix with ! to run inside the sandbox: ")+chalk.cyan(`!${n}`)),console.log(),void po.prompt();if(/(?:^|\s)(?:\/(?!home\/|Users\/|tmp\/)|\.\.\/|~\/)/.test(n)&&!n.startsWith("cd ")){const o=/(?:^|\s)\/(?!home\/\w+\/icoa-workspace|Users\/\w+\/icoa-workspace|tmp\/)/.test(n),e=/\.\./.test(n);if(o||e)return console.log(chalk.red(" Blocked: access outside workspace is not allowed.")),console.log(chalk.gray(` Workspace: ${J}`)),console.log(),void po.prompt()}let o=n.startsWith("!")?n.slice(1).trim():n;if("darwin"===process.platform){const e="/opt/homebrew/opt/python@3.12/bin/python3.12";o=o.replace(/^python3?\s/,`${e} `).replace(/^(python3|python)$/,e)}else if("win32"===process.platform){const e=(()=>{try{return l("py -3 --version",{stdio:["ignore","ignore","ignore"],timeout:1500}),"py -3"}catch{}return"python"})();o=o.replace(/^python3?(\.\d+)?\s/,`${e} `).replace(/^python3?(\.\d+)?$/,e)}else{const e=(()=>{try{return l("which python3.12",{stdio:"ignore"}),"python3.12"}catch{return"python3"}})();o=o.replace(/^python\s/,`${e} `).replace(/^python$/,e)}const e=B(),t=F(n);M({cwd:e,input:n,riskFlags:t}),t.length>0&&L()&&(console.log(chalk.yellow(` ⚠ Action flagged: ${t.join(", ")} — logged.`)),console.log()),/^(\S*python3?(\.\d+)?)\s*$/.test(o)&&(o=`PYTHONSTARTUP="${function(){const o=z(V(),".icoa");W(o)||X(o,{recursive:!0});const e=z(o,"python-startup.py");return W(e)||Y(e,"# ICOA exam interactive startup — auto-loaded by PYTHONSTARTUP\nimport base64, struct, hashlib, re, json, os, sys, binascii\ntry: import requests\nexcept ImportError: pass\ntry: from Crypto.Cipher import AES\nexcept ImportError: pass\ntry: from Crypto.Util.Padding import pad, unpad\nexcept ImportError: pass\ntry: from pwn import xor, p32, u32, p64, u64\nexcept ImportError: pass\ntry: import bs4\nexcept ImportError: pass\ntry: import numpy as np\nexcept ImportError: pass\n"),e}()}" ${o}`,console.log(),console.log(chalk.cyan(" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")),console.log(chalk.bold.white(" Python ready — ICOA exam toolkit pre-loaded")),console.log(chalk.cyan(" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")),console.log(),console.log(chalk.white(" Already imported: ")+chalk.gray("base64, struct, hashlib, re, json, binascii")),console.log(chalk.white(" Also available: ")+chalk.gray("requests, bs4, numpy, AES, pad/unpad, xor, p32/u32/p64/u64")),console.log(),console.log(chalk.yellow(" Quick examples:")),console.log(chalk.gray(' base64.b64decode("aGVsbG8=") ')+chalk.gray("# decode base64")),console.log(chalk.gray(' bytes.fromhex("48656c6c6f") ')+chalk.gray("# hex → bytes")),console.log(chalk.gray(' "ICOA{x}".encode() ')+chalk.gray("# str → bytes")),console.log(chalk.gray(" [chr(c) for c in [73,67,79,65]] ")+chalk.gray("# ASCII codes")),console.log(chalk.gray(' xor(bytes.fromhex("0a2b"), b"IC") ')+chalk.gray("# pwntools XOR")),console.log(),console.log(chalk.gray(" Exit: ")+chalk.white("exit()")+chalk.gray(" or Ctrl-D")),console.log(chalk.cyan(" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")),console.log()),uo=!0;try{q()&&await j()?await N(o,po):await eo(o,po,e)}catch{console.log(chalk.yellow(` Command failed: ${d}`))}return uo=!1,console.log(),void po.prompt()}uo=!0;const T=n.trim(),E=T.toLowerCase();let S,$=null,O="";if(m)if("submit"===E)$="final";else if(E.startsWith("submit ")){let o=T.slice(7).trim();/^submit\s+/i.test(o)&&(o=o.replace(/^submit\s+/i,"").trim()),o=o.replace(/^["'`]+|["'`]+$/g,"").trim(),o&&/^ICOA\{[^}]*\}?$/i.test(o)&&($="flag",O=o)}else/^ICOA\{[^}]+\}$/i.test(T)&&($="flag",O=T);S="final"===$?["exam","submit"]:"flag"===$?["exam","answer",String(m?._lastQ||1),O]:function(o){const e=o.split(/\s+/),l=e[0].toLowerCase(),n=e.slice(1),t={demo:["exam","demo"],retry:["exam","demo-retry"],nations:["exam","nations"],next:["exam","next"],prev:["exam","prev"],mark:["exam","mark",...n],unmark:["exam","unmark",...n],review:["exam","review"],logout:["ctf","logout"],join:["ctf","join",...n],activate:["ctf","activate",...n],challenges:["ctf","challenges"],ch:["ctf","challenges"],open:["ctf","open",...n],submit:["ctf","submit",...n],flag:["ctf","submit",...n],scoreboard:["ctf","scoreboard",...n],sb:["ctf","scoreboard",...n],status:["ctf","status"],time:["ctf","time"]};return t[l]?t[l]:["ref","shell","files","connect","note","notes","logbook","log","man","manual","lang","setup","env","ai4ctf","model","ctf","exam","ctf4ai"].includes(l)?[l,...n]:e}(n);const R="ctf"===S[0]&&"join"===S[1];R&&po.pause(),process.exit=()=>{throw new Error(K)};try{await e.parseAsync(["node","icoa",...S])}catch(o){const e=o instanceof Error?o.message:String(o);if(e===K);else if(e.includes("commander.unknownCommand")){const{distance:o}=await import("fastest-levenshtein"),e=["ctf","ref","shell","files","connect","note","notes","logbook","log","manual","lang","setup","env","ai4ctf","exam","ctf4ai","theme","clear","cls","quit","exit","back","menu","help","continue","activate","demo","challenges","status","scoreboard","join","logout"],l=d.split(/\s+/)[0]||d;let n={word:"",dist:1/0};for(const t of e){const e=o(l.toLowerCase(),t);e<n.dist&&(n={word:t,dist:e})}console.log(chalk.yellow(` Unknown command: ${d}.`)),n.dist>0&&n.dist<=2&&console.log(chalk.gray(" Did you mean: ")+chalk.bold.cyan(n.word)+chalk.gray("?")),console.log(chalk.gray(" Type ")+chalk.cyan("help")+chalk.gray(" for the full command list."))}else e.includes("commander.")||(e.includes("fetch failed")||e.includes("ECONNREFUSED")||e.includes("ETIMEDOUT"))&&console.log(chalk.yellow(" Network error. Check your connection."))}finally{process.exit=ao,uo=!1,R&&po.resume()}b()?po.setPrompt(chalk.magenta("icoa ai4ctf> ")):v()?po.setPrompt(chalk.red("icoa ctf4ai> ")):A()&&po.setPrompt(chalk.bold.cyan("icoa ctf4vla> ")),console.log(),po.prompt()}),po.on("SIGINT",()=>{if(console.log(),b()||v())console.log(chalk.yellow(" Ctrl+C did not close ICOA CLI — you are still in the AI chat.")),console.log(chalk.white(" Type ")+chalk.bold.cyan("exit")+chalk.white(" to leave the chat and return to the menu."));else if(D()){const o="demo-free"!==D().session.examId;console.log(chalk.yellow(" Ctrl+C did NOT close ICOA CLI.")),console.log(chalk.gray(` Your ${o?"exam":"demo"} is paused and every answer is auto-saved.`)),console.log(),console.log(chalk.white(" Resume: ")+chalk.cyan("exam q 1")+chalk.gray(" · Back to menu: ")+chalk.cyan("back")+chalk.gray(" · Close CLI: ")+chalk.cyan(o?"quit confirm":"quit"))}else console.log(chalk.yellow(" Ctrl+C did not close ICOA CLI — you are still at the ")+chalk.cyan("icoa>")+chalk.yellow(" prompt.")),console.log(chalk.gray(" Keep typing — ")+chalk.cyan("help")+chalk.gray(" lists commands. (Only ")+chalk.cyan("quit")+chalk.gray(" or Ctrl+D actually close the CLI.)"));console.log(),po.prompt()}),po.on("close",()=>{Q(),g(),P(),ao(0)})}function eo(o,l,n){return new Promise(t=>{const a=process.stdin,s=!!a.isTTY&&!!a.isRaw;if(l.pause(),a.isTTY&&"function"==typeof a.setRawMode)try{a.setRawMode(!1)}catch{}const r=e(o,{shell:!0,stdio:"inherit",cwd:n||process.cwd()}),i=()=>{if(a.isTTY&&"function"==typeof a.setRawMode&&s)try{a.setRawMode(!0)}catch{}l.resume(),t()};r.on("close",i),r.on("error",i)})}function lo(o,e="olympiad"){return console.log(),"selection"===e||"organizer"===e?(console.log(chalk.bold.white(" Exam")),console.log(chalk.white(" join <url> ")+chalk.gray("Connect to exam server")),console.log(chalk.white(" exam list ")+chalk.gray("Available exams")),console.log(chalk.white(" exam start <id> ")+chalk.gray("Begin an exam")),console.log(chalk.white(" exam q [n] ")+chalk.gray("View questions")),console.log(chalk.white(" exam answer <n> <X> ")+chalk.gray("Answer question")),console.log(chalk.white(" exam review ")+chalk.gray("Review all answers")),console.log(chalk.white(" exam submit ")+chalk.gray("Submit for grading")),console.log(chalk.white(" exam result ")+chalk.gray("View your score")),console.log(),console.log(chalk.bold.white(" System")),console.log(chalk.white(" ref [topic] ")+chalk.gray("Quick reference")),console.log(chalk.white(" setup ")+chalk.gray("Settings / switch mode")),console.log(chalk.white(" lang [code] ")+chalk.gray("Switch language")),console.log(chalk.white(" clear ")+chalk.gray("Clear screen")),console.log(chalk.white(" exit ")+chalk.gray("Quit")),void console.log()):o?(console.log(chalk.cyan(" ═══════════════════════════════════════════════")),console.log(chalk.bold.white(" How it works")),console.log(),console.log(chalk.gray(" 1. Browse ")+chalk.white("challenges")+chalk.gray(" and pick one")),console.log(chalk.gray(" 2. ")+chalk.white("open <id>")+chalk.gray(" to read the challenge")),console.log(chalk.gray(" 3. Use ")+chalk.white("ai4ctf")+chalk.gray(" to chat with AI when stuck")),console.log(chalk.gray(" 4. ")+chalk.white("submit <id> icoa{flag}")+chalk.gray(" to score points")),console.log(chalk.gray(" 5. Check ")+chalk.white("scoreboard")+chalk.gray(" to track your rank")),console.log(chalk.cyan(" ═══════════════════════════════════════════════")),console.log(),console.log(chalk.bold.white(" Competition")),console.log(chalk.white(" join <url> ")+chalk.gray("Connect to CTFd")),console.log(chalk.white(" challenges (ch) ")+chalk.gray("List challenges by category")),console.log(chalk.white(" open <id> ")+chalk.gray("Read challenge + get next steps")),console.log(chalk.white(" submit <id> <flag> ")+chalk.gray("Submit a flag")),console.log(chalk.white(" scoreboard (sb) ")+chalk.gray("Live rankings")),console.log(chalk.white(" status ")+chalk.gray("Your score, budget & timer")),console.log(chalk.white(" time ")+chalk.gray("Countdown timer")),console.log(),console.log(chalk.bold.white(" AI Teammate")+chalk.gray(" — 3 levels, use wisely")),console.log(chalk.white(' hint "question" ')+chalk.gray("Level A — General guidance (50 uses)")),console.log(chalk.white(' hint-b "question" ')+chalk.gray("Level B — Deep analysis (10 uses)")),console.log(chalk.white(' hint-c "question" ')+chalk.gray("Level C — Critical assist (2 uses)")),console.log(chalk.white(" hint budget ")+chalk.gray("Check remaining uses")),console.log(chalk.white(" ai4ctf ")+chalk.gray("Free-chat with AI (no limit)")),console.log(),console.log(chalk.bold.white(" Tools")),console.log(chalk.white(" ref [topic] ")+chalk.gray("Quick reference (linux, web, crypto...)")),console.log(chalk.white(" shell ")+chalk.gray("Docker sandbox")),console.log(chalk.white(" files <id> ")+chalk.gray("Download challenge files")),console.log(chalk.white(" connect <id> ")+chalk.gray("Connect to remote target")),console.log(chalk.white(" note [text] ")+chalk.gray("Add a logbook entry (in-exam: tags current Q)")),console.log(chalk.white(" logbook [n] ")+chalk.gray("View your logbook (in-exam: filter by Q)")),console.log(chalk.white(" log ")+chalk.gray("Session history")),console.log(chalk.white(" man / manual ")+chalk.gray("Show this command list (works in exam too, does not burn elimination)")),console.log(),console.log(chalk.bold.white(" System")),console.log(chalk.white(" setup ")+chalk.gray("Configure settings")),console.log(chalk.white(" lang [code] ")+chalk.gray("Switch language (15 supported)")),console.log(chalk.white(" logout ")+chalk.gray("Disconnect")),console.log(chalk.white(" clear ")+chalk.gray("Clear screen")),console.log(chalk.white(" exit ")+chalk.gray("Quit (session saved)")),void console.log()):(console.log(chalk.bold.yellow(" Restricted Mode — activate with a token to unlock all commands")),console.log(),console.log(chalk.white(" activate <token> ")+chalk.gray("Unlock full access")),console.log(chalk.white(" ref [topic] ")+chalk.gray("Quick reference")),console.log(chalk.white(" exit ")+chalk.gray("Quit")),void console.log())}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "icoa-cli",
|
|
3
|
-
"version": "2.19.
|
|
3
|
+
"version": "2.19.233",
|
|
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": {
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"marked": "^15.0.7",
|
|
55
55
|
"marked-terminal": "^7.3.0",
|
|
56
56
|
"ora": "^8.2.0",
|
|
57
|
-
"qrcode-terminal": "^0.12.0"
|
|
57
|
+
"qrcode-terminal": "^0.12.0",
|
|
58
|
+
"string-width": "^4.2.3"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@anthropic-ai/sdk": "^0.98.0",
|