icoa-cli 2.19.395 → 2.19.396

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{existsSync as o,mkdirSync as e,readFileSync as n,readdirSync as l,writeFileSync as t}from"node:fs";import{join as s}from"node:path";import chalk from"chalk";import{makeReplAsker as r}from"../lib/repl-asker.js";import{logCommand as i}from"../lib/logger.js";import{openFile as a}from"../lib/open-file.js";import{serializeIpynb as c}from"../lib/notebook-doc.js";import{loadLearnState as g}from"../lib/learn-state.js";import{buildStarterCells as u,dataFileUrls as d,parseKaggleTaskList as y,parseSubmitResponse as m,validateSubmissionText as b}from"../lib/kaggle.js";function h(){return process.env.ICOA_KAGGLE_SERVER?process.env.ICOA_KAGGLE_SERVER.replace(/\/$/,""):"https://practice.icoa2026.au"}async function p(o){try{const e=await fetch(h()+o,{signal:AbortSignal.timeout(3e4)});return await e.json()}catch{return null}}async function f(o){try{const e=await fetch(o,{signal:AbortSignal.timeout(6e4)});return e.ok?await e.text():null}catch{return null}}function w(){console.log(),console.log(chalk.yellow(" The practice server is not reachable right now.")),console.log(chalk.gray(" Check your connection, or try again later.")),console.log()}function k(o){return s(process.cwd(),"icoa-kaggle",o)}function v(o,e){const n=[];let l="";for(const t of o.split(/\s+/))""!==l&&l.length+1+t.length>e?(n.push(l),l=t):l=""===l?t:`${l} ${t}`;return""!==l&&n.push(l),n}function $(o){if("icoa-holdout"===o?.judge)return console.log(chalk.bold.white(" Submit to ICOA (scored on the server):")),console.log(chalk.gray(" ")+chalk.bold.cyan(`kaggle submit ${o.comp_id}`)),void console.log(chalk.gray(" scores vs the secret test labels → live rank on ")+chalk.cyan("board"));console.log(chalk.bold.white(" Submit on Kaggle (manual, your own account):")),o?.kaggle_url?(console.log(chalk.gray(" 1. open ")+chalk.cyan(o.kaggle_url)),console.log(chalk.gray(" 2. Submit Predictions → upload ")+chalk.white("submission.csv")),console.log(chalk.gray(" 3. read your score, improve the MODEL cell, re-run, repeat."))):(console.log(chalk.gray(" (placeholder dataset — no live Kaggle page yet; when the real")),console.log(chalk.gray(" competition dataset lands, its submission link will appear here.)")))}export async function runKaggle(n){const l=r(),a=async o=>await l.ask(o)??"";try{console.log(),console.log(chalk.bold.cyan(" 📊 KAGGLE PRACTICE")+chalk.gray(" · train locally · submit on Kaggle yourself")),console.log(chalk.gray(" Small real-format datasets, a runnable starter notebook, your own CPU.")),console.log();const l=await p("/api/icoa/kaggle/tasks");if(!l)return w();const r=y(l);if(0===r.length)return console.log(chalk.yellow(" No practice competitions are live yet — check back soon.")),void console.log();let g=n?r.find(o=>o.comp_id===n):void 0;if(n&&!g&&console.log(chalk.yellow(` No competition called "${n}".`)),!g){console.log(chalk.white(" Live practice competitions:")),r.forEach((o,e)=>{console.log(chalk.gray(` ${e+1}. `)+chalk.cyan(o.comp_id)+chalk.gray(` — ${o.title} · ${o.metric}`))});for(let o=0;o<3&&!g;o++){const o=(await a(chalk.cyan(" Pick one (number or id): "))).trim();if(!o)return;const e=Number(o);g=!Number.isNaN(e)&&r[e-1]||r.find(e=>e.comp_id===o.toLowerCase())||void 0,g||console.log(chalk.gray(` Try a number 1-${r.length} or an id like "${r[0].comp_id}".`))}if(!g)return}const m=g.comp_id;if(i(`kaggle: download ${m}`),g.needs_gpu)return void await async function(n,l){const r=k(n);if(console.log(),console.log(chalk.white(` ${l.title}`)+chalk.gray(` · ${l.metric}`)+chalk.bold.magenta(" ⚡ GPU task")),l.description){console.log();for(const o of v(l.description,74))console.log(chalk.white(` ${o}`))}console.log(),console.log(chalk.gray(" Fetching the notebook…")+chalk.gray(" (the audio dataset stays on the GPU box — no download)"));const i=await p(`/api/icoa/kaggle/${n}/starter`),a=i?.data?.starter??i?.starter;if("string"!=typeof a||0===a.length)return w();const c=s(r,"starter.ipynb");let g=!1;try{e(r,{recursive:!0}),o(c)?g=!0:t(c,a),t(s(r,"meta.json"),`${JSON.stringify(l,null,2)}\n`)}catch{return void console.log(chalk.yellow(" Could not write the notebook here — check directory permissions."))}console.log(chalk.green(" ✓ notebook: ")+chalk.white(c)+chalk.gray(g?" (kept your existing one)":" (the real IOAI task inside)")),console.log(chalk.green(" ✓ dataset: ")+chalk.white("already on the GPU box")+chalk.gray(" — no download needed")),console.log(),console.log(chalk.bold.white(" This is a GPU task — 3 steps:")),console.log(chalk.gray(" 1. ")+chalk.bold.cyan(`icoa ipynb ${c}`)),console.log(chalk.gray(" 2. ")+chalk.bold.cyan("gpu key <your-key>")+chalk.gray(" then ")+chalk.bold.cyan("gpu on")+chalk.gray(" (connects you to the box where the audio data lives)")),console.log(chalk.gray(" 3. ")+chalk.bold.cyan("run all")+chalk.gray(" → the notebook trains on the GPU.")),console.log(),l.kaggle_url&&(console.log(chalk.gray(" Submit on Kaggle: ")+chalk.cyan(l.kaggle_url)),console.log())}(m,g);if(console.log(),console.log(chalk.white(` ${g.title}`)+chalk.gray(` · ${g.metric}`)),g.description){console.log();for(const o of v(g.description,74))console.log(chalk.white(` ${o}`))}console.log(),console.log(chalk.gray(" Downloading data…"));let b=null;const h=d(g);if(h){const[o,e,n]=await Promise.all([f(h.train),f(h.test),f(h.sample_submission)]);o&&e&&n&&(b={train:o,test:e,sample_submission:n})}if(!b){const o=await p(`/api/icoa/kaggle/${m}/data`);b=o?.data??o}if(!b?.train||!b?.test||!b?.sample_submission)return w();const $=k(m),j={...g,...b.meta??{}};try{e($,{recursive:!0}),t(s($,"train.csv"),b.train),t(s($,"test.csv"),b.test),t(s($,"sample_submission.csv"),b.sample_submission),t(s($,"meta.json"),`${JSON.stringify(j,null,2)}\n`)}catch{return void console.log(chalk.yellow(" Could not write the data files here — check directory permissions."))}const S=s($,"starter.ipynb");let K=!1;o(S)?K=!0:t(S,c(u($,j)));const N="number"==typeof j.n_train?`${j.n_train} rows`:"labelled",x="number"==typeof j.n_test?`${j.n_test} rows`:"predict these";console.log(chalk.green(" ✓ train: ")+chalk.white(s($,"train.csv"))+chalk.gray(` (${N})`)),console.log(chalk.green(" ✓ test: ")+chalk.white(s($,"test.csv"))+chalk.gray(` (${x})`)),console.log(chalk.green(" ✓ format: ")+chalk.white(s($,"sample_submission.csv"))),console.log(chalk.green(" ✓ notebook: ")+chalk.white(S)+chalk.gray(K?" (kept your existing one)":" (runnable baseline inside)")),(j.licence||j.attribution)&&console.log(chalk.gray(` licence: ${j.licence??"—"} · ${j.attribution??""}`)),console.log(),_(m,j,S),console.log()}finally{l.close()}}function _(o,e,n){console.log(chalk.bold.white(" Your move — 3 steps:")),console.log(chalk.gray(" 1. ")+chalk.bold.cyan(`icoa ipynb ${n}`)+chalk.gray(" → run all (baseline trains)")),console.log(chalk.gray(" 2. it writes ")+chalk.white("submission.csv")+chalk.gray(" → verify with ")+chalk.cyan(`kaggle check ${o}`)),"icoa-holdout"===e?.judge?console.log(chalk.gray(" 3. ")+chalk.bold.cyan(`kaggle submit ${o}`)+chalk.gray(" → scored + ranked live.")):console.log(chalk.gray(" 3. upload it on Kaggle yourself — then beat the baseline.")),console.log(),console.log(chalk.bold.white(" 🤝 Want AI help? Set up your cockpit:")),console.log(chalk.gray(" 1. split the screen: ")+chalk.bold.cyan("Ctrl-b %")+chalk.gray(" (lesson/work left · AI right)")),console.log(chalk.gray(" 2. in the right pane: ")+chalk.bold.cyan("icoa")+chalk.gray(" then type ")+chalk.bold.cyan("ai4ioai")+chalk.gray(" → live Gemma 3")),console.log(chalk.gray(" 3. ask it for feature ideas or bug fixes — then ")+chalk.bold.white("VERIFY")+chalk.gray(" with your val score (a tip that lowers it, drop it).")),console.log(),$(e)}export async function runKaggleGuide(e){console.log();const l=j(e,"guide");if(!l)return;const t=k(l);let r=null;try{r=JSON.parse(n(s(t,"meta.json"),"utf-8"))}catch{r=null}i(`kaggle: guide ${l}`);const a=s(t,"starter.ipynb"),c=o(s(t,"submission.csv"));console.log(chalk.bold.white(` ═══ 📖 ${r?.title??l} — walkthrough ═══`)),console.log(),console.log(chalk.gray(" Where you are: data ")+chalk.green("downloaded ✓")+chalk.gray(" · submission.csv: ")+(c?chalk.green("written ✓"):chalk.yellow("not yet — run the notebook"))),console.log(),_(l,r,a),console.log(),console.log(chalk.gray(" Inside the notebook, ")+chalk.cyan("guide")+chalk.gray(" shows the notebook-level guide.")),console.log()}function j(e,n){if(e)return e;const t=s(process.cwd(),"icoa-kaggle"),r=o(t)?l(t,{withFileTypes:!0}).filter(e=>e.isDirectory()&&o(s(t,e.name,"sample_submission.csv"))).map(o=>o.name):[];if(1===r.length)return r[0];if(0===r.length)return console.log(chalk.yellow(" No downloaded competition found here — run ")+chalk.cyan("kaggle")+chalk.yellow(" first.")),console.log(),null;console.log(chalk.white(" Several competitions downloaded — pick one:"));for(const o of r)console.log(chalk.gray(` kaggle ${n} `)+chalk.cyan(o));return console.log(),null}export async function runKaggleCheck(e){console.log();const l=j(e,"check");if(!l)return;const t=k(l),r=s(t,"submission.csv"),c=s(t,"sample_submission.csv");if(!o(c))return console.log(chalk.yellow(` No data for "${l}" here — run `)+chalk.cyan(`kaggle ${l}`)+chalk.yellow(" first.")),void console.log();if(!o(r))return console.log(chalk.yellow(" No submission.csv yet — open the notebook and run all cells:")),console.log(chalk.gray(" ")+chalk.cyan(`icoa ipynb ${s(t,"starter.ipynb")}`)),void console.log();const g=b(n(r,"utf-8"),n(c,"utf-8"));i(`kaggle: check ${l} → ${g.ok?"ok":`rejected: ${g.problems[0]??"format"}`}`);let u=null;try{u=JSON.parse(n(s(t,"meta.json"),"utf-8"))}catch{u=null}if(g.ok)console.log(chalk.bold.green(" ✓ submission.csv is Kaggle-ready")+chalk.gray(" (matches sample_submission.csv)")),console.log(chalk.gray(" ")+chalk.white(r)),console.log(),$(u),a(t);else{console.log(chalk.bold.red(" ✗ submission.csv would be rejected:"));for(const o of g.problems)console.log(chalk.yellow(` · ${o}`));console.log(),console.log(chalk.gray(" Fix the submission cell in the notebook and re-run it."))}console.log()}export async function runKaggleSubmit(e){console.log();const l=j(e,"submit");if(!l)return;const t=k(l),a=s(t,"submission.csv"),c=s(t,"sample_submission.csv");let u=null;try{u=JSON.parse(n(s(t,"meta.json"),"utf-8"))}catch{u=null}if(!u||!o(c))return console.log(chalk.yellow(` No data for "${l}" here — run `)+chalk.cyan(`kaggle ${l}`)+chalk.yellow(" first.")),void console.log();if("icoa-holdout"!==u.judge)return console.log(chalk.yellow(" This competition is judged on kaggle.com — nothing to submit here.")),console.log(),$(u),void console.log();if(!o(a))return console.log(chalk.yellow(" No submission.csv yet — open the notebook and run all cells:")),console.log(chalk.gray(" ")+chalk.cyan(`icoa ipynb ${s(t,"starter.ipynb")}`)),void console.log();const d=n(a,"utf-8"),y=b(d,n(c,"utf-8"));if(!y.ok){i(`kaggle: submit ${l} → local-reject: ${y.problems[0]??"format"}`),console.log(chalk.bold.red(" ✗ submission.csv would be rejected:"));for(const o of y.problems)console.log(chalk.yellow(` · ${o}`));return console.log(),console.log(chalk.gray(" Fix the submission cell in the notebook and re-run it.")),void console.log()}let p=g()?.token??"";if(!p){const o=r();try{p=(await o.ask(chalk.cyan(" Your learn token: "))??"").trim()}finally{o.close()}if(!p)return console.log(chalk.gray(" A learn token identifies you on the board — ask your coach for one.")),void console.log()}console.log(chalk.gray(" Scoring on the server…"));const f=await async function(o,e){try{const n=await fetch(h()+o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(6e4)});return await n.json()}catch{return null}}(`/api/icoa/kaggle/${l}/submit`,{token:p,submission_csv:d}),w=m(f);if(i(`kaggle: submit ${l} → ${w.ok?`${w.metric}=${w.score}${w.deduped?" (deduped)":""} rank=${w.rank??"-"}`:w.reason??"error"}`),console.log(),w.ok){const o=`${w.metric}: ${Number(w.score).toFixed(4).replace(/\.?0+$/,"")}`;w.deduped?(console.log(chalk.bold.white(` ✓ already scored — ${o}`)),console.log(chalk.gray(" These exact predictions were scored before. Improve the MODEL cell and re-run."))):(console.log(chalk.bold.green(` ✓ scored — ${o}`)),"number"==typeof w.rank&&"number"==typeof w.fieldSize&&console.log(chalk.bold.white(` 🏁 board rank #${w.rank}`)+chalk.gray(` of ${w.fieldSize}`))),w.boardEvent&&console.log(chalk.gray(" standings: ")+chalk.cyan(`board ${w.boardEvent} ${l}`))}else switch(w.reason){case"bad_token":console.log(chalk.bold.red(" ✗ that learn token was not accepted")+chalk.gray(" (invalid or expired)")),console.log(chalk.gray(" Check the token with your coach, then try again."));break;case"format":console.log(chalk.bold.red(" ✗ the server rejected submission.csv:"));for(const o of w.problems??[])console.log(chalk.yellow(` · ${o}`));break;case"event_closed":case"outside_window":console.log(chalk.bold.yellow(" ⏱ the submission window for this competition is closed."));break;case"cap_reached":console.log(chalk.bold.yellow(" ⏱ you have used all your submissions for this competition."));break;case"kaggle_judged":console.log(chalk.yellow(" This competition is judged on kaggle.com — nothing to submit here."));break;default:console.log(chalk.yellow(" The scoring server is not reachable right now — try again in a minute."))}console.log()}export function registerKaggleCommand(o){o.command("kaggle").argument("[action]",'competition id, or "check" / "submit"').argument("[comp]",'competition id (with "check" / "submit")').description("Kaggle-style ML practice — dataset + starter notebook, check & submit").action(async(o,e)=>{"check"===o?await runKaggleCheck(e):"submit"===o?await runKaggleSubmit(e):"guide"===o?await runKaggleGuide(e):await runKaggle(o)}),o.command("guide").argument("[comp]","competition id").description("Show the competition walkthrough (3 steps + AI cockpit)").action(async o=>{await runKaggleGuide(o)})}
1
+ import{existsSync as o,mkdirSync as e,readFileSync as n,readdirSync as l,writeFileSync as t}from"node:fs";import{join as s}from"node:path";import chalk from"chalk";import{makeReplAsker as r}from"../lib/repl-asker.js";import{logCommand as i}from"../lib/logger.js";import{openFile as a}from"../lib/open-file.js";import{serializeIpynb as c}from"../lib/notebook-doc.js";import{loadLearnState as g}from"../lib/learn-state.js";import{buildStarterCells as u,dataFileUrls as d,parseKaggleTaskList as y,parseSubmitResponse as m,validateSubmissionText as b}from"../lib/kaggle.js";function h(){return process.env.ICOA_KAGGLE_SERVER?process.env.ICOA_KAGGLE_SERVER.replace(/\/$/,""):"https://practice.icoa2026.au"}async function p(o){try{const e=await fetch(h()+o,{signal:AbortSignal.timeout(3e4)});return await e.json()}catch{return null}}async function f(o){try{const e=await fetch(o,{signal:AbortSignal.timeout(6e4)});return e.ok?await e.text():null}catch{return null}}function k(){console.log(),console.log(chalk.yellow(" The practice server is not reachable right now.")),console.log(chalk.gray(" Check your connection, or try again later.")),console.log()}function w(o){return s(process.cwd(),"icoa-kaggle",o)}function v(o,e){const n=[];let l="";for(const t of o.split(/\s+/))""!==l&&l.length+1+t.length>e?(n.push(l),l=t):l=""===l?t:`${l} ${t}`;return""!==l&&n.push(l),n}function $(o){if("icoa-holdout"===o?.judge)return console.log(chalk.bold.white(" Submit to ICOA (scored on the server):")),console.log(chalk.gray(" ")+chalk.bold.cyan(`kaggle submit ${o.comp_id}`)),void console.log(chalk.gray(" scores vs the secret test labels → live rank on ")+chalk.cyan("board"));console.log(chalk.bold.white(" Submit on Kaggle (manual, your own account):")),o?.kaggle_url?(console.log(chalk.gray(" 1. open ")+chalk.cyan(o.kaggle_url)),console.log(chalk.gray(" 2. Submit Predictions → upload ")+chalk.white("submission.csv")),console.log(chalk.gray(" 3. read your score, improve the MODEL cell, re-run, repeat."))):(console.log(chalk.gray(" (placeholder dataset — no live Kaggle page yet; when the real")),console.log(chalk.gray(" competition dataset lands, its submission link will appear here.)")))}export async function runKaggle(n){const l=r(),a=async o=>await l.ask(o)??"";try{console.log(),console.log(chalk.bold.cyan(" 📊 KAGGLE PRACTICE")+chalk.gray(" · real ML competitions, a runnable starter notebook")),console.log(chalk.gray(" Small tasks run on your CPU; ")+chalk.magenta("⚡ GPU tasks")+chalk.gray(" run on the ICOA GPU box.")),console.log();const l=await p("/api/icoa/kaggle/tasks");if(!l)return k();const r=y(l);if(0===r.length)return console.log(chalk.yellow(" No practice competitions are live yet — check back soon.")),void console.log();let g=n?r.find(o=>o.comp_id===n):void 0;if(n&&!g&&console.log(chalk.yellow(` No competition called "${n}".`)),!g){console.log(chalk.white(" Live practice competitions:")),r.forEach((o,e)=>{const n=o.needs_gpu?chalk.magenta(" ⚡GPU"):"";console.log(chalk.gray(` ${e+1}. `)+chalk.cyan(o.comp_id)+chalk.gray(` — ${o.title} · ${o.metric}`)+n)});for(let o=0;o<3&&!g;o++){const o=(await a(chalk.cyan(" Pick one (number or id): "))).trim();if(!o)return;const e=Number(o);g=!Number.isNaN(e)&&r[e-1]||r.find(e=>e.comp_id===o.toLowerCase())||void 0,g||console.log(chalk.gray(` Try a number 1-${r.length} or an id like "${r[0].comp_id}".`))}if(!g)return}const m=g.comp_id;if(i(`kaggle: download ${m}`),g.needs_gpu)return void await async function(n,l){const r=w(n);if(console.log(),console.log(chalk.white(` ${l.title}`)+chalk.gray(` · ${l.metric}`)+chalk.bold.magenta(" ⚡ GPU task")),l.description){console.log();for(const o of v(l.description,74))console.log(chalk.white(` ${o}`))}console.log(),console.log(chalk.gray(" Fetching the notebook…")+chalk.gray(" (the audio dataset stays on the GPU box — no download)"));const i=await p(`/api/icoa/kaggle/${n}/starter`),a=i?.data?.starter??i?.starter;if("string"!=typeof a||0===a.length)return k();const c=s(r,"starter.ipynb");let g=!1;try{e(r,{recursive:!0}),o(c)?g=!0:t(c,a),t(s(r,"meta.json"),`${JSON.stringify(l,null,2)}\n`)}catch{return void console.log(chalk.yellow(" Could not write the notebook here — check directory permissions."))}console.log(chalk.green(" ✓ notebook: ")+chalk.white(c)+chalk.gray(g?" (kept your existing one)":" (the real IOAI task inside)")),console.log(chalk.green(" ✓ dataset: ")+chalk.white("already on the GPU box")+chalk.gray(" — no download needed")),console.log(),console.log(chalk.bold.white(" This is a GPU task — 3 steps:")),console.log(chalk.gray(" 1. ")+chalk.bold.cyan(`icoa ipynb ${c}`)),console.log(chalk.gray(" 2. ")+chalk.bold.cyan("gpu key <your-key>")+chalk.gray(" then ")+chalk.bold.cyan("gpu on")+chalk.gray(" (connects you to the box where the audio data lives)")),console.log(chalk.gray(" 3. ")+chalk.bold.cyan("run all")+chalk.gray(" → the notebook trains on the GPU.")),console.log(),l.kaggle_url&&(console.log(chalk.gray(" Submit on Kaggle: ")+chalk.cyan(l.kaggle_url)),console.log())}(m,g);if(console.log(),console.log(chalk.white(` ${g.title}`)+chalk.gray(` · ${g.metric}`)),g.description){console.log();for(const o of v(g.description,74))console.log(chalk.white(` ${o}`))}console.log(),console.log(chalk.gray(" Downloading data…"));let b=null;const h=d(g);if(h){const[o,e,n]=await Promise.all([f(h.train),f(h.test),f(h.sample_submission)]);o&&e&&n&&(b={train:o,test:e,sample_submission:n})}if(!b){const o=await p(`/api/icoa/kaggle/${m}/data`);b=o?.data??o}if(!b?.train||!b?.test||!b?.sample_submission)return k();const $=w(m),j={...g,...b.meta??{}};try{e($,{recursive:!0}),t(s($,"train.csv"),b.train),t(s($,"test.csv"),b.test),t(s($,"sample_submission.csv"),b.sample_submission),t(s($,"meta.json"),`${JSON.stringify(j,null,2)}\n`)}catch{return void console.log(chalk.yellow(" Could not write the data files here — check directory permissions."))}const S=s($,"starter.ipynb");let K=!1;o(S)?K=!0:t(S,c(u($,j)));const x="number"==typeof j.n_train?`${j.n_train} rows`:"labelled",G="number"==typeof j.n_test?`${j.n_test} rows`:"predict these";console.log(chalk.green(" ✓ train: ")+chalk.white(s($,"train.csv"))+chalk.gray(` (${x})`)),console.log(chalk.green(" ✓ test: ")+chalk.white(s($,"test.csv"))+chalk.gray(` (${G})`)),console.log(chalk.green(" ✓ format: ")+chalk.white(s($,"sample_submission.csv"))),console.log(chalk.green(" ✓ notebook: ")+chalk.white(S)+chalk.gray(K?" (kept your existing one)":" (runnable baseline inside)")),(j.licence||j.attribution)&&console.log(chalk.gray(` licence: ${j.licence??"—"} · ${j.attribution??""}`)),console.log(),_(m,j,S),console.log()}finally{l.close()}}function _(o,e,n){console.log(chalk.bold.white(" Your move — 3 steps:")),console.log(chalk.gray(" 1. ")+chalk.bold.cyan(`icoa ipynb ${n}`)+chalk.gray(" → run all (baseline trains)")),console.log(chalk.gray(" 2. it writes ")+chalk.white("submission.csv")+chalk.gray(" → verify with ")+chalk.cyan(`kaggle check ${o}`)),"icoa-holdout"===e?.judge?console.log(chalk.gray(" 3. ")+chalk.bold.cyan(`kaggle submit ${o}`)+chalk.gray(" → scored + ranked live.")):console.log(chalk.gray(" 3. upload it on Kaggle yourself — then beat the baseline.")),console.log(),console.log(chalk.bold.white(" 🤝 Want AI help? Set up your cockpit:")),console.log(chalk.gray(" 1. split the screen: ")+chalk.bold.cyan("Ctrl-b %")+chalk.gray(" (lesson/work left · AI right)")),console.log(chalk.gray(" 2. in the right pane: ")+chalk.bold.cyan("icoa")+chalk.gray(" then type ")+chalk.bold.cyan("ai4ioai")+chalk.gray(" → live Gemma 3")),console.log(chalk.gray(" 3. ask it for feature ideas or bug fixes — then ")+chalk.bold.white("VERIFY")+chalk.gray(" with your val score (a tip that lowers it, drop it).")),console.log(),$(e)}export async function runKaggleGuide(e){console.log();const l=j(e,"guide");if(!l)return;const t=w(l);let r=null;try{r=JSON.parse(n(s(t,"meta.json"),"utf-8"))}catch{r=null}i(`kaggle: guide ${l}`);const a=s(t,"starter.ipynb"),c=o(s(t,"submission.csv"));console.log(chalk.bold.white(` ═══ 📖 ${r?.title??l} — walkthrough ═══`)),console.log(),console.log(chalk.gray(" Where you are: data ")+chalk.green("downloaded ✓")+chalk.gray(" · submission.csv: ")+(c?chalk.green("written ✓"):chalk.yellow("not yet — run the notebook"))),console.log(),_(l,r,a),console.log(),console.log(chalk.gray(" Inside the notebook, ")+chalk.cyan("guide")+chalk.gray(" shows the notebook-level guide.")),console.log()}function j(e,n){if(e)return e;const t=s(process.cwd(),"icoa-kaggle"),r=o(t)?l(t,{withFileTypes:!0}).filter(e=>e.isDirectory()&&o(s(t,e.name,"sample_submission.csv"))).map(o=>o.name):[];if(1===r.length)return r[0];if(0===r.length)return console.log(chalk.yellow(" No downloaded competition found here — run ")+chalk.cyan("kaggle")+chalk.yellow(" first.")),console.log(),null;console.log(chalk.white(" Several competitions downloaded — pick one:"));for(const o of r)console.log(chalk.gray(` kaggle ${n} `)+chalk.cyan(o));return console.log(),null}export async function runKaggleCheck(e){console.log();const l=j(e,"check");if(!l)return;const t=w(l),r=s(t,"submission.csv"),c=s(t,"sample_submission.csv");if(!o(c))return console.log(chalk.yellow(` No data for "${l}" here — run `)+chalk.cyan(`kaggle ${l}`)+chalk.yellow(" first.")),void console.log();if(!o(r))return console.log(chalk.yellow(" No submission.csv yet — open the notebook and run all cells:")),console.log(chalk.gray(" ")+chalk.cyan(`icoa ipynb ${s(t,"starter.ipynb")}`)),void console.log();const g=b(n(r,"utf-8"),n(c,"utf-8"));i(`kaggle: check ${l} → ${g.ok?"ok":`rejected: ${g.problems[0]??"format"}`}`);let u=null;try{u=JSON.parse(n(s(t,"meta.json"),"utf-8"))}catch{u=null}if(g.ok)console.log(chalk.bold.green(" ✓ submission.csv is Kaggle-ready")+chalk.gray(" (matches sample_submission.csv)")),console.log(chalk.gray(" ")+chalk.white(r)),console.log(),$(u),a(t);else{console.log(chalk.bold.red(" ✗ submission.csv would be rejected:"));for(const o of g.problems)console.log(chalk.yellow(` · ${o}`));console.log(),console.log(chalk.gray(" Fix the submission cell in the notebook and re-run it."))}console.log()}export async function runKaggleSubmit(e){console.log();const l=j(e,"submit");if(!l)return;const t=w(l),a=s(t,"submission.csv"),c=s(t,"sample_submission.csv");let u=null;try{u=JSON.parse(n(s(t,"meta.json"),"utf-8"))}catch{u=null}if(!u||!o(c))return console.log(chalk.yellow(` No data for "${l}" here — run `)+chalk.cyan(`kaggle ${l}`)+chalk.yellow(" first.")),void console.log();if("icoa-holdout"!==u.judge)return console.log(chalk.yellow(" This competition is judged on kaggle.com — nothing to submit here.")),console.log(),$(u),void console.log();if(!o(a))return console.log(chalk.yellow(" No submission.csv yet — open the notebook and run all cells:")),console.log(chalk.gray(" ")+chalk.cyan(`icoa ipynb ${s(t,"starter.ipynb")}`)),void console.log();const d=n(a,"utf-8"),y=b(d,n(c,"utf-8"));if(!y.ok){i(`kaggle: submit ${l} → local-reject: ${y.problems[0]??"format"}`),console.log(chalk.bold.red(" ✗ submission.csv would be rejected:"));for(const o of y.problems)console.log(chalk.yellow(` · ${o}`));return console.log(),console.log(chalk.gray(" Fix the submission cell in the notebook and re-run it.")),void console.log()}let p=g()?.token??"";if(!p){const o=r();try{p=(await o.ask(chalk.cyan(" Your learn token: "))??"").trim()}finally{o.close()}if(!p)return console.log(chalk.gray(" A learn token identifies you on the board — ask your coach for one.")),void console.log()}console.log(chalk.gray(" Scoring on the server…"));const f=await async function(o,e){try{const n=await fetch(h()+o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(6e4)});return await n.json()}catch{return null}}(`/api/icoa/kaggle/${l}/submit`,{token:p,submission_csv:d}),k=m(f);if(i(`kaggle: submit ${l} → ${k.ok?`${k.metric}=${k.score}${k.deduped?" (deduped)":""} rank=${k.rank??"-"}`:k.reason??"error"}`),console.log(),k.ok){const o=`${k.metric}: ${Number(k.score).toFixed(4).replace(/\.?0+$/,"")}`;k.deduped?(console.log(chalk.bold.white(` ✓ already scored — ${o}`)),console.log(chalk.gray(" These exact predictions were scored before. Improve the MODEL cell and re-run."))):(console.log(chalk.bold.green(` ✓ scored — ${o}`)),"number"==typeof k.rank&&"number"==typeof k.fieldSize&&console.log(chalk.bold.white(` 🏁 board rank #${k.rank}`)+chalk.gray(` of ${k.fieldSize}`))),k.boardEvent&&console.log(chalk.gray(" standings: ")+chalk.cyan(`board ${k.boardEvent} ${l}`))}else switch(k.reason){case"bad_token":console.log(chalk.bold.red(" ✗ that learn token was not accepted")+chalk.gray(" (invalid or expired)")),console.log(chalk.gray(" Check the token with your coach, then try again."));break;case"format":console.log(chalk.bold.red(" ✗ the server rejected submission.csv:"));for(const o of k.problems??[])console.log(chalk.yellow(` · ${o}`));break;case"event_closed":case"outside_window":console.log(chalk.bold.yellow(" ⏱ the submission window for this competition is closed."));break;case"cap_reached":console.log(chalk.bold.yellow(" ⏱ you have used all your submissions for this competition."));break;case"kaggle_judged":console.log(chalk.yellow(" This competition is judged on kaggle.com — nothing to submit here."));break;default:console.log(chalk.yellow(" The scoring server is not reachable right now — try again in a minute."))}console.log()}export function registerKaggleCommand(o){o.command("kaggle").argument("[action]",'competition id, or "check" / "submit"').argument("[comp]",'competition id (with "check" / "submit")').description("Kaggle-style ML practice — dataset + starter notebook, check & submit").action(async(o,e)=>{"check"===o?await runKaggleCheck(e):"submit"===o?await runKaggleSubmit(e):"guide"===o?await runKaggleGuide(e):await runKaggle(o)}),o.command("guide").argument("[comp]","competition id").description("Show the competition walkthrough (3 steps + AI cockpit)").action(async o=>{await runKaggleGuide(o)})}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.395",
3
+ "version": "2.19.396",
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": {